You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
14714 lines
613 KiB
14714 lines
613 KiB
From 4a5b83835224c80847892029213838545df065e2 Mon Sep 17 00:00:00 2001 |
|
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org> |
|
Date: Wed, 20 May 2015 17:44:50 +0200 |
|
Subject: [PATCH 1/5] Add top-icons extension |
|
|
|
--- |
|
extensions/top-icons/extension.js | 225 ++++++++++++++++++++++++++ |
|
extensions/top-icons/meson.build | 5 + |
|
extensions/top-icons/metadata.json.in | 10 ++ |
|
extensions/top-icons/stylesheet.css | 1 + |
|
meson.build | 1 + |
|
5 files changed, 242 insertions(+) |
|
create mode 100644 extensions/top-icons/extension.js |
|
create mode 100644 extensions/top-icons/meson.build |
|
create mode 100644 extensions/top-icons/metadata.json.in |
|
create mode 100644 extensions/top-icons/stylesheet.css |
|
|
|
diff --git a/extensions/top-icons/extension.js b/extensions/top-icons/extension.js |
|
new file mode 100644 |
|
index 0000000..7312a26 |
|
--- /dev/null |
|
+++ b/extensions/top-icons/extension.js |
|
@@ -0,0 +1,225 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const Shell = imports.gi.Shell; |
|
+const St = imports.gi.St; |
|
+const Main = imports.ui.main; |
|
+const GLib = imports.gi.GLib; |
|
+const Lang = imports.lang; |
|
+const Panel = imports.ui.panel; |
|
+const PanelMenu = imports.ui.panelMenu; |
|
+const Meta = imports.gi.Meta; |
|
+const Mainloop = imports.mainloop; |
|
+const NotificationDaemon = imports.ui.notificationDaemon; |
|
+const System = imports.system; |
|
+ |
|
+let trayAddedId = 0; |
|
+let trayRemovedId = 0; |
|
+let getSource = null; |
|
+let icons = []; |
|
+let notificationDaemon = null; |
|
+let sysTray = null; |
|
+ |
|
+const PANEL_ICON_SIZE = 24; |
|
+ |
|
+function init() { |
|
+ if (Main.legacyTray) { |
|
+ notificationDaemon = Main.legacyTray; |
|
+ NotificationDaemon.STANDARD_TRAY_ICON_IMPLEMENTATIONS = imports.ui.legacyTray.STANDARD_TRAY_ICON_IMPLEMENTATIONS; |
|
+ } |
|
+ else if (Main.notificationDaemon._fdoNotificationDaemon && |
|
+ Main.notificationDaemon._fdoNotificationDaemon._trayManager) { |
|
+ notificationDaemon = Main.notificationDaemon._fdoNotificationDaemon; |
|
+ getSource = Lang.bind(notificationDaemon, NotificationDaemon.FdoNotificationDaemon.prototype._getSource); |
|
+ } |
|
+ else if (Main.notificationDaemon._trayManager) { |
|
+ notificationDaemon = Main.notificationDaemon; |
|
+ getSource = Lang.bind(notificationDaemon, NotificationDaemon.NotificationDaemon.prototype._getSource); |
|
+ } |
|
+ else { |
|
+ NotificationDaemon.STANDARD_TRAY_ICON_IMPLEMENTATIONS = { |
|
+ 'bluetooth-applet': 1, 'gnome-sound-applet': 1, 'nm-applet': 1, |
|
+ 'gnome-power-manager': 1, 'keyboard': 1, 'a11y-keyboard': 1, |
|
+ 'kbd-scrolllock': 1, 'kbd-numlock': 1, 'kbd-capslock': 1, 'ibus-ui-gtk': 1 |
|
+ }; |
|
+ } |
|
+} |
|
+ |
|
+function enable() { |
|
+ if (notificationDaemon) |
|
+ GLib.idle_add(GLib.PRIORITY_LOW, moveToTop); |
|
+ else |
|
+ createTray(); |
|
+} |
|
+ |
|
+function createSource (title, pid, ndata, sender, trayIcon) { |
|
+ if (trayIcon) { |
|
+ onTrayIconAdded(this, trayIcon, title); |
|
+ return null; |
|
+ } |
|
+ |
|
+ return getSource(title, pid, ndata, sender, trayIcon); |
|
+}; |
|
+ |
|
+function onTrayIconAdded(o, icon, role) { |
|
+ let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : ''; |
|
+ if (NotificationDaemon.STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined) |
|
+ return; |
|
+ |
|
+ let buttonBox = new PanelMenu.ButtonBox(); |
|
+ let box = buttonBox.actor; |
|
+ let parent = box.get_parent(); |
|
+ |
|
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ let iconSize = PANEL_ICON_SIZE * scaleFactor; |
|
+ |
|
+ icon.set_size(iconSize, iconSize); |
|
+ box.add_actor(icon); |
|
+ |
|
+ icon.reactive = true; |
|
+ |
|
+ if (parent) |
|
+ parent.remove_actor(box); |
|
+ |
|
+ icons.push(icon); |
|
+ Main.panel._rightBox.insert_child_at_index(box, 0); |
|
+ |
|
+ let clickProxy = new St.Bin({ width: iconSize, height: iconSize }); |
|
+ clickProxy.reactive = true; |
|
+ Main.uiGroup.add_actor(clickProxy); |
|
+ |
|
+ icon._proxyAlloc = Main.panel._rightBox.connect('allocation-changed', function() { |
|
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function() { |
|
+ let [x, y] = icon.get_transformed_position(); |
|
+ clickProxy.set_position(x, y); |
|
+ }); |
|
+ }); |
|
+ |
|
+ icon.connect("destroy", function() { |
|
+ Main.panel._rightBox.disconnect(icon._proxyAlloc); |
|
+ clickProxy.destroy(); |
|
+ }); |
|
+ |
|
+ clickProxy.connect('button-release-event', function(actor, event) { |
|
+ icon.click(event); |
|
+ }); |
|
+ |
|
+ icon._clickProxy = clickProxy; |
|
+ |
|
+ /* Fixme: HACK */ |
|
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function() { |
|
+ let [x, y] = icon.get_transformed_position(); |
|
+ clickProxy.set_position(x, y); |
|
+ return false; |
|
+ }); |
|
+ let timerId = 0; |
|
+ let i = 0; |
|
+ timerId = Mainloop.timeout_add(500, function() { |
|
+ icon.set_size(icon.width == iconSize ? iconSize - 1 : iconSize, |
|
+ icon.width == iconSize ? iconSize - 1 : iconSize); |
|
+ i++; |
|
+ if (i == 2) |
|
+ Mainloop.source_remove(timerId); |
|
+ }); |
|
+} |
|
+ |
|
+function onTrayIconRemoved(o, icon) { |
|
+ let parent = icon.get_parent(); |
|
+ parent.destroy(); |
|
+ icon.destroy(); |
|
+ icons.splice(icons.indexOf(icon), 1); |
|
+} |
|
+ |
|
+function createTray() { |
|
+ sysTray = new Shell.TrayManager(); |
|
+ sysTray.connect('tray-icon-added', onTrayIconAdded); |
|
+ sysTray.connect('tray-icon-removed', onTrayIconRemoved); |
|
+ sysTray.manage_screen(global.screen, Main.panel.actor); |
|
+} |
|
+ |
|
+function destroyTray() { |
|
+ icons.forEach(icon => { icon.get_parent().destroy(); }); |
|
+ icons = []; |
|
+ |
|
+ sysTray = null; |
|
+ System.gc(); // force finalizing tray to unmanage screen |
|
+} |
|
+ |
|
+function moveToTop() { |
|
+ notificationDaemon._trayManager.disconnect(notificationDaemon._trayIconAddedId); |
|
+ notificationDaemon._trayManager.disconnect(notificationDaemon._trayIconRemovedId); |
|
+ trayAddedId = notificationDaemon._trayManager.connect('tray-icon-added', onTrayIconAdded); |
|
+ trayRemovedId = notificationDaemon._trayManager.connect('tray-icon-removed', onTrayIconRemoved); |
|
+ |
|
+ notificationDaemon._getSource = createSource; |
|
+ |
|
+ let toDestroy = []; |
|
+ if (notificationDaemon._sources) { |
|
+ for (let i = 0; i < notificationDaemon._sources.length; i++) { |
|
+ let source = notificationDaemon._sources[i]; |
|
+ if (!source.trayIcon) |
|
+ continue; |
|
+ let parent = source.trayIcon.get_parent(); |
|
+ parent.remove_actor(source.trayIcon); |
|
+ onTrayIconAdded(this, source.trayIcon, source.initialTitle); |
|
+ toDestroy.push(source); |
|
+ } |
|
+ } |
|
+ else { |
|
+ for (let i = 0; i < notificationDaemon._iconBox.get_n_children(); i++) { |
|
+ let button = notificationDaemon._iconBox.get_child_at_index(i); |
|
+ let icon = button.child; |
|
+ button.remove_actor(icon); |
|
+ onTrayIconAdded(this, icon, ''); |
|
+ toDestroy.push(button); |
|
+ } |
|
+ } |
|
+ |
|
+ for (let i = 0; i < toDestroy.length; i++) { |
|
+ toDestroy[i].destroy(); |
|
+ } |
|
+} |
|
+ |
|
+function moveToTray() { |
|
+ if (trayAddedId != 0) { |
|
+ notificationDaemon._trayManager.disconnect(trayAddedId); |
|
+ trayAddedId = 0; |
|
+ } |
|
+ |
|
+ if (trayRemovedId != 0) { |
|
+ notificationDaemon._trayManager.disconnect(trayRemovedId); |
|
+ trayRemovedId = 0; |
|
+ } |
|
+ |
|
+ notificationDaemon._trayIconAddedId = notificationDaemon._trayManager.connect('tray-icon-added', |
|
+ Lang.bind(notificationDaemon, notificationDaemon._onTrayIconAdded)); |
|
+ notificationDaemon._trayIconRemovedId = notificationDaemon._trayManager.connect('tray-icon-removed', |
|
+ Lang.bind(notificationDaemon, notificationDaemon._onTrayIconRemoved)); |
|
+ |
|
+ notificationDaemon._getSource = getSource; |
|
+ |
|
+ for (let i = 0; i < icons.length; i++) { |
|
+ let icon = icons[i]; |
|
+ let parent = icon.get_parent(); |
|
+ if (icon._clicked) { |
|
+ icon.disconnect(icon._clicked); |
|
+ } |
|
+ icon._clicked = undefined; |
|
+ if (icon._proxyAlloc) { |
|
+ Main.panel._rightBox.disconnect(icon._proxyAlloc); |
|
+ } |
|
+ icon._clickProxy.destroy(); |
|
+ parent.remove_actor(icon); |
|
+ parent.destroy(); |
|
+ notificationDaemon._onTrayIconAdded(notificationDaemon, icon); |
|
+ } |
|
+ |
|
+ icons = []; |
|
+} |
|
+ |
|
+function disable() { |
|
+ if (notificationDaemon) |
|
+ moveToTray(); |
|
+ else |
|
+ destroyTray(); |
|
+} |
|
diff --git a/extensions/top-icons/meson.build b/extensions/top-icons/meson.build |
|
new file mode 100644 |
|
index 0000000..48504f6 |
|
--- /dev/null |
|
+++ b/extensions/top-icons/meson.build |
|
@@ -0,0 +1,5 @@ |
|
+extension_data += configure_file( |
|
+ input: metadata_name + '.in', |
|
+ output: metadata_name, |
|
+ configuration: metadata_conf |
|
+) |
|
diff --git a/extensions/top-icons/metadata.json.in b/extensions/top-icons/metadata.json.in |
|
new file mode 100644 |
|
index 0000000..f1e2436 |
|
--- /dev/null |
|
+++ b/extensions/top-icons/metadata.json.in |
|
@@ -0,0 +1,10 @@ |
|
+{ |
|
+"extension-id": "@extension_id@", |
|
+"uuid": "@uuid@", |
|
+"settings-schema": "@gschemaname@", |
|
+"gettext-domain": "@gettext_domain@", |
|
+"name": "Top Icons", |
|
+"description": "Shows legacy tray icons on top", |
|
+"shell-version": [ "@shell_current@" ], |
|
+"url": "http://94.247.144.115/repo/topicons/" |
|
+} |
|
diff --git a/extensions/top-icons/stylesheet.css b/extensions/top-icons/stylesheet.css |
|
new file mode 100644 |
|
index 0000000..25134b6 |
|
--- /dev/null |
|
+++ b/extensions/top-icons/stylesheet.css |
|
@@ -0,0 +1 @@ |
|
+/* This extensions requires no special styling */ |
|
diff --git a/meson.build b/meson.build |
|
index 40a8275..c16bde1 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -54,6 +54,7 @@ all_extensions += [ |
|
'auto-move-windows', |
|
'example', |
|
'native-window-placement', |
|
+ 'top-icons', |
|
'user-theme' |
|
] |
|
|
|
-- |
|
2.17.1 |
|
|
|
|
|
From cbf1d6142b0b46fdd3947606286eb5abc0b1e02c Mon Sep 17 00:00:00 2001 |
|
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org> |
|
Date: Wed, 20 May 2015 18:05:41 +0200 |
|
Subject: [PATCH 2/5] Add dash-to-dock extension |
|
|
|
--- |
|
extensions/dash-to-dock/Settings.ui | 3332 +++++++++++++++++ |
|
extensions/dash-to-dock/appIconIndicators.js | 1124 ++++++ |
|
extensions/dash-to-dock/appIcons.js | 1171 ++++++ |
|
extensions/dash-to-dock/convenience.js | 74 + |
|
extensions/dash-to-dock/dash.js | 1175 ++++++ |
|
extensions/dash-to-dock/docking.js | 1925 ++++++++++ |
|
extensions/dash-to-dock/extension.js | 23 + |
|
extensions/dash-to-dock/intellihide.js | 323 ++ |
|
extensions/dash-to-dock/launcherAPI.js | 244 ++ |
|
extensions/dash-to-dock/media/glossy.svg | 139 + |
|
extensions/dash-to-dock/media/logo.svg | 528 +++ |
|
extensions/dash-to-dock/meson.build | 23 + |
|
extensions/dash-to-dock/metadata.json.in | 12 + |
|
....shell.extensions.dash-to-dock.gschema.xml | 540 +++ |
|
extensions/dash-to-dock/prefs.js | 868 +++++ |
|
extensions/dash-to-dock/stylesheet.css | 109 + |
|
extensions/dash-to-dock/theming.js | 672 ++++ |
|
extensions/dash-to-dock/utils.js | 255 ++ |
|
extensions/dash-to-dock/windowPreview.js | 630 ++++ |
|
meson.build | 1 + |
|
20 files changed, 13168 insertions(+) |
|
create mode 100644 extensions/dash-to-dock/Settings.ui |
|
create mode 100644 extensions/dash-to-dock/appIconIndicators.js |
|
create mode 100644 extensions/dash-to-dock/appIcons.js |
|
create mode 100644 extensions/dash-to-dock/convenience.js |
|
create mode 100644 extensions/dash-to-dock/dash.js |
|
create mode 100644 extensions/dash-to-dock/docking.js |
|
create mode 100644 extensions/dash-to-dock/extension.js |
|
create mode 100644 extensions/dash-to-dock/intellihide.js |
|
create mode 100644 extensions/dash-to-dock/launcherAPI.js |
|
create mode 100644 extensions/dash-to-dock/media/glossy.svg |
|
create mode 100644 extensions/dash-to-dock/media/logo.svg |
|
create mode 100644 extensions/dash-to-dock/meson.build |
|
create mode 100644 extensions/dash-to-dock/metadata.json.in |
|
create mode 100644 extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml |
|
create mode 100644 extensions/dash-to-dock/prefs.js |
|
create mode 100644 extensions/dash-to-dock/stylesheet.css |
|
create mode 100644 extensions/dash-to-dock/theming.js |
|
create mode 100644 extensions/dash-to-dock/utils.js |
|
create mode 100644 extensions/dash-to-dock/windowPreview.js |
|
|
|
diff --git a/extensions/dash-to-dock/Settings.ui b/extensions/dash-to-dock/Settings.ui |
|
new file mode 100644 |
|
index 0000000..2b164a8 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/Settings.ui |
|
@@ -0,0 +1,3332 @@ |
|
+<?xml version="1.0" encoding="UTF-8"?> |
|
+<!-- Generated with glade 3.20.0 --> |
|
+<interface> |
|
+ <requires lib="gtk+" version="3.18"/> |
|
+ <object class="GtkAdjustment" id="animation_time_adjustment"> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.050000000000000003</property> |
|
+ <property name="page_increment">0.25</property> |
|
+ </object> |
|
+ <object class="GtkBox" id="box_middle_click_options"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="frame_middle_click_options"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox_middle_click_options"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow11"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="buitin_theme7"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description4"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">When set to minimize, double clicking minimizes all the windows of the application.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="max_width_chars">40</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label4"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Shift+Click action</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="shift_click_action_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <items> |
|
+ <item translatable="yes">Raise window</item> |
|
+ <item translatable="yes">Minimize window</item> |
|
+ <item translatable="yes">Launch new instance</item> |
|
+ <item translatable="yes">Cycle through windows</item> |
|
+ <item translatable="yes">Minimize or overview</item> |
|
+ <item translatable="yes">Show window previews</item> |
|
+ <item translatable="yes">Minimize or show previews</item> |
|
+ <item translatable="yes">Quit</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_middle_click"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid_middle_click"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="description_middle_click"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Behavior for Middle-Click.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="max_width_chars">40</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label_middle_click"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Middle-Click action</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="middle_click_action_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <items> |
|
+ <item translatable="yes">Raise window</item> |
|
+ <item translatable="yes">Minimize window</item> |
|
+ <item translatable="yes">Launch new instance</item> |
|
+ <item translatable="yes">Cycle through windows</item> |
|
+ <item translatable="yes">Minimize or overview</item> |
|
+ <item translatable="yes">Show window previews</item> |
|
+ <item translatable="yes">Minimize or show previews</item> |
|
+ <item translatable="yes">Quit</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_shift_middle_click"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid_shift_middle_click"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="description_shift_middle_click"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Behavior for Shift+Middle-Click.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="max_width_chars">40</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label_shift_middle_click"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Shift+Middle-Click action</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="shift_middle_click_action_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <items> |
|
+ <item translatable="yes">Raise window</item> |
|
+ <item translatable="yes">Minimize window</item> |
|
+ <item translatable="yes">Launch new instance</item> |
|
+ <item translatable="yes">Cycle through windows</item> |
|
+ <item translatable="yes">Minimize or overview</item> |
|
+ <item translatable="yes">Show window previews</item> |
|
+ <item translatable="yes">Minimize or show previews</item> |
|
+ <item translatable="yes">Quit</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="custom_opacity_adjustement"> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.01</property> |
|
+ <property name="page_increment">0.10000000000000001</property> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="dock_size_adjustment"> |
|
+ <property name="lower">0.33000000000000002</property> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.01</property> |
|
+ <property name="page_increment">0.10000000000000001</property> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="dot_border_width_adjustment"> |
|
+ <property name="upper">10</property> |
|
+ <property name="step_increment">1</property> |
|
+ <property name="page_increment">5</property> |
|
+ </object> |
|
+ <object class="GtkBox" id="running_dots_advance_settings_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="frame1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox7"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow10"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkBox" id="dot_style_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">12</property> |
|
+ <child> |
|
+ <object class="GtkBox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="unity_backlit_items_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="label" translatable="yes">Enable Unity7 like glossy backlit items</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="unity_backlit_items_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dominant_color_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="halign">start</property> |
|
+ <property name="label" translatable="yes">Use dominant color</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="dominant_color_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="dot_style_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label4"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Customize indicator style</property> |
|
+ <property name="justify">fill</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="dot_style_settings_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_bottom">1</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">12</property> |
|
+ <child> |
|
+ <object class="GtkBox" id="dot_color_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dot_color_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Color</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkColorButton" id="dot_color_colorbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="border_color_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dot_border_color_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Border color</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkColorButton" id="dot_border_color_colorbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="box_boder_width_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dot_border_width_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Border width</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSpinButton" id="dot_border_width_spin_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="adjustment">dot_border_width_adjustment</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="hide_timeout_adjustment"> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.050000000000000003</property> |
|
+ <property name="page_increment">0.25</property> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="icon_size_adjustment"> |
|
+ <property name="lower">16</property> |
|
+ <property name="upper">128</property> |
|
+ <property name="step_increment">1</property> |
|
+ <property name="page_increment">10</property> |
|
+ </object> |
|
+ <object class="GtkNotebook" id="settings_notebook"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="margin_left">6</property> |
|
+ <property name="margin_right">6</property> |
|
+ <property name="margin_top">6</property> |
|
+ <property name="margin_bottom">6</property> |
|
+ <child> |
|
+ <object class="GtkBox" id="position_and_size"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">24</property> |
|
+ <property name="margin_right">24</property> |
|
+ <property name="margin_top">24</property> |
|
+ <property name="margin_bottom">24</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">24</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="dock_display"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="dock_display_listbox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="dock_monitor_listboxrow"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="dock_monitor_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dock_monitor_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show the dock on</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="dock_monitor_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <signal name="changed" handler="dock_display_combo_changed_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="multi_monitor_button"> |
|
+ <property name="label" translatable="yes">Show on all monitors.</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">2</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <placeholder/> |
|
+ </child> |
|
+ <child> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="dock_position_listboxrow"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkBox" id="dock_position_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dock_position_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Position on screen</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="dock_position_butttons_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="position_left_button"> |
|
+ <property name="label" translatable="yes">Left</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <signal name="toggled" handler="position_left_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="position_bottom_button"> |
|
+ <property name="label" translatable="yes">Bottom</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <property name="group">position_left_button</property> |
|
+ <signal name="toggled" handler="position_bottom_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="position_top_button"> |
|
+ <property name="label" translatable="yes">Top</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="image_position">bottom</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <property name="group">position_left_button</property> |
|
+ <signal name="toggled" handler="position_top_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="position_right_button"> |
|
+ <property name="label" translatable="yes">Right</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <property name="group">position_left_button</property> |
|
+ <signal name="toggled" handler="position_right_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkFrame" id="intelligent_autohide_frame"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow14"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="intelligent_autohide_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description7"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Hide the dock when it obstructs a window of the current application. More refined settings are available.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label8"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Intelligent autohide</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="box3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkButton" id="intelligent_autohide_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xalign">0.46000000834465027</property> |
|
+ <child> |
|
+ <object class="GtkImage" id="image"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="icon_name">emblem-system-symbolic</property> |
|
+ </object> |
|
+ </child> |
|
+ <style> |
|
+ <class name="circular"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="intelligent_autohide_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkFrame" id="size_frame"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="size_listbox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="dock_size_listboxrow"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="dock_size_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="dock_size_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Dock size limit</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkScale" id="dock_size_scale"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="valign">baseline</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="adjustment">dock_size_adjustment</property> |
|
+ <property name="round_digits">0</property> |
|
+ <property name="digits">2</property> |
|
+ <property name="value_pos">right</property> |
|
+ <signal name="format-value" handler="dock_size_scale_format_value_cb" swapped="no"/> |
|
+ <signal name="value-changed" handler="dock_size_scale_value_changed_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="dock_size_extend_checkbutton"> |
|
+ <property name="label" translatable="yes">Panel mode: extend to the screen edge</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="icon_size_listboxrow"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="icon_size_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label6"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Icon size limit</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkScale" id="icon_size_scale"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="valign">baseline</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="adjustment">icon_size_adjustment</property> |
|
+ <property name="round_digits">1</property> |
|
+ <property name="digits">0</property> |
|
+ <property name="value_pos">right</property> |
|
+ <signal name="format-value" handler="icon_size_scale_format_value_cb" swapped="no"/> |
|
+ <signal name="value-changed" handler="icon_size_scale_value_changed_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="icon_size_fixed_checkbutton"> |
|
+ <property name="label" translatable="yes">Fixed icon size: scroll to reveal other icons</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="tab"> |
|
+ <object class="GtkLabel" id="general_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Position and size</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="tab_fill">False</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="apps"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">24</property> |
|
+ <property name="margin_right">24</property> |
|
+ <property name="margin_top">24</property> |
|
+ <property name="margin_bottom">24</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">24</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="customize_theme1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox9"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow6"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="shrink_dash1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="show_favorite_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shrink_dash_label1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show favorite applications</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow16"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="shrink_dash2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="show_running_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shrink_dash_label2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show running applications</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="application_button_isolation_button"> |
|
+ <property name="label" translatable="yes">Isolate workspaces.</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">2</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="application_button_monitor_isolation_button"> |
|
+ <property name="label" translatable="yes">Isolate monitors.</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">3</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="windows_preview_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">3</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="windows_previews_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Show open windows previews.</property> |
|
+ <property name="use_markup">True</property> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow17"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="shrink_dash3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="show_applications_button_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shrink_dash_description1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">If disabled, these settings are accessible from gnome-tweak-tool or the extension website.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shrink_dash_label3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show <i>Applications</i> icon</property> |
|
+ <property name="use_markup">True</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="application_button_first_button"> |
|
+ <property name="label" translatable="yes">Move the applications button at the beginning of the dock.</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">2</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="application_button_animation_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">3</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="yalign">0.43000000715255737</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Animate <i>Show Applications</i>.</property> |
|
+ <property name="use_markup">True</property> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">3</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child type="tab"> |
|
+ <object class="GtkLabel" id="launchers_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Launchers</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">1</property> |
|
+ <property name="tab_fill">False</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="behaviour"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">24</property> |
|
+ <property name="margin_right">24</property> |
|
+ <property name="margin_top">24</property> |
|
+ <property name="margin_bottom">24</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">24</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="hot_keys_frame"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="hot_keys_listbox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="hot_keys_listboxrow"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="hot_keys_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="hot_keys_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Enable Super+(0-9) as shortcuts to activate apps. It can also be used together with Shift and Ctrl.</property> |
|
+ <property name="use_markup">True</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="hot_keys_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Use keyboard shortcuts to activate apps</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="overlay_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkButton" id="overlay_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xalign">0.46000000834465027</property> |
|
+ <child> |
|
+ <object class="GtkImage" id="image_overlay"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="icon_name">emblem-system-symbolic</property> |
|
+ </object> |
|
+ </child> |
|
+ <style> |
|
+ <class name="circular"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="hot_keys_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkFrame" id="built_in_theme_frame3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox6"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow9"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="buitin_theme5"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description5"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Behaviour when clicking on the icon of a running application.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label5"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Click action</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="click_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkButton" id="middle_click_options_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="valign">center</property> |
|
+ <child> |
|
+ <object class="GtkImage" id="middle_click_image"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="icon_name">emblem-system-symbolic</property> |
|
+ </object> |
|
+ </child> |
|
+ <style> |
|
+ <class name="circular"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="click_action_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <items> |
|
+ <item translatable="yes">Raise window</item> |
|
+ <item translatable="yes">Minimize</item> |
|
+ <item translatable="yes">Launch new instance</item> |
|
+ <item translatable="yes">Cycle through windows</item> |
|
+ <item translatable="yes">Minimize or overview</item> |
|
+ <item translatable="yes">Show window previews</item> |
|
+ <item translatable="yes">Minimize or show previews</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkFrame" id="built_in_theme_frame_scroll"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox5"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_scroll"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="buitin_theme_scroll"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description_scroll"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Behaviour when scrolling on the icon of an application.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label_scroll"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Scroll action</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="click_box_scroll"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="scroll_action_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <items> |
|
+ <item translatable="yes">Do nothing</item> |
|
+ <item translatable="yes">Cycle through windows</item> |
|
+ <item translatable="yes">Switch workspace</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child type="tab"> |
|
+ <object class="GtkLabel" id="behaviour_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Behavior</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">2</property> |
|
+ <property name="tab_fill">False</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="appearance"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">24</property> |
|
+ <property name="margin_right">24</property> |
|
+ <property name="margin_top">24</property> |
|
+ <property name="margin_bottom">24</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">24</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="built_in_theme_frame"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="buitin_theme"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Few customizations meant to integrate the dock with the default GNOME theme. Alternatively, specific options can be enabled below.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Use built-in theme</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="builtin_theme_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkFrame" id="customize_theme"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="shrink_dash"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="shrink_dash_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shrink_dash_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Save space reducing padding and border radius.</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shrink_dash_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Shrink the dash</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="running_dots"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="running_dots_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Customize windows counter indicators</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="box4"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkButton" id="running_indicators_advance_settings_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xalign">0.46000000834465027</property> |
|
+ <child> |
|
+ <object class="GtkImage" id="image1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="icon_name">emblem-system-symbolic</property> |
|
+ </object> |
|
+ </child> |
|
+ <style> |
|
+ <class name="circular"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="running_indicators_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <items> |
|
+ <item translatable="yes">Default</item> |
|
+ <item translatable="yes">Dots</item> |
|
+ <item translatable="yes">Squares</item> |
|
+ <item translatable="yes">Dashes</item> |
|
+ <item translatable="yes">Segmented</item> |
|
+ <item translatable="yes">Solid</item> |
|
+ <item translatable="yes">Ciliora</item> |
|
+ <item translatable="yes">Metro</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="custom_background_color_listboxrow"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="custom_background_color_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="custom_background_color_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Set the background color for the dash.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="custom_background_color_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Customize the dash color</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="custom_background_color_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkColorButton" id="custom_background_color"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xalign">0.46000000834465027</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="custom_background_color_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow4"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkBox" id="box2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="customize_opacity"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="customize_opacity_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Tune the dash background opacity.</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="customize_opacity_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Customize opacity</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="customize_opacity_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">6</property> |
|
+ <child> |
|
+ <object class="GtkButton" id="dynamic_opacity_button"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="valign">center</property> |
|
+ <child> |
|
+ <object class="GtkImage" id="dynamic_opacity_image"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="icon_name">emblem-system-symbolic</property> |
|
+ </object> |
|
+ </child> |
|
+ <style> |
|
+ <class name="circular"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkComboBoxText" id="customize_opacity_combo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <items> |
|
+ <item translatable="yes">Default</item> |
|
+ <item translatable="yes">Fixed</item> |
|
+ <item translatable="yes">Adaptive</item> |
|
+ <item translatable="yes">Dynamic</item> |
|
+ </items> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="custom_opacity"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="custom_opacity_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Opacity</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkScale" id="custom_opacity_scale"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="adjustment">custom_opacity_adjustement</property> |
|
+ <property name="lower_stepper_sensitivity">on</property> |
|
+ <property name="restrict_to_fill_level">False</property> |
|
+ <property name="fill_level">0</property> |
|
+ <property name="round_digits">0</property> |
|
+ <property name="digits">2</property> |
|
+ <property name="value_pos">right</property> |
|
+ <signal name="format-value" handler="custom_opacity_scale_format_value_cb" swapped="no"/> |
|
+ <signal name="value-changed" handler="custom_opacity_scale_value_changed_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_rnd_border"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkBox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="force_straight_corner_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="xpad">12</property> |
|
+ <property name="label" translatable="yes">Force straight corner |
|
+</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="force_straight_corner_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="margin_left">3</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="padding">12</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child type="tab"> |
|
+ <object class="GtkLabel" id="appearance_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Appearance</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">3</property> |
|
+ <property name="tab_fill">False</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="about"> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_top">24</property> |
|
+ <property name="margin_bottom">24</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="vexpand">True</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">5</property> |
|
+ <child> |
|
+ <object class="GtkImage" id="logo"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="pixbuf">./media/logo.svg</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="padding">10</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="extension_name"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label"><b>Dash to Dock</b></property> |
|
+ <property name="use_markup">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="box1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="halign">center</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="extension_version_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="label" translatable="yes">version: </property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="extension_version"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="halign">start</property> |
|
+ <property name="label">...</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="extension_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Moves the dash out of the overview transforming it in a dock</property> |
|
+ <property name="justify">center</property> |
|
+ <property name="wrap">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="box10"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="halign">center</property> |
|
+ <property name="spacing">5</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label15"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Created by</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label16"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="label">Michele (<a href="mailto:micxgx@gmail.com">micxgx@gmail.com</a>)</property> |
|
+ <property name="use_markup">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">4</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLinkButton" id="homepage_link"> |
|
+ <property name="label" translatable="yes">Webpage</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">True</property> |
|
+ <property name="events"/> |
|
+ <property name="halign">center</property> |
|
+ <property name="relief">none</property> |
|
+ <property name="uri">https://micheleg.github.io/dash-to-dock/</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">5</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label1"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="valign">end</property> |
|
+ <property name="label" translatable="yes"><span size="small">This program comes with ABSOLUTELY NO WARRANTY. |
|
+See the <a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU General Public License, version 2 or later</a> for details.</span></property> |
|
+ <property name="use_markup">True</property> |
|
+ <property name="justify">center</property> |
|
+ <property name="wrap">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">6</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">4</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child type="tab"> |
|
+ <object class="GtkLabel" id="about_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">About</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="position">4</property> |
|
+ <property name="tab_fill">False</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="max_opacity_adjustement"> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.01</property> |
|
+ <property name="page_increment">0.10000000000000001</property> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="min_opacity_adjustement"> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.01</property> |
|
+ <property name="page_increment">0.10000000000000001</property> |
|
+ </object> |
|
+ <object class="GtkBox" id="advanced_transparency_dialog"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="advanced_transparency_frame"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="advanced_transparency_listbox"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="advanced_transparency_listboxrow"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkBox" id="advanced_transparency_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <property name="spacing">12</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="advanced_transparency_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="customize_alphas_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="customize_alphas_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Customize minimum and maximum opacity values</property> |
|
+ <property name="justify">fill</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="min_alpha_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="min_alpha_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Minimum opacity</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkScale" id="min_alpha_scale"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="adjustment">min_opacity_adjustement</property> |
|
+ <property name="lower_stepper_sensitivity">on</property> |
|
+ <property name="restrict_to_fill_level">False</property> |
|
+ <property name="fill_level">0</property> |
|
+ <property name="round_digits">0</property> |
|
+ <property name="digits">2</property> |
|
+ <property name="value_pos">right</property> |
|
+ <signal name="format-value" handler="min_opacity_scale_format_value_cb" swapped="no"/> |
|
+ <signal name="value-changed" handler="min_opacity_scale_value_changed_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="max_alpha_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="max_alpha_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Maximum opacity</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkScale" id="max_alpha_scale"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="adjustment">max_opacity_adjustement</property> |
|
+ <property name="lower_stepper_sensitivity">on</property> |
|
+ <property name="restrict_to_fill_level">False</property> |
|
+ <property name="fill_level">0</property> |
|
+ <property name="round_digits">0</property> |
|
+ <property name="digits">2</property> |
|
+ <property name="value_pos">right</property> |
|
+ <signal name="format-value" handler="max_opacity_scale_format_value_cb" swapped="no"/> |
|
+ <signal name="value-changed" handler="max_opacity_scale_value_changed_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">True</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="pressure_threshold_adjustment"> |
|
+ <property name="upper">1000</property> |
|
+ <property name="step_increment">50</property> |
|
+ <property name="page_increment">250</property> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="shortcut_time_adjustment"> |
|
+ <property name="upper">10</property> |
|
+ <property name="step_increment">0.25</property> |
|
+ <property name="page_increment">1</property> |
|
+ </object> |
|
+ <object class="GtkBox" id="box_overlay_shortcut"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="frame_overlay_show_dock"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox_overlay_shortcut"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_overlay_shortcut"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid_overlay"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="overlay_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="overlay_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Number overlay</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="overlay_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Temporarily show the application numbers over the icons, corresponding to the shortcut.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="max_width_chars">40</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_show_dock"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid_show_dock"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="show_dock_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="show_dock_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show the dock if it is hidden</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="show_dock_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">If using autohide, the dock will appear for a short time when triggering the shortcut.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="max_width_chars">40</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_extra_shortcut"> |
|
+ <property name="width_request">100</property> |
|
+ <property name="height_request">80</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid_shortcut"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkEntry" id="shortcut_entry"> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="valign">center</property> |
|
+ <property name="width_chars">12</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shortcut_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Shortcut for the options above</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shortcut_description"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label" translatable="yes">Syntax: <Shift>, <Ctrl>, <Alt>, <Super></property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="max_width_chars">40</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow_timeout"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid_timeout"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="row_spacing">6</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSpinButton" id="timeout_spinbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="adjustment">shortcut_time_adjustment</property> |
|
+ <property name="digits">3</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="shortcut_timeout_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Hide timeout (s)</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <object class="GtkAdjustment" id="show_timeout_adjustment"> |
|
+ <property name="upper">1</property> |
|
+ <property name="step_increment">0.050000000000000003</property> |
|
+ <property name="page_increment">0.25</property> |
|
+ </object> |
|
+ <object class="GtkBox" id="intelligent_autohide_advanced_settings_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkFrame" id="intelligent_autohide_advanced_settings_frame"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="label_xalign">0</property> |
|
+ <property name="shadow_type">in</property> |
|
+ <child> |
|
+ <object class="GtkListBox" id="listbox4"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="selection_mode">none</property> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow8"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="buitin_theme2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show the dock by mouse hover on the screen edge.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Autohide</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="autohide_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="require_pressure_checkbutton"> |
|
+ <property name="label" translatable="yes">Push to show: require pressure to show the dock</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">3</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkCheckButton" id="autohide_enable_in_fullscreen_checkbutton"> |
|
+ <property name="label" translatable="yes">Enable in fullscreen mode</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">2</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow15"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="intellihide_grid"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_description3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show the dock when it doesn't obstruct application windows.</property> |
|
+ <property name="wrap">True</property> |
|
+ <property name="xalign">0</property> |
|
+ <style> |
|
+ <class name="dim-label"/> |
|
+ </style> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="builtin_theme_label3"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Dodge windows</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSwitch" id="intellihide_switch"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="valign">center</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ <property name="height">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkBox" id="intellihide_mode_box"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="orientation">vertical</property> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="all_windows_radio_button"> |
|
+ <property name="label" translatable="yes">All windows</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="active">True</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <signal name="toggled" handler="all_windows_radio_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="focus_application_windows_radio_button"> |
|
+ <property name="label" translatable="yes">Only focused application's windows</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="active">True</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <property name="group">all_windows_radio_button</property> |
|
+ <signal name="toggled" handler="focus_application_windows_radio_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkRadioButton" id="maximized_windows_radio_button"> |
|
+ <property name="label" translatable="yes">Only maximized windows</property> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="receives_default">False</property> |
|
+ <property name="xalign">0</property> |
|
+ <property name="active">True</property> |
|
+ <property name="draw_indicator">True</property> |
|
+ <property name="group">all_windows_radio_button</property> |
|
+ <signal name="toggled" handler="maximized_windows_radio_button_toggled_cb" swapped="no"/> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">2</property> |
|
+ <property name="width">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkListBoxRow" id="listboxrow5"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <child> |
|
+ <object class="GtkGrid" id="grid2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="margin_left">12</property> |
|
+ <property name="margin_right">12</property> |
|
+ <property name="margin_top">12</property> |
|
+ <property name="margin_bottom">12</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="row_spacing">6</property> |
|
+ <property name="column_spacing">32</property> |
|
+ <child> |
|
+ <object class="GtkSpinButton" id="animation_duration_spinbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="adjustment">animation_time_adjustment</property> |
|
+ <property name="digits">3</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label2"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Animation duration (s)</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSpinButton" id="hide_timeout_spinbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="adjustment">hide_timeout_adjustment</property> |
|
+ <property name="digits">3</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSpinButton" id="show_timeout_spinbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="halign">end</property> |
|
+ <property name="adjustment">show_timeout_adjustment</property> |
|
+ <property name="digits">3</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkSpinButton" id="pressure_threshold_spinbutton"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">True</property> |
|
+ <property name="text">0.000</property> |
|
+ <property name="adjustment">pressure_threshold_adjustment</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">1</property> |
|
+ <property name="top_attach">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="label9"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Hide timeout (s)</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">1</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="show_timeout_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Show timeout (s)</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">2</property> |
|
+ </packing> |
|
+ </child> |
|
+ <child> |
|
+ <object class="GtkLabel" id="pressure_threshold_label"> |
|
+ <property name="visible">True</property> |
|
+ <property name="can_focus">False</property> |
|
+ <property name="hexpand">True</property> |
|
+ <property name="label" translatable="yes">Pressure threshold</property> |
|
+ <property name="xalign">0</property> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="left_attach">0</property> |
|
+ <property name="top_attach">3</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ </object> |
|
+ </child> |
|
+ <child type="label_item"> |
|
+ <placeholder/> |
|
+ </child> |
|
+ </object> |
|
+ <packing> |
|
+ <property name="expand">False</property> |
|
+ <property name="fill">True</property> |
|
+ <property name="position">0</property> |
|
+ </packing> |
|
+ </child> |
|
+ </object> |
|
+</interface> |
|
diff --git a/extensions/dash-to-dock/appIconIndicators.js b/extensions/dash-to-dock/appIconIndicators.js |
|
new file mode 100644 |
|
index 0000000..6eb0706 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/appIconIndicators.js |
|
@@ -0,0 +1,1124 @@ |
|
+const Cogl = imports.gi.Cogl; |
|
+const Cairo = imports.cairo; |
|
+const Clutter = imports.gi.Clutter; |
|
+const GdkPixbuf = imports.gi.GdkPixbuf |
|
+const Gio = imports.gi.Gio; |
|
+const Gtk = imports.gi.Gtk; |
|
+const Lang = imports.lang; |
|
+const Pango = imports.gi.Pango; |
|
+const Shell = imports.gi.Shell; |
|
+const St = imports.gi.St; |
|
+ |
|
+const Util = imports.misc.util; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Utils = Me.imports.utils; |
|
+ |
|
+let tracker = Shell.WindowTracker.get_default(); |
|
+ |
|
+const RunningIndicatorStyle = { |
|
+ DEFAULT: 0, |
|
+ DOTS: 1, |
|
+ SQUARES: 2, |
|
+ DASHES: 3, |
|
+ SEGMENTED: 4, |
|
+ SOLID: 5, |
|
+ CILIORA: 6, |
|
+ METRO: 7 |
|
+}; |
|
+ |
|
+const MAX_WINDOWS_CLASSES = 4; |
|
+ |
|
+ |
|
+/* |
|
+ * This is the main indicator class to be used. The desired bahviour is |
|
+ * obtained by composing the desired classes below based on the settings. |
|
+ * |
|
+ */ |
|
+var AppIconIndicator = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.AppIconIndicator', |
|
+ |
|
+ _init: function(source, settings) { |
|
+ this._indicators = []; |
|
+ |
|
+ // Unity indicators always enabled for now |
|
+ let unityIndicator = new UnityIndicator(source, settings); |
|
+ this._indicators.push(unityIndicator); |
|
+ |
|
+ // Choose the style for the running indicators |
|
+ let runningIndicator = null; |
|
+ let runningIndicatorStyle; |
|
+ |
|
+ if (settings.get_boolean('apply-custom-theme' )) { |
|
+ runningIndicatorStyle = RunningIndicatorStyle.DOTS; |
|
+ } else { |
|
+ runningIndicatorStyle = settings.get_enum('running-indicator-style'); |
|
+ } |
|
+ |
|
+ switch (runningIndicatorStyle) { |
|
+ case RunningIndicatorStyle.DEFAULT: |
|
+ runningIndicator = new RunningIndicatorBase(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.DOTS: |
|
+ runningIndicator = new RunningIndicatorDots(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.SQUARES: |
|
+ runningIndicator = new RunningIndicatorSquares(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.DASHES: |
|
+ runningIndicator = new RunningIndicatorDashes(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.SEGMENTED: |
|
+ runningIndicator = new RunningIndicatorSegmented(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.SOLID: |
|
+ runningIndicator = new RunningIndicatorSolid(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.CILIORA: |
|
+ runningIndicator = new RunningIndicatorCiliora(source, settings); |
|
+ break; |
|
+ |
|
+ case RunningIndicatorStyle.METRO: |
|
+ runningIndicator = new RunningIndicatorMetro(source, settings); |
|
+ break; |
|
+ |
|
+ default: |
|
+ runningIndicator = new RunningIndicatorBase(source, settings); |
|
+ } |
|
+ |
|
+ this._indicators.push(runningIndicator); |
|
+ }, |
|
+ |
|
+ update: function() { |
|
+ for (let i=0; i<this._indicators.length; i++){ |
|
+ let indicator = this._indicators[i]; |
|
+ indicator.update(); |
|
+ } |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ for (let i=0; i<this._indicators.length; i++){ |
|
+ let indicator = this._indicators[i]; |
|
+ indicator.destroy(); |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+/* |
|
+ * Base class to be inherited by all indicators of any kind |
|
+*/ |
|
+const IndicatorBase = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.IndicatorBase', |
|
+ |
|
+ _init: function(source, settings) { |
|
+ this._settings = settings; |
|
+ this._source = source; |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ |
|
+ this._sourceDestroyId = this._source.actor.connect('destroy', |
|
+ Lang.bind(this._signalsHandler, this._signalsHandler.destroy) |
|
+ ); |
|
+ }, |
|
+ |
|
+ update: function() { |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this._source.actor.disconnect(this._sourceDestroyId); |
|
+ this._signalsHandler.destroy(); |
|
+ } |
|
+}); |
|
+ |
|
+/* |
|
+ * A base indicator class for running style, from which all other EunningIndicators should derive, |
|
+ * providing some basic methods, variables definitions and their update, css style classes handling. |
|
+ * |
|
+ */ |
|
+const RunningIndicatorBase = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorBase', |
|
+ Extends: IndicatorBase, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ |
|
+ this.parent(source, settings) |
|
+ |
|
+ this._side = Utils.getPosition(this._settings); |
|
+ this._nWindows = 0; |
|
+ |
|
+ this._dominantColorExtractor = new DominantColorExtractor(this._source.app); |
|
+ |
|
+ // These statuses take into account the workspace/monitor isolation |
|
+ this._isFocused = false; |
|
+ this._isRunning = false; |
|
+ }, |
|
+ |
|
+ update: function() { |
|
+ // Limit to 1 to MAX_WINDOWS_CLASSES windows classes |
|
+ this._nWindows = Math.min(this._source.getInterestingWindows().length, MAX_WINDOWS_CLASSES); |
|
+ |
|
+ // We need to check the number of windows, as the focus might be |
|
+ // happening on another monitor if using isolation |
|
+ if (tracker.focus_app == this._source.app && this._nWindows > 0) |
|
+ this._isFocused = true; |
|
+ else |
|
+ this._isFocused = false; |
|
+ |
|
+ // In the case of workspace isolation, we need to hide the dots of apps with |
|
+ // no windows in the current workspace |
|
+ if (this._source.app.state != Shell.AppState.STOPPED && this._nWindows > 0) |
|
+ this._isRunning = true; |
|
+ else |
|
+ this._isRunning = false; |
|
+ |
|
+ this._updateCounterClass(); |
|
+ this._updateFocusClass(); |
|
+ this._updateDefaultDot(); |
|
+ }, |
|
+ |
|
+ _updateCounterClass: function() { |
|
+ for (let i = 1; i <= MAX_WINDOWS_CLASSES; i++) { |
|
+ let className = 'running' + i; |
|
+ if (i != this._nWindows) |
|
+ this._source.actor.remove_style_class_name(className); |
|
+ else |
|
+ this._source.actor.add_style_class_name(className); |
|
+ } |
|
+ }, |
|
+ |
|
+ _updateFocusClass: function() { |
|
+ if (this._isFocused) |
|
+ this._source.actor.add_style_class_name('focused'); |
|
+ else |
|
+ this._source.actor.remove_style_class_name('focused'); |
|
+ }, |
|
+ |
|
+ _updateDefaultDot: function() { |
|
+ if (this._isRunning) |
|
+ this._source._dot.show(); |
|
+ else |
|
+ this._source._dot.hide(); |
|
+ }, |
|
+ |
|
+ _hideDefaultDot: function() { |
|
+ // I use opacity to hide the default dot because the show/hide function |
|
+ // are used by the parent class. |
|
+ this._source._dot.opacity = 0; |
|
+ }, |
|
+ |
|
+ _restoreDefaultDot: function() { |
|
+ this._source._dot.opacity = 255; |
|
+ }, |
|
+ |
|
+ _enableBacklight: function() { |
|
+ |
|
+ let colorPalette = this._dominantColorExtractor._getColorPalette(); |
|
+ |
|
+ // Fallback |
|
+ if (colorPalette === null) { |
|
+ this._source._iconContainer.set_style( |
|
+ 'border-radius: 5px;' + |
|
+ 'background-gradient-direction: vertical;' + |
|
+ 'background-gradient-start: #e0e0e0;' + |
|
+ 'background-gradient-end: darkgray;' |
|
+ ); |
|
+ |
|
+ return; |
|
+ } |
|
+ |
|
+ this._source._iconContainer.set_style( |
|
+ 'border-radius: 5px;' + |
|
+ 'background-gradient-direction: vertical;' + |
|
+ 'background-gradient-start: ' + colorPalette.original + ';' + |
|
+ 'background-gradient-end: ' + colorPalette.darker + ';' |
|
+ ); |
|
+ |
|
+ }, |
|
+ |
|
+ _disableBacklight: function() { |
|
+ this._source._iconContainer.set_style(null); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this.parent(); |
|
+ this._disableBacklight(); |
|
+ // Remove glossy background if the children still exists |
|
+ if (this._source._iconContainer.get_children().length > 1) |
|
+ this._source._iconContainer.get_children()[1].set_style(null); |
|
+ this._restoreDefaultDot(); |
|
+ } |
|
+}); |
|
+ |
|
+ |
|
+const RunningIndicatorDots = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorDots', |
|
+ Extends: RunningIndicatorBase, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ |
|
+ this.parent(source, settings) |
|
+ |
|
+ this._hideDefaultDot(); |
|
+ |
|
+ this._area = new St.DrawingArea({x_expand: true, y_expand: true}); |
|
+ |
|
+ // We draw for the bottom case and rotate the canvas for other placements |
|
+ //set center of rotatoins to the center |
|
+ this._area.set_pivot_point(0.5, 0.5); |
|
+ // prepare transformation matrix |
|
+ let m = new Cogl.Matrix(); |
|
+ m.init_identity(); |
|
+ |
|
+ switch (this._side) { |
|
+ case St.Side.TOP: |
|
+ m.xx = -1; |
|
+ m.rotate(180, 0, 0, 1); |
|
+ break |
|
+ |
|
+ case St.Side.BOTTOM: |
|
+ // nothing |
|
+ break; |
|
+ |
|
+ case St.Side.LEFT: |
|
+ m.yy = -1; |
|
+ m.rotate(90, 0, 0, 1); |
|
+ break; |
|
+ |
|
+ case St.Side.RIGHT: |
|
+ m.rotate(-90, 0, 0, 1); |
|
+ break |
|
+ } |
|
+ |
|
+ this._area.set_transform(m); |
|
+ |
|
+ this._area.connect('repaint', Lang.bind(this, this._updateIndicator)); |
|
+ this._source._iconContainer.add_child(this._area); |
|
+ |
|
+ let keys = ['custom-theme-running-dots-color', |
|
+ 'custom-theme-running-dots-border-color', |
|
+ 'custom-theme-running-dots-border-width', |
|
+ 'custom-theme-customize-running-dots', |
|
+ 'unity-backlit-items', |
|
+ 'running-indicator-dominant-color']; |
|
+ |
|
+ keys.forEach(function(key) { |
|
+ this._signalsHandler.add([ |
|
+ this._settings, |
|
+ 'changed::' + key, |
|
+ Lang.bind(this, this.update) |
|
+ ]); |
|
+ }, this); |
|
+ |
|
+ // Apply glossy background |
|
+ // TODO: move to enable/disableBacklit to apply itonly to the running apps? |
|
+ // TODO: move to css class for theming support |
|
+ let path = imports.misc.extensionUtils.getCurrentExtension().path; |
|
+ this._glossyBackgroundStyle = 'background-image: url(\'' + path + '/media/glossy.svg\');' + |
|
+ 'background-size: contain;'; |
|
+ |
|
+ }, |
|
+ |
|
+ update: function() { |
|
+ this.parent(); |
|
+ |
|
+ // Enable / Disable the backlight of running apps |
|
+ if (!this._settings.get_boolean('apply-custom-theme') && this._settings.get_boolean('unity-backlit-items')) { |
|
+ this._source._iconContainer.get_children()[1].set_style(this._glossyBackgroundStyle); |
|
+ if (this._isRunning) |
|
+ this._enableBacklight(); |
|
+ else |
|
+ this._disableBacklight(); |
|
+ } else { |
|
+ this._disableBacklight(); |
|
+ this._source._iconContainer.get_children()[1].set_style(null); |
|
+ } |
|
+ |
|
+ if (this._area) |
|
+ this._area.queue_repaint(); |
|
+ }, |
|
+ |
|
+ _computeStyle: function() { |
|
+ |
|
+ let [width, height] = this._area.get_surface_size(); |
|
+ this._width = height; |
|
+ this._height = width; |
|
+ |
|
+ // By defaut re-use the style - background color, and border width and color - |
|
+ // of the default dot |
|
+ let themeNode = this._source._dot.get_theme_node(); |
|
+ this._borderColor = themeNode.get_border_color(this._side); |
|
+ this._borderWidth = themeNode.get_border_width(this._side); |
|
+ this._bodyColor = themeNode.get_background_color(); |
|
+ |
|
+ if (!this._settings.get_boolean('apply-custom-theme')) { |
|
+ // Adjust for the backlit case |
|
+ if (this._settings.get_boolean('unity-backlit-items')) { |
|
+ // Use dominant color for dots too if the backlit is enables |
|
+ let colorPalette = this._dominantColorExtractor._getColorPalette(); |
|
+ |
|
+ // Slightly adjust the styling |
|
+ this._borderWidth = 2; |
|
+ |
|
+ if (colorPalette !== null) { |
|
+ this._borderColor = Clutter.color_from_string(colorPalette.lighter)[1] ; |
|
+ this._bodyColor = Clutter.color_from_string(colorPalette.darker)[1]; |
|
+ } else { |
|
+ // Fallback |
|
+ this._borderColor = Clutter.color_from_string('white')[1]; |
|
+ this._bodyColor = Clutter.color_from_string('gray')[1]; |
|
+ } |
|
+ } |
|
+ |
|
+ // Apply dominant color if requested |
|
+ if (this._settings.get_boolean('running-indicator-dominant-color')) { |
|
+ let colorPalette = this._dominantColorExtractor._getColorPalette(); |
|
+ if (colorPalette !== null) { |
|
+ this._bodyColor = Clutter.color_from_string(colorPalette.original)[1]; |
|
+ } |
|
+ } |
|
+ |
|
+ // Finally, use customize style if requested |
|
+ if (this._settings.get_boolean('custom-theme-customize-running-dots')) { |
|
+ this._borderColor = Clutter.color_from_string(this._settings.get_string('custom-theme-running-dots-border-color'))[1]; |
|
+ this._borderWidth = this._settings.get_int('custom-theme-running-dots-border-width'); |
|
+ this._bodyColor = Clutter.color_from_string(this._settings.get_string('custom-theme-running-dots-color'))[1]; |
|
+ } |
|
+ } |
|
+ |
|
+ // Define the radius as an arbitrary size, but keep large enough to account |
|
+ // for the drawing of the border. |
|
+ this._radius = Math.max(this._width/22, this._borderWidth/2); |
|
+ this._padding = 0; // distance from the margin |
|
+ this._spacing = this._radius + this._borderWidth; // separation between the dots |
|
+ }, |
|
+ |
|
+ _updateIndicator: function() { |
|
+ |
|
+ let area = this._area; |
|
+ let cr = this._area.get_context(); |
|
+ |
|
+ this._computeStyle(); |
|
+ this._drawIndicator(cr); |
|
+ cr.$dispose(); |
|
+ }, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ // Draw the required numbers of dots |
|
+ let n = this._nWindows; |
|
+ |
|
+ cr.setLineWidth(this._borderWidth); |
|
+ Clutter.cairo_set_source_color(cr, this._borderColor); |
|
+ |
|
+ // draw for the bottom case: |
|
+ cr.translate((this._width - (2*n)*this._radius - (n-1)*this._spacing)/2, this._height - this._padding); |
|
+ for (let i = 0; i < n; i++) { |
|
+ cr.newSubPath(); |
|
+ cr.arc((2*i+1)*this._radius + i*this._spacing, -this._radius - this._borderWidth/2, this._radius, 0, 2*Math.PI); |
|
+ } |
|
+ |
|
+ cr.strokePreserve(); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.fill(); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this.parent(); |
|
+ this._area.destroy(); |
|
+ } |
|
+ |
|
+}); |
|
+ |
|
+// Adapted from dash-to-panel by Jason DeRose |
|
+// https://github.com/jderose9/dash-to-panel |
|
+const RunningIndicatorCiliora = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorCiliora', |
|
+ Extends: RunningIndicatorDots, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ if (this._isRunning) { |
|
+ |
|
+ let size = Math.max(this._width/20, this._borderWidth); |
|
+ let spacing = size; // separation between the dots |
|
+ let lineLength = this._width - (size*(this._nWindows-1)) - (spacing*(this._nWindows-1)); |
|
+ let padding = this._borderWidth; |
|
+ // For the backlit case here we don't want the outer border visible |
|
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots')) |
|
+ padding = 0; |
|
+ let yOffset = this._height - padding - size; |
|
+ |
|
+ cr.setLineWidth(this._borderWidth); |
|
+ Clutter.cairo_set_source_color(cr, this._borderColor); |
|
+ |
|
+ cr.translate(0, yOffset); |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(0, 0, lineLength, size); |
|
+ for (let i = 1; i < this._nWindows; i++) { |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(lineLength + (i*spacing) + ((i-1)*size), 0, size, size); |
|
+ } |
|
+ |
|
+ cr.strokePreserve(); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.fill(); |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+// Adapted from dash-to-panel by Jason DeRose |
|
+// https://github.com/jderose9/dash-to-panel |
|
+const RunningIndicatorSegmented = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorSegmented', |
|
+ Extends: RunningIndicatorDots, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ if (this._isRunning) { |
|
+ let size = Math.max(this._width/20, this._borderWidth); |
|
+ let spacing = Math.ceil(this._width/18); // separation between the dots |
|
+ let dashLength = Math.ceil((this._width - ((this._nWindows-1)*spacing))/this._nWindows); |
|
+ let lineLength = this._width - (size*(this._nWindows-1)) - (spacing*(this._nWindows-1)); |
|
+ let padding = this._borderWidth; |
|
+ // For the backlit case here we don't want the outer border visible |
|
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots')) |
|
+ padding = 0; |
|
+ let yOffset = this._height - padding - size; |
|
+ |
|
+ cr.setLineWidth(this._borderWidth); |
|
+ Clutter.cairo_set_source_color(cr, this._borderColor); |
|
+ |
|
+ cr.translate(0, yOffset); |
|
+ for (let i = 0; i < this._nWindows; i++) { |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size); |
|
+ } |
|
+ |
|
+ cr.strokePreserve(); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.fill() |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+// Adapted from dash-to-panel by Jason DeRose |
|
+// https://github.com/jderose9/dash-to-panel |
|
+const RunningIndicatorSolid = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorSolid', |
|
+ Extends: RunningIndicatorDots, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ if (this._isRunning) { |
|
+ |
|
+ let size = Math.max(this._width/20, this._borderWidth); |
|
+ let padding = this._borderWidth; |
|
+ // For the backlit case here we don't want the outer border visible |
|
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots')) |
|
+ padding = 0; |
|
+ let yOffset = this._height - padding - size; |
|
+ |
|
+ cr.setLineWidth(this._borderWidth); |
|
+ Clutter.cairo_set_source_color(cr, this._borderColor); |
|
+ |
|
+ cr.translate(0, yOffset); |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(0, 0, this._width, size); |
|
+ |
|
+ cr.strokePreserve(); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.fill(); |
|
+ |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+// Adapted from dash-to-panel by Jason DeRose |
|
+// https://github.com/jderose9/dash-to-panel |
|
+const RunningIndicatorSquares = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorSquares', |
|
+ Extends: RunningIndicatorDots, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ if (this._isRunning) { |
|
+ let size = Math.max(this._width/11, this._borderWidth); |
|
+ let padding = this._borderWidth; |
|
+ let spacing = Math.ceil(this._width/18); // separation between the dots |
|
+ let yOffset = this._height - padding - size; |
|
+ |
|
+ cr.setLineWidth(this._borderWidth); |
|
+ Clutter.cairo_set_source_color(cr, this._borderColor); |
|
+ |
|
+ cr.translate(Math.floor((this._width - this._nWindows*size - (this._nWindows-1)*spacing)/2), yOffset); |
|
+ for (let i = 0; i < this._nWindows; i++) { |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(i*size + i*spacing, 0, size, size); |
|
+ } |
|
+ cr.strokePreserve(); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.fill(); |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+// Adapted from dash-to-panel by Jason DeRose |
|
+// https://github.com/jderose9/dash-to-panel |
|
+const RunningIndicatorDashes = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorDashes', |
|
+ Extends: RunningIndicatorDots, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ if (this._isRunning) { |
|
+ let size = Math.max(this._width/20, this._borderWidth); |
|
+ let padding = this._borderWidth; |
|
+ let spacing = Math.ceil(this._width/18); // separation between the dots |
|
+ let dashLength = Math.floor(this._width/4) - spacing; |
|
+ let yOffset = this._height - padding - size; |
|
+ |
|
+ cr.setLineWidth(this._borderWidth); |
|
+ Clutter.cairo_set_source_color(cr, this._borderColor); |
|
+ |
|
+ cr.translate(Math.floor((this._width - this._nWindows*dashLength - (this._nWindows-1)*spacing)/2), yOffset); |
|
+ for (let i = 0; i < this._nWindows; i++) { |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(i*dashLength + i*spacing, 0, dashLength, size); |
|
+ } |
|
+ |
|
+ cr.strokePreserve(); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.fill(); |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+// Adapted from dash-to-panel by Jason DeRose |
|
+// https://github.com/jderose9/dash-to-panel |
|
+const RunningIndicatorMetro = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.RunningIndicatorMetro', |
|
+ Extends: RunningIndicatorDots, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ this.parent(source, settings); |
|
+ this._source.actor.add_style_class_name('metro'); |
|
+ }, |
|
+ |
|
+ _drawIndicator: function(cr) { |
|
+ if (this._isRunning) { |
|
+ let size = Math.max(this._width/20, this._borderWidth); |
|
+ let padding = 0; |
|
+ // For the backlit case here we don't want the outer border visible |
|
+ if (this._settings.get_boolean('unity-backlit-items') && !this._settings.get_boolean('custom-theme-customize-running-dots')) |
|
+ padding = 0; |
|
+ let yOffset = this._height - padding - size; |
|
+ |
|
+ let n = this._nWindows; |
|
+ if(n <= 1) { |
|
+ cr.translate(0, yOffset); |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(0, 0, this._width, size); |
|
+ cr.fill(); |
|
+ } else { |
|
+ let blackenedLength = (1/48)*this._width; // need to scale with the SVG for the stacked highlight |
|
+ let darkenedLength = this._isFocused ? (2/48)*this._width : (10/48)*this._width; |
|
+ let blackenedColor = this._bodyColor.shade(.3); |
|
+ let darkenedColor = this._bodyColor.shade(.7); |
|
+ |
|
+ cr.translate(0, yOffset); |
|
+ |
|
+ Clutter.cairo_set_source_color(cr, this._bodyColor); |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(0, 0, this._width - darkenedLength - blackenedLength, size); |
|
+ cr.fill(); |
|
+ Clutter.cairo_set_source_color(cr, blackenedColor); |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(this._width - darkenedLength - blackenedLength, 0, 1, size); |
|
+ cr.fill(); |
|
+ Clutter.cairo_set_source_color(cr, darkenedColor); |
|
+ cr.newSubPath(); |
|
+ cr.rectangle(this._width - darkenedLength, 0, darkenedLength, size); |
|
+ cr.fill(); |
|
+ } |
|
+ } |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this.parent(); |
|
+ this._source.actor.remove_style_class_name('metro'); |
|
+ } |
|
+}); |
|
+ |
|
+/* |
|
+ * Unity like notification and progress indicators |
|
+ */ |
|
+const UnityIndicator = new Lang.Class({ |
|
+ Name: 'DashToDock.UnityIndicator', |
|
+ Extends: IndicatorBase, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ |
|
+ this.parent(source, settings); |
|
+ |
|
+ this._notificationBadgeLabel = new St.Label(); |
|
+ this._notificationBadgeBin = new St.Bin({ |
|
+ child: this._notificationBadgeLabel, |
|
+ x_align: St.Align.END, y_align: St.Align.START, |
|
+ x_expand: true, y_expand: true |
|
+ }); |
|
+ this._notificationBadgeLabel.add_style_class_name('notification-badge'); |
|
+ this._notificationBadgeCount = 0; |
|
+ this._notificationBadgeBin.hide(); |
|
+ |
|
+ this._source._iconContainer.add_child(this._notificationBadgeBin); |
|
+ this._source._iconContainer.connect('allocation-changed', Lang.bind(this, this.updateNotificationBadge)); |
|
+ |
|
+ this._remoteEntries = []; |
|
+ this._source.remoteModel.lookupById(this._source.app.id).forEach( |
|
+ Lang.bind(this, function(entry) { |
|
+ this.insertEntryRemote(entry); |
|
+ }) |
|
+ ); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ this._source.remoteModel, |
|
+ 'entry-added', |
|
+ Lang.bind(this, this._onLauncherEntryRemoteAdded) |
|
+ ], [ |
|
+ this._source.remoteModel, |
|
+ 'entry-removed', |
|
+ Lang.bind(this, this._onLauncherEntryRemoteRemoved) |
|
+ ]) |
|
+ }, |
|
+ |
|
+ _onLauncherEntryRemoteAdded: function(remoteModel, entry) { |
|
+ if (!entry || !entry.appId()) |
|
+ return; |
|
+ if (this._source && this._source.app && this._source.app.id == entry.appId()) { |
|
+ this.insertEntryRemote(entry); |
|
+ } |
|
+ }, |
|
+ |
|
+ _onLauncherEntryRemoteRemoved: function(remoteModel, entry) { |
|
+ if (!entry || !entry.appId()) |
|
+ return; |
|
+ |
|
+ if (this._source && this._source.app && this._source.app.id == entry.appId()) { |
|
+ this.removeEntryRemote(entry); |
|
+ } |
|
+ }, |
|
+ |
|
+ updateNotificationBadge: function() { |
|
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ let [minWidth, natWidth] = this._source._iconContainer.get_preferred_width(-1); |
|
+ let logicalNatWidth = natWidth / scaleFactor; |
|
+ let font_size = Math.max(10, Math.round(logicalNatWidth / 5)); |
|
+ let margin_left = Math.round(logicalNatWidth / 4); |
|
+ |
|
+ this._notificationBadgeLabel.set_style( |
|
+ 'font-size: ' + font_size + 'px;' + |
|
+ 'margin-left: ' + margin_left + 'px;' |
|
+ ); |
|
+ |
|
+ this._notificationBadgeBin.width = Math.round(logicalNatWidth - margin_left); |
|
+ this._notificationBadgeLabel.clutter_text.ellipsize = Pango.EllipsizeMode.MIDDLE; |
|
+ }, |
|
+ |
|
+ _notificationBadgeCountToText: function(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: function(count) { |
|
+ this._notificationBadgeCount = count; |
|
+ let text = this._notificationBadgeCountToText(count); |
|
+ this._notificationBadgeLabel.set_text(text); |
|
+ }, |
|
+ |
|
+ toggleNotificationBadge: function(activate) { |
|
+ if (activate && this._notificationBadgeCount > 0) { |
|
+ this.updateNotificationBadge(); |
|
+ this._notificationBadgeBin.show(); |
|
+ } |
|
+ else |
|
+ this._notificationBadgeBin.hide(); |
|
+ }, |
|
+ |
|
+ _showProgressOverlay: function() { |
|
+ if (this._progressOverlayArea) { |
|
+ this._updateProgressOverlay(); |
|
+ return; |
|
+ } |
|
+ |
|
+ this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true}); |
|
+ this._progressOverlayArea.connect('repaint', Lang.bind(this, function() { |
|
+ this._drawProgressOverlay(this._progressOverlayArea); |
|
+ })); |
|
+ |
|
+ this._source._iconContainer.add_child(this._progressOverlayArea); |
|
+ this._updateProgressOverlay(); |
|
+ }, |
|
+ |
|
+ _hideProgressOverlay: function() { |
|
+ if (this._progressOverlayArea) |
|
+ this._progressOverlayArea.destroy(); |
|
+ this._progressOverlayArea = null; |
|
+ }, |
|
+ |
|
+ _updateProgressOverlay: function() { |
|
+ if (this._progressOverlayArea) |
|
+ this._progressOverlayArea.queue_repaint(); |
|
+ }, |
|
+ |
|
+ _drawProgressOverlay: function(area) { |
|
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ 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(0.5, 0.5, 0.5, 0.5, 0.1); |
|
+ stroke.addColorStopRGBA(0.9, 0.8, 0.8, 0.8, 0.4); |
|
+ 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 = Cairo.SolidPattern.createRGBA(0.20, 0.20, 0.20, 0.9); |
|
+ fill = new Cairo.LinearGradient(0, y, 0, y + height); |
|
+ fill.addColorStopRGBA(0.4, 0.25, 0.25, 0.25, 1.0); |
|
+ fill.addColorStopRGBA(0.9, 0.35, 0.35, 0.35, 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); |
|
+ stroke = Cairo.SolidPattern.createRGBA(0.8, 0.8, 0.8, 1.0); |
|
+ fill = Cairo.SolidPattern.createRGBA(0.9, 0.9, 0.9, 1.0); |
|
+ |
|
+ 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: function(progress) { |
|
+ this._progress = Math.min(Math.max(progress, 0.0), 1.0); |
|
+ this._updateProgressOverlay(); |
|
+ }, |
|
+ |
|
+ toggleProgressOverlay: function(activate) { |
|
+ if (activate) { |
|
+ this._showProgressOverlay(); |
|
+ } |
|
+ else { |
|
+ this._hideProgressOverlay(); |
|
+ } |
|
+ }, |
|
+ |
|
+ insertEntryRemote: function(remote) { |
|
+ if (!remote || this._remoteEntries.indexOf(remote) !== -1) |
|
+ return; |
|
+ |
|
+ this._remoteEntries.push(remote); |
|
+ this._selectEntryRemote(remote); |
|
+ }, |
|
+ |
|
+ removeEntryRemote: function(remote) { |
|
+ if (!remote || this._remoteEntries.indexOf(remote) == -1) |
|
+ return; |
|
+ |
|
+ this._remoteEntries.splice(this._remoteEntries.indexOf(remote), 1); |
|
+ |
|
+ if (this._remoteEntries.length > 0) { |
|
+ this._selectEntryRemote(this._remoteEntries[this._remoteEntries.length-1]); |
|
+ } else { |
|
+ this.setNotificationBadge(0); |
|
+ this.toggleNotificationBadge(false); |
|
+ this.setProgress(0); |
|
+ this.toggleProgressOverlay(false); |
|
+ } |
|
+ }, |
|
+ |
|
+ _selectEntryRemote: function(remote) { |
|
+ if (!remote) |
|
+ return; |
|
+ |
|
+ this._signalsHandler.removeWithLabel('entry-remotes'); |
|
+ |
|
+ this._signalsHandler.addWithLabel('entry-remotes', |
|
+ [ |
|
+ remote, |
|
+ 'count-changed', |
|
+ Lang.bind(this, (remote, value) => { |
|
+ this.setNotificationBadge(value); |
|
+ }) |
|
+ ], [ |
|
+ remote, |
|
+ 'count-visible-changed', |
|
+ Lang.bind(this, (remote, value) => { |
|
+ this.toggleNotificationBadge(value); |
|
+ }) |
|
+ ], [ |
|
+ remote, |
|
+ 'progress-changed', |
|
+ Lang.bind(this, (remote, value) => { |
|
+ this.setProgress(value); |
|
+ }) |
|
+ ], [ |
|
+ remote, |
|
+ 'progress-visible-changed', |
|
+ Lang.bind(this, (remote, value) => { |
|
+ this.toggleProgressOverlay(value); |
|
+ }) |
|
+ ]); |
|
+ |
|
+ this.setNotificationBadge(remote.count()); |
|
+ this.toggleNotificationBadge(remote.countVisible()); |
|
+ this.setProgress(remote.progress()); |
|
+ this.toggleProgressOverlay(remote.progressVisible()); |
|
+ } |
|
+}); |
|
+ |
|
+ |
|
+// We need an icons theme object, this is the only way I managed to get |
|
+// pixel buffers that can be used for calculating the backlight color |
|
+let themeLoader = null; |
|
+ |
|
+// Global icon cache. Used for Unity7 styling. |
|
+let iconCacheMap = new Map(); |
|
+// Max number of items to store |
|
+// We don't expect to ever reach this number, but let's put an hard limit to avoid |
|
+// even the remote possibility of the cached items to grow indefinitely. |
|
+const MAX_CACHED_ITEMS = 1000; |
|
+// When the size exceed it, the oldest 'n' ones are deleted |
|
+const BATCH_SIZE_TO_DELETE = 50; |
|
+// The icon size used to extract the dominant color |
|
+const DOMINANT_COLOR_ICON_SIZE = 64; |
|
+ |
|
+// Compute dominant color frim the app icon. |
|
+// The color is cached for efficiency. |
|
+const DominantColorExtractor = new Lang.Class({ |
|
+ Name: 'DashToDock.DominantColorExtractor', |
|
+ |
|
+ _init: function(app) { |
|
+ this._app = app; |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Try to get the pixel buffer for the current icon, if not fail gracefully |
|
+ */ |
|
+ _getIconPixBuf: function() { |
|
+ let iconTexture = this._app.create_icon_texture(16); |
|
+ |
|
+ if (themeLoader === null) { |
|
+ let ifaceSettings = new Gio.Settings({ schema: "org.gnome.desktop.interface" }); |
|
+ |
|
+ themeLoader = new Gtk.IconTheme(), |
|
+ themeLoader.set_custom_theme(ifaceSettings.get_string('icon-theme')); // Make sure the correct theme is loaded |
|
+ } |
|
+ |
|
+ // 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 |
|
+ let icon_info = themeLoader.lookup_icon(iconTexture.get_names()[0], DOMINANT_COLOR_ICON_SIZE, 0); |
|
+ if (icon_info !== null) |
|
+ return icon_info.load_icon(); |
|
+ else |
|
+ 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: function() { |
|
+ 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(), |
|
+ offset = 0; |
|
+ |
|
+ 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 = Utils.ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255); |
|
+ |
|
+ if (hsv.s > 0.15) |
|
+ hsv.s = 0.65; |
|
+ hsv.v = 0.90; |
|
+ |
|
+ let rgb = Utils.ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v); |
|
+ |
|
+ // Cache the result. |
|
+ let backgroundColor = { |
|
+ lighter: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0.2), |
|
+ original: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0), |
|
+ darker: Utils.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: function (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; |
|
+ }, |
|
+}) |
|
diff --git a/extensions/dash-to-dock/appIcons.js b/extensions/dash-to-dock/appIcons.js |
|
new file mode 100644 |
|
index 0000000..ef9c6e1 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/appIcons.js |
|
@@ -0,0 +1,1171 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const GdkPixbuf = imports.gi.GdkPixbuf |
|
+const Gio = imports.gi.Gio; |
|
+const GLib = imports.gi.GLib; |
|
+const Gtk = imports.gi.Gtk; |
|
+const Signals = imports.signals; |
|
+const Lang = imports.lang; |
|
+const Meta = imports.gi.Meta; |
|
+const Shell = imports.gi.Shell; |
|
+const St = imports.gi.St; |
|
+const Mainloop = imports.mainloop; |
|
+ |
|
+// Use __ () and N__() for the extension gettext domain, and reuse |
|
+// the shell domain with the default _() and N_() |
|
+const Gettext = imports.gettext.domain('dashtodock'); |
|
+const __ = Gettext.gettext; |
|
+const N__ = function(e) { return e }; |
|
+ |
|
+const AppDisplay = imports.ui.appDisplay; |
|
+const AppFavorites = imports.ui.appFavorites; |
|
+const Dash = imports.ui.dash; |
|
+const DND = imports.ui.dnd; |
|
+const IconGrid = imports.ui.iconGrid; |
|
+const Main = imports.ui.main; |
|
+const PopupMenu = imports.ui.popupMenu; |
|
+const Tweener = imports.ui.tweener; |
|
+const Util = imports.misc.util; |
|
+const Workspace = imports.ui.workspace; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Utils = Me.imports.utils; |
|
+const WindowPreview = Me.imports.windowPreview; |
|
+const AppIconIndicators = Me.imports.appIconIndicators; |
|
+ |
|
+let tracker = Shell.WindowTracker.get_default(); |
|
+ |
|
+let DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME; |
|
+ |
|
+const clickAction = { |
|
+ SKIP: 0, |
|
+ MINIMIZE: 1, |
|
+ LAUNCH: 2, |
|
+ CYCLE_WINDOWS: 3, |
|
+ MINIMIZE_OR_OVERVIEW: 4, |
|
+ PREVIEWS: 5, |
|
+ MINIMIZE_OR_PREVIEWS: 6, |
|
+ QUIT: 7 |
|
+}; |
|
+ |
|
+const scrollAction = { |
|
+ DO_NOTHING: 0, |
|
+ CYCLE_WINDOWS: 1, |
|
+ SWITCH_WORKSPACE: 2 |
|
+}; |
|
+ |
|
+let recentlyClickedAppLoopId = 0; |
|
+let recentlyClickedApp = null; |
|
+let recentlyClickedAppWindows = null; |
|
+let recentlyClickedAppIndex = 0; |
|
+let recentlyClickedAppMonitor = -1; |
|
+ |
|
+/** |
|
+ * Extend AppIcon |
|
+ * |
|
+ * - Pass settings to the constructor and bind settings changes |
|
+ * - Apply a css class based on the number of windows of each application (#N); |
|
+ * - Customized indicators for running applications in place of 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 |
|
+ * - Update menu if open on windows change |
|
+ */ |
|
+var MyAppIcon = new Lang.Class({ |
|
+ Name: 'DashToDock.AppIcon', |
|
+ Extends: AppDisplay.AppIcon, |
|
+ |
|
+ // settings are required inside. |
|
+ _init: function(settings, remoteModel, app, monitorIndex, iconParams) { |
|
+ // a prefix is required to avoid conflicting with the parent class variable |
|
+ this._dtdSettings = settings; |
|
+ this.monitorIndex = monitorIndex; |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ this.remoteModel = remoteModel; |
|
+ this._indicator = null; |
|
+ |
|
+ this.parent(app, iconParams); |
|
+ |
|
+ this._updateIndicatorStyle(); |
|
+ |
|
+ // 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._windowsChangedId = this.app.connect('windows-changed', |
|
+ Lang.bind(this, |
|
+ this.onWindowsChanged)); |
|
+ this._focusAppChangeId = tracker.connect('notify::focus-app', |
|
+ Lang.bind(this, |
|
+ this._onFocusAppChanged)); |
|
+ |
|
+ // In Wayland sessions, this signal is needed to track the state of windows dragged |
|
+ // from one monitor to another. As this is triggered quite often (whenever a new winow |
|
+ // of any application opened or moved to a different desktop), |
|
+ // we restrict this signal to the case when 'isolate-monitors' is true, |
|
+ // and if there are at least 2 monitors. |
|
+ if (this._dtdSettings.get_boolean('isolate-monitors') && |
|
+ Main.layoutManager.monitors.length > 1) { |
|
+ this._signalsHandler.removeWithLabel('isolate-monitors'); |
|
+ this._signalsHandler.addWithLabel('isolate-monitors', [ |
|
+ global.screen, |
|
+ 'window-entered-monitor', |
|
+ Lang.bind(this, this._onWindowEntered) |
|
+ ]); |
|
+ } |
|
+ |
|
+ this._progressOverlayArea = null; |
|
+ this._progress = 0; |
|
+ |
|
+ let keys = ['apply-custom-theme', |
|
+ 'running-indicator-style', |
|
+ ]; |
|
+ |
|
+ keys.forEach(function(key) { |
|
+ this._signalsHandler.add([ |
|
+ this._dtdSettings, |
|
+ 'changed::' + key, |
|
+ Lang.bind(this, this._updateIndicatorStyle) |
|
+ ]); |
|
+ }, this); |
|
+ |
|
+ this._dtdSettings.connect('changed::scroll-action', Lang.bind(this, function() { |
|
+ this._optionalScrollCycleWindows(); |
|
+ })); |
|
+ this._optionalScrollCycleWindows(); |
|
+ |
|
+ this._numberOverlay(); |
|
+ |
|
+ this._previewMenuManager = null; |
|
+ this._previewMenu = null; |
|
+ }, |
|
+ |
|
+ _onDestroy: function() { |
|
+ this.parent(); |
|
+ |
|
+ // This is necessary due to an upstream bug |
|
+ // https://bugzilla.gnome.org/show_bug.cgi?id=757556 |
|
+ // It can be safely removed once it get solved upstrea. |
|
+ if (this._menu) |
|
+ this._menu.close(false); |
|
+ |
|
+ // Disconect global signals |
|
+ |
|
+ if (this._windowsChangedId > 0) |
|
+ this.app.disconnect(this._windowsChangedId); |
|
+ this._windowsChangedId = 0; |
|
+ |
|
+ if (this._focusAppChangeId > 0) { |
|
+ tracker.disconnect(this._focusAppChangeId); |
|
+ this._focusAppChangeId = 0; |
|
+ } |
|
+ |
|
+ this._signalsHandler.destroy(); |
|
+ |
|
+ if (this._scrollEventHandler) |
|
+ this.actor.disconnect(this._scrollEventHandler); |
|
+ }, |
|
+ |
|
+ // TOOD Rename this function |
|
+ _updateIndicatorStyle: function() { |
|
+ |
|
+ if (this._indicator !== null) { |
|
+ this._indicator.destroy(); |
|
+ this._indicator = null; |
|
+ } |
|
+ this._indicator = new AppIconIndicators.AppIconIndicator(this, this._dtdSettings); |
|
+ this._indicator.update(); |
|
+ }, |
|
+ |
|
+ _onWindowEntered: function(metaScreen, monitorIndex, metaWin) { |
|
+ let app = Shell.WindowTracker.get_default().get_window_app(metaWin); |
|
+ if (app && app.get_id() == this.app.get_id()) |
|
+ this.onWindowsChanged(); |
|
+ }, |
|
+ |
|
+ _optionalScrollCycleWindows: function() { |
|
+ if (this._scrollEventHandler) { |
|
+ this.actor.disconnect(this._scrollEventHandler); |
|
+ this._scrollEventHandler = 0; |
|
+ } |
|
+ |
|
+ let isEnabled = this._dtdSettings.get_enum('scroll-action') === scrollAction.CYCLE_WINDOWS; |
|
+ if (!isEnabled) return; |
|
+ this._scrollEventHandler = this.actor.connect('scroll-event', Lang.bind(this, |
|
+ this.onScrollEvent)); |
|
+ }, |
|
+ |
|
+ onScrollEvent: function(actor, event) { |
|
+ |
|
+ // We only activate windows of running applications, i.e. we never open new windows |
|
+ // 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 |
|
+ && this.getInterestingWindows().length > 0; |
|
+ |
|
+ if (!appIsRunning) |
|
+ return false |
|
+ |
|
+ if (this._optionalScrollCycleWindowsDeadTimeId > 0) |
|
+ return false; |
|
+ else |
|
+ this._optionalScrollCycleWindowsDeadTimeId = Mainloop.timeout_add(250, Lang.bind(this, function() { |
|
+ this._optionalScrollCycleWindowsDeadTimeId = 0; |
|
+ })); |
|
+ |
|
+ let direction = null; |
|
+ |
|
+ switch (event.get_scroll_direction()) { |
|
+ case Clutter.ScrollDirection.UP: |
|
+ direction = Meta.MotionDirection.UP; |
|
+ break; |
|
+ case Clutter.ScrollDirection.DOWN: |
|
+ direction = Meta.MotionDirection.DOWN; |
|
+ break; |
|
+ case Clutter.ScrollDirection.SMOOTH: |
|
+ let [dx, dy] = event.get_scroll_delta(); |
|
+ if (dy < 0) |
|
+ direction = Meta.MotionDirection.UP; |
|
+ else if (dy > 0) |
|
+ direction = Meta.MotionDirection.DOWN; |
|
+ break; |
|
+ } |
|
+ |
|
+ let focusedApp = tracker.focus_app; |
|
+ if (!Main.overview._shown) { |
|
+ let reversed = direction === Meta.MotionDirection.UP; |
|
+ if (this.app == focusedApp) |
|
+ this._cycleThroughWindows(reversed); |
|
+ else { |
|
+ // Activate the first window |
|
+ let windows = this.getInterestingWindows(); |
|
+ if (windows.length > 0) { |
|
+ let w = windows[0]; |
|
+ Main.activateWindow(w); |
|
+ } |
|
+ } |
|
+ } |
|
+ else |
|
+ this.app.activate(); |
|
+ return true; |
|
+ }, |
|
+ |
|
+ onWindowsChanged: function() { |
|
+ |
|
+ if (this._menu && this._menu.isOpen) |
|
+ this._menu.update(); |
|
+ |
|
+ this._indicator.update(); |
|
+ this.updateIconGeometry(); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Update taraget for minimization animation |
|
+ */ |
|
+ updateIconGeometry: function() { |
|
+ // 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.actor.get_stage() == null) |
|
+ return; |
|
+ |
|
+ let rect = new Meta.Rectangle(); |
|
+ |
|
+ [rect.x, rect.y] = this.actor.get_transformed_position(); |
|
+ [rect.width, rect.height] = this.actor.get_transformed_size(); |
|
+ |
|
+ let windows = this.app.get_windows(); |
|
+ if (this._dtdSettings.get_boolean('multi-monitor')){ |
|
+ let monitorIndex = this.monitorIndex; |
|
+ windows = windows.filter(function(w) { |
|
+ return w.get_monitor() == monitorIndex; |
|
+ }); |
|
+ } |
|
+ windows.forEach(function(w) { |
|
+ w.set_icon_geometry(rect); |
|
+ }); |
|
+ }, |
|
+ |
|
+ _updateRunningStyle: function() { |
|
+ // The logic originally in this function has been moved to |
|
+ // AppIconIndicatorBase._updateDefaultDot(). However it cannot be removed as |
|
+ // it called by the parent constructor. |
|
+ }, |
|
+ |
|
+ popupMenu: function() { |
|
+ this._removeMenuTimeout(); |
|
+ this.actor.fake_release(); |
|
+ this._draggable.fakeRelease(); |
|
+ |
|
+ if (!this._menu) { |
|
+ this._menu = new MyAppIconMenu(this, this._dtdSettings); |
|
+ this._menu.connect('activate-window', Lang.bind(this, function(menu, window) { |
|
+ this.activateWindow(window); |
|
+ })); |
|
+ this._menu.connect('open-state-changed', Lang.bind(this, function(menu, isPoppedUp) { |
|
+ if (!isPoppedUp) |
|
+ this._onMenuPoppedDown(); |
|
+ else { |
|
+ // Setting the max-height is s useful if part of the menu is |
|
+ // scrollable so the minimum height is smaller than the natural height. |
|
+ let monitor_index = Main.layoutManager.findIndexForActor(this.actor); |
|
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(monitor_index); |
|
+ let position = Utils.getPosition(this._dtdSettings); |
|
+ this._isHorizontal = ( position == St.Side.TOP || |
|
+ position == St.Side.BOTTOM); |
|
+ // If horizontal also remove the height of the dash |
|
+ let additional_margin = this._isHorizontal && !this._dtdSettings.get_boolean('dock-fixed') ? Main.overview._dash.actor.height : 0; |
|
+ let verticalMargins = this._menu.actor.margin_top + this._menu.actor.margin_bottom; |
|
+ // Also set a max width to the menu, so long labels (long windows title) get truncated |
|
+ this._menu.actor.style = ('max-height: ' + Math.round(workArea.height - additional_margin - verticalMargins) + 'px;' + |
|
+ 'max-width: 400px'); |
|
+ } |
|
+ })); |
|
+ let id = Main.overview.connect('hiding', Lang.bind(this, function() { |
|
+ this._menu.close(); |
|
+ })); |
|
+ this._menu.actor.connect('destroy', function() { |
|
+ Main.overview.disconnect(id); |
|
+ }); |
|
+ |
|
+ this._menuManager.addMenu(this._menu); |
|
+ } |
|
+ |
|
+ this.emit('menu-state-changed', true); |
|
+ |
|
+ this.actor.set_hover(true); |
|
+ this._menu.popup(); |
|
+ this._menuManager.ignoreRelease(); |
|
+ this.emit('sync-tooltip'); |
|
+ |
|
+ return false; |
|
+ }, |
|
+ |
|
+ _onFocusAppChanged: function() { |
|
+ this._indicator.update(); |
|
+ }, |
|
+ |
|
+ activate: function(button) { |
|
+ let event = Clutter.get_current_event(); |
|
+ let modifiers = event ? event.get_state() : 0; |
|
+ let focusedApp = tracker.focus_app; |
|
+ |
|
+ // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.) |
|
+ modifiers = modifiers & (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK); |
|
+ |
|
+ // We don't change the CTRL-click behaviour: in such case we just chain |
|
+ // up the parent method and return. |
|
+ if (modifiers & Clutter.ModifierType.CONTROL_MASK) { |
|
+ // Keep default behaviour: launch new window |
|
+ // By calling the parent method I make it compatible |
|
+ // with other extensions tweaking ctrl + click |
|
+ this.parent(button); |
|
+ return; |
|
+ } |
|
+ |
|
+ // 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; |
|
+ if (button && button == 2 ) { |
|
+ if (modifiers & Clutter.ModifierType.SHIFT_MASK) |
|
+ buttonAction = this._dtdSettings.get_enum('shift-middle-click-action'); |
|
+ else |
|
+ buttonAction = this._dtdSettings.get_enum('middle-click-action'); |
|
+ } |
|
+ else if (button && button == 1) { |
|
+ if (modifiers & Clutter.ModifierType.SHIFT_MASK) |
|
+ buttonAction = this._dtdSettings.get_enum('shift-click-action'); |
|
+ else |
|
+ buttonAction = this._dtdSettings.get_enum('click-action'); |
|
+ } |
|
+ |
|
+ // We check if the app is running, and that the # of windows is > 0 in |
|
+ // case we use workspace isolation. |
|
+ let windows = this.getInterestingWindows(); |
|
+ let appIsRunning = this.app.state == Shell.AppState.RUNNING |
|
+ && windows.length > 0; |
|
+ |
|
+ // Some action modes (e.g. MINIMIZE_OR_OVERVIEW) require overview to remain open |
|
+ // This variable keeps track of this |
|
+ let shouldHideOverview = true; |
|
+ |
|
+ // We customize the action only when the application is already running |
|
+ if (appIsRunning) { |
|
+ switch (buttonAction) { |
|
+ case clickAction.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 (this.app == focusedApp || 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 click_count = 0; |
|
+ if (Clutter.EventType.CLUTTER_BUTTON_PRESS) |
|
+ click_count = event.get_click_count(); |
|
+ let all_windows = (button == 1 && ! modifiers) || click_count > 1; |
|
+ this._minimizeWindow(all_windows); |
|
+ } |
|
+ else |
|
+ this._activateAllWindows(); |
|
+ } |
|
+ else { |
|
+ let w = windows[0]; |
|
+ Main.activateWindow(w); |
|
+ } |
|
+ break; |
|
+ |
|
+ case clickAction.MINIMIZE_OR_OVERVIEW: |
|
+ // When a single window is present, toggle minimization |
|
+ // If only one windows is present toggle minimization, but only when trigggered with the |
|
+ // simple click action (no modifiers, no middle click). |
|
+ if (windows.length == 1 && !modifiers && button == 1) { |
|
+ let w = windows[0]; |
|
+ if (this.app == focusedApp) { |
|
+ // Window is raised, minimize it |
|
+ this._minimizeWindow(w); |
|
+ } else { |
|
+ // Window is minimized, raise it |
|
+ Main.activateWindow(w); |
|
+ } |
|
+ // Launch overview when multiple windows are present |
|
+ // TODO: only show current app windows when gnome shell API will allow it |
|
+ } else { |
|
+ shouldHideOverview = false; |
|
+ Main.overview.toggle(); |
|
+ } |
|
+ break; |
|
+ |
|
+ case clickAction.CYCLE_WINDOWS: |
|
+ if (!Main.overview._shown){ |
|
+ if (this.app == focusedApp) |
|
+ this._cycleThroughWindows(); |
|
+ else { |
|
+ // Activate the first window |
|
+ let w = windows[0]; |
|
+ Main.activateWindow(w); |
|
+ } |
|
+ } |
|
+ else |
|
+ this.app.activate(); |
|
+ break; |
|
+ |
|
+ case clickAction.LAUNCH: |
|
+ this.launchNewWindow(); |
|
+ break; |
|
+ |
|
+ case clickAction.PREVIEWS: |
|
+ if (!Main.overview._shown) { |
|
+ // If only one windows is present just switch to it, but only when trigggered with the |
|
+ // simple click action (no modifiers, no middle click). |
|
+ if (windows.length == 1 && !modifiers && button == 1) { |
|
+ let w = windows[0]; |
|
+ Main.activateWindow(w); |
|
+ } else |
|
+ this._windowPreviews(); |
|
+ } |
|
+ else { |
|
+ this.app.activate(); |
|
+ } |
|
+ break; |
|
+ |
|
+ case clickAction.MINIMIZE_OR_PREVIEWS: |
|
+ // When a single window is present, toggle minimization |
|
+ // If only one windows is present toggle minimization, but only when trigggered with the |
|
+ // simple click action (no modifiers, no middle click). |
|
+ if (!Main.overview._shown){ |
|
+ if (windows.length == 1 && !modifiers && button == 1) { |
|
+ let w = windows[0]; |
|
+ if (this.app == focusedApp) { |
|
+ // Window is raised, minimize it |
|
+ this._minimizeWindow(w); |
|
+ } else { |
|
+ // Window is minimized, raise it |
|
+ Main.activateWindow(w); |
|
+ } |
|
+ } else { |
|
+ // Launch previews when multiple windows are present |
|
+ this._windowPreviews(); |
|
+ } |
|
+ } else { |
|
+ this.app.activate(); |
|
+ } |
|
+ break; |
|
+ |
|
+ case clickAction.QUIT: |
|
+ this.closeAllWindows(); |
|
+ break; |
|
+ |
|
+ case clickAction.SKIP: |
|
+ let w = windows[0]; |
|
+ Main.activateWindow(w); |
|
+ break; |
|
+ } |
|
+ } |
|
+ else { |
|
+ this.launchNewWindow(); |
|
+ } |
|
+ |
|
+ // Hide overview except when action mode requires it |
|
+ if(shouldHideOverview) { |
|
+ Main.overview.hide(); |
|
+ } |
|
+ }, |
|
+ |
|
+ shouldShowTooltip: function() { |
|
+ return this.actor.hover && (!this._menu || !this._menu.isOpen) && |
|
+ (!this._previewMenu || !this._previewMenu.isOpen); |
|
+ }, |
|
+ |
|
+ _windowPreviews: function() { |
|
+ if (!this._previewMenu) { |
|
+ this._previewMenuManager = new PopupMenu.PopupMenuManager(this); |
|
+ |
|
+ this._previewMenu = new WindowPreview.WindowPreviewMenu(this, this._dtdSettings); |
|
+ |
|
+ this._previewMenuManager.addMenu(this._previewMenu); |
|
+ |
|
+ this._previewMenu.connect('open-state-changed', Lang.bind(this, function(menu, isPoppedUp) { |
|
+ if (!isPoppedUp) |
|
+ this._onMenuPoppedDown(); |
|
+ })); |
|
+ let id = Main.overview.connect('hiding', Lang.bind(this, function() { |
|
+ this._previewMenu.close(); |
|
+ })); |
|
+ this._previewMenu.actor.connect('destroy', function() { |
|
+ Main.overview.disconnect(id); |
|
+ }); |
|
+ |
|
+ } |
|
+ |
|
+ if (this._previewMenu.isOpen) |
|
+ this._previewMenu.close(); |
|
+ else |
|
+ this._previewMenu.popup(); |
|
+ |
|
+ return false; |
|
+ }, |
|
+ |
|
+ // Try to do the right thing when attempting to launch a new window of an app. In |
|
+ // particular, if the application doens't allow to launch a new window, activate |
|
+ // the existing window instead. |
|
+ launchNewWindow: function(p) { |
|
+ let appInfo = this.app.get_app_info(); |
|
+ let actions = appInfo.list_actions(); |
|
+ if (this.app.can_open_new_window()) { |
|
+ this.animateLaunch(); |
|
+ // This is used as a workaround for a bug resulting in no new windows being opened |
|
+ // for certain running applications when calling open_new_window(). |
|
+ // |
|
+ // https://bugzilla.gnome.org/show_bug.cgi?id=756844 |
|
+ // |
|
+ // Similar to what done when generating the popupMenu entries, if the application provides |
|
+ // a "New Window" action, use it instead of directly requesting a new window with |
|
+ // open_new_window(), which fails for certain application, notably Nautilus. |
|
+ if (actions.indexOf('new-window') == -1) { |
|
+ this.app.open_new_window(-1); |
|
+ } |
|
+ else { |
|
+ let i = actions.indexOf('new-window'); |
|
+ if (i !== -1) |
|
+ this.app.launch_action(actions[i], global.get_current_time(), -1); |
|
+ } |
|
+ } |
|
+ else { |
|
+ // Try to manually activate the first window. Otherwise, when the app is activated by |
|
+ // switching to a different workspace, a launch spinning icon is shown and disappers only |
|
+ // after a timeout. |
|
+ let windows = this.app.get_windows(); |
|
+ if (windows.length > 0) |
|
+ Main.activateWindow(windows[0]) |
|
+ else |
|
+ this.app.activate(); |
|
+ } |
|
+ }, |
|
+ |
|
+ _numberOverlay: function() { |
|
+ // Add label for a Hot-Key visual aid |
|
+ this._numberOverlayLabel = new St.Label(); |
|
+ this._numberOverlayBin = new St.Bin({ |
|
+ child: this._numberOverlayLabel, |
|
+ x_align: St.Align.START, y_align: St.Align.START, |
|
+ x_expand: true, y_expand: true |
|
+ }); |
|
+ this._numberOverlayLabel.add_style_class_name('number-overlay'); |
|
+ this._numberOverlayOrder = -1; |
|
+ this._numberOverlayBin.hide(); |
|
+ |
|
+ this._iconContainer.add_child(this._numberOverlayBin); |
|
+ |
|
+ }, |
|
+ |
|
+ updateNumberOverlay: function() { |
|
+ // 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. |
|
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ // 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._iconContainer.get_preferred_width(-1); |
|
+ let font_size = Math.round(Math.max(12, 0.3*natWidth) / scaleFactor); |
|
+ let size = Math.round(font_size*1.2); |
|
+ this._numberOverlayLabel.set_style( |
|
+ 'font-size: ' + font_size + 'px;' + |
|
+ 'border-radius: ' + this.icon.iconSize + 'px;' + |
|
+ 'width: ' + size + 'px; height: ' + size +'px;' |
|
+ ); |
|
+ }, |
|
+ |
|
+ setNumberOverlay: function(number) { |
|
+ this._numberOverlayOrder = number; |
|
+ this._numberOverlayLabel.set_text(number.toString()); |
|
+ }, |
|
+ |
|
+ toggleNumberOverlay: function(activate) { |
|
+ if (activate && this._numberOverlayOrder > -1) { |
|
+ this.updateNumberOverlay(); |
|
+ this._numberOverlayBin.show(); |
|
+ } |
|
+ else |
|
+ this._numberOverlayBin.hide(); |
|
+ }, |
|
+ |
|
+ _minimizeWindow: function(param) { |
|
+ // Param true make all app windows minimize |
|
+ let windows = this.getInterestingWindows(); |
|
+ let current_workspace = global.screen.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. |
|
+ _activateAllWindows: function() { |
|
+ // First activate first window so workspace is switched if needed. |
|
+ // We don't do this if isolation is on! |
|
+ if (!this._dtdSettings.get_boolean('isolate-workspaces') && |
|
+ !this._dtdSettings.get_boolean('isolate-monitors')) |
|
+ this.app.activate(); |
|
+ |
|
+ // then activate all other app windows in the current workspace |
|
+ let windows = this.getInterestingWindows(); |
|
+ let activeWorkspace = global.screen.get_active_workspace_index(); |
|
+ |
|
+ if (windows.length <= 0) |
|
+ return; |
|
+ |
|
+ let activatedWindows = 0; |
|
+ |
|
+ for (let i = windows.length - 1; i >= 0; i--) { |
|
+ if (windows[i].get_workspace().index() == activeWorkspace) { |
|
+ Main.activateWindow(windows[i]); |
|
+ activatedWindows++; |
|
+ } |
|
+ } |
|
+ }, |
|
+ |
|
+ //This closes all windows of the app. |
|
+ closeAllWindows: function() { |
|
+ let windows = this.getInterestingWindows(); |
|
+ for (let i = 0; i < windows.length; i++) |
|
+ windows[i].delete(global.get_current_time()); |
|
+ }, |
|
+ |
|
+ _cycleThroughWindows: function(reversed) { |
|
+ // 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 = this.getInterestingWindows(); |
|
+ |
|
+ if (app_windows.length <1) |
|
+ return |
|
+ |
|
+ if (recentlyClickedAppLoopId > 0) |
|
+ Mainloop.source_remove(recentlyClickedAppLoopId); |
|
+ recentlyClickedAppLoopId = Mainloop.timeout_add(MEMORY_TIME, this._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. |
|
+ let monitorIsolation = this._dtdSettings.get_boolean('isolate-monitors'); |
|
+ if (!recentlyClickedApp || |
|
+ recentlyClickedApp.get_id() != this.app.get_id() || |
|
+ recentlyClickedAppWindows.length != app_windows.length || |
|
+ (recentlyClickedAppMonitor != this.monitorIndex && monitorIsolation)) { |
|
+ recentlyClickedApp = this.app; |
|
+ recentlyClickedAppWindows = app_windows; |
|
+ recentlyClickedAppMonitor = this.monitorIndex; |
|
+ recentlyClickedAppIndex = 0; |
|
+ } |
|
+ |
|
+ if (reversed) { |
|
+ recentlyClickedAppIndex--; |
|
+ if (recentlyClickedAppIndex < 0) recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1; |
|
+ } else { |
|
+ recentlyClickedAppIndex++; |
|
+ } |
|
+ let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length; |
|
+ let window = recentlyClickedAppWindows[index]; |
|
+ |
|
+ Main.activateWindow(window); |
|
+ }, |
|
+ |
|
+ _resetRecentlyClickedApp: function() { |
|
+ if (recentlyClickedAppLoopId > 0) |
|
+ Mainloop.source_remove(recentlyClickedAppLoopId); |
|
+ recentlyClickedAppLoopId=0; |
|
+ recentlyClickedApp =null; |
|
+ recentlyClickedAppWindows = null; |
|
+ recentlyClickedAppIndex = 0; |
|
+ recentlyClickedAppMonitor = -1; |
|
+ |
|
+ return false; |
|
+ }, |
|
+ |
|
+ // Filter out unnecessary windows, for instance |
|
+ // nautilus desktop window. |
|
+ getInterestingWindows: function() { |
|
+ return getInterestingWindows(this.app, this._dtdSettings, this.monitorIndex); |
|
+ } |
|
+}); |
|
+/** |
|
+ * Extend AppIconMenu |
|
+ * |
|
+ * - Pass settings to the constructor |
|
+ * - set popup arrow side based on dash orientation |
|
+ * - Add close windows option based on quitfromdash extension |
|
+ * (https://github.com/deuill/shell-extension-quitfromdash) |
|
+ * - Add open windows thumbnails instead of list |
|
+ * - update menu when application windows change |
|
+ */ |
|
+const MyAppIconMenu = new Lang.Class({ |
|
+ Name: 'DashToDock.MyAppIconMenu', |
|
+ Extends: AppDisplay.AppIconMenu, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ let side = Utils.getPosition(settings); |
|
+ |
|
+ // Damm it, there has to be a proper way of doing this... |
|
+ // As I can't call the parent parent constructor (?) passing the side |
|
+ // parameter, I overwite what I need later |
|
+ this.parent(source); |
|
+ |
|
+ // Change the initialized side where required. |
|
+ this._arrowSide = side; |
|
+ this._boxPointer._arrowSide = side; |
|
+ this._boxPointer._userArrowSide = side; |
|
+ |
|
+ this._dtdSettings = settings; |
|
+ }, |
|
+ |
|
+ _redisplay: function() { |
|
+ this.removeAll(); |
|
+ |
|
+ if (this._dtdSettings.get_boolean('show-windows-preview')) { |
|
+ // Display the app windows menu items and the separator between windows |
|
+ // of the current desktop and other windows. |
|
+ |
|
+ this._allWindowsMenuItem = new PopupMenu.PopupSubMenuMenuItem(__('All Windows'), false); |
|
+ this._allWindowsMenuItem.actor.hide(); |
|
+ this.addMenuItem(this._allWindowsMenuItem); |
|
+ |
|
+ if (!this._source.app.is_window_backed()) { |
|
+ this._appendSeparator(); |
|
+ |
|
+ let appInfo = this._source.app.get_app_info(); |
|
+ let actions = appInfo.list_actions(); |
|
+ if (this._source.app.can_open_new_window() && |
|
+ actions.indexOf('new-window') == -1) { |
|
+ this._newWindowMenuItem = this._appendMenuItem(_("New Window")); |
|
+ this._newWindowMenuItem.connect('activate', Lang.bind(this, function() { |
|
+ if (this._source.app.state == Shell.AppState.STOPPED) |
|
+ this._source.animateLaunch(); |
|
+ |
|
+ this._source.app.open_new_window(-1); |
|
+ this.emit('activate-window', null); |
|
+ })); |
|
+ this._appendSeparator(); |
|
+ } |
|
+ |
|
+ |
|
+ if (AppDisplay.discreteGpuAvailable && |
|
+ this._source.app.state == Shell.AppState.STOPPED && |
|
+ actions.indexOf('activate-discrete-gpu') == -1) { |
|
+ this._onDiscreteGpuMenuItem = this._appendMenuItem(_("Launch using Dedicated Graphics Card")); |
|
+ this._onDiscreteGpuMenuItem.connect('activate', Lang.bind(this, function() { |
|
+ if (this._source.app.state == Shell.AppState.STOPPED) |
|
+ this._source.animateLaunch(); |
|
+ |
|
+ this._source.app.launch(0, -1, true); |
|
+ this.emit('activate-window', null); |
|
+ })); |
|
+ } |
|
+ |
|
+ for (let i = 0; i < actions.length; i++) { |
|
+ let action = actions[i]; |
|
+ let item = this._appendMenuItem(appInfo.get_action_name(action)); |
|
+ item.connect('activate', Lang.bind(this, function(emitter, event) { |
|
+ this._source.app.launch_action(action, event.get_time(), -1); |
|
+ this.emit('activate-window', null); |
|
+ })); |
|
+ } |
|
+ |
|
+ let canFavorite = global.settings.is_writable('favorite-apps'); |
|
+ |
|
+ if (canFavorite) { |
|
+ this._appendSeparator(); |
|
+ |
|
+ let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id()); |
|
+ |
|
+ if (isFavorite) { |
|
+ let item = this._appendMenuItem(_("Remove from Favorites")); |
|
+ item.connect('activate', Lang.bind(this, function() { |
|
+ let favs = AppFavorites.getAppFavorites(); |
|
+ favs.removeFavorite(this._source.app.get_id()); |
|
+ })); |
|
+ } else { |
|
+ let item = this._appendMenuItem(_("Add to Favorites")); |
|
+ item.connect('activate', Lang.bind(this, function() { |
|
+ let favs = AppFavorites.getAppFavorites(); |
|
+ favs.addFavorite(this._source.app.get_id()); |
|
+ })); |
|
+ } |
|
+ } |
|
+ |
|
+ if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) { |
|
+ this._appendSeparator(); |
|
+ let item = this._appendMenuItem(_("Show Details")); |
|
+ item.connect('activate', Lang.bind(this, function() { |
|
+ let id = this._source.app.get_id(); |
|
+ let args = GLib.Variant.new('(ss)', [id, '']); |
|
+ Gio.DBus.get(Gio.BusType.SESSION, null, |
|
+ function(o, res) { |
|
+ let bus = Gio.DBus.get_finish(res); |
|
+ bus.call('org.gnome.Software', |
|
+ '/org/gnome/Software', |
|
+ 'org.gtk.Actions', 'Activate', |
|
+ GLib.Variant.new('(sava{sv})', |
|
+ ['details', [args], null]), |
|
+ null, 0, -1, null, null); |
|
+ Main.overview.hide(); |
|
+ }); |
|
+ })); |
|
+ } |
|
+ } |
|
+ |
|
+ } else { |
|
+ this.parent(); |
|
+ } |
|
+ |
|
+ // quit menu |
|
+ this._appendSeparator(); |
|
+ this._quitfromDashMenuItem = this._appendMenuItem(_("Quit")); |
|
+ this._quitfromDashMenuItem.connect('activate', Lang.bind(this, function() { |
|
+ this._source.closeAllWindows(); |
|
+ })); |
|
+ |
|
+ this.update(); |
|
+ }, |
|
+ |
|
+ // update menu content when application windows change. This is desirable as actions |
|
+ // acting on windows (closing) are performed while the menu is shown. |
|
+ update: function() { |
|
+ |
|
+ if(this._dtdSettings.get_boolean('show-windows-preview')){ |
|
+ |
|
+ let windows = this._source.getInterestingWindows(); |
|
+ |
|
+ // update, show or hide the quit menu |
|
+ if ( windows.length > 0) { |
|
+ let quitFromDashMenuText = ""; |
|
+ if (windows.length == 1) |
|
+ this._quitfromDashMenuItem.label.set_text(_("Quit")); |
|
+ else |
|
+ this._quitfromDashMenuItem.label.set_text(_("Quit") + ' ' + windows.length + ' ' + _("Windows")); |
|
+ |
|
+ this._quitfromDashMenuItem.actor.show(); |
|
+ |
|
+ } else { |
|
+ this._quitfromDashMenuItem.actor.hide(); |
|
+ } |
|
+ |
|
+ // update, show, or hide the allWindows menu |
|
+ // Check if there are new windows not already displayed. In such case, repopulate the allWindows |
|
+ // menu. Windows removal is already handled by each preview being connected to the destroy signal |
|
+ let old_windows = this._allWindowsMenuItem.menu._getMenuItems().map(function(item){ |
|
+ return item._window; |
|
+ }); |
|
+ |
|
+ let new_windows = windows.filter(function(w) {return old_windows.indexOf(w) < 0;}); |
|
+ if (new_windows.length > 0) { |
|
+ this._populateAllWindowMenu(windows); |
|
+ |
|
+ // Try to set the width to that of the submenu. |
|
+ // TODO: can't get the actual size, getting a bit less. |
|
+ // Temporary workaround: add 15px to compensate |
|
+ this._allWindowsMenuItem.actor.width = this._allWindowsMenuItem.menu.actor.width + 15; |
|
+ |
|
+ } |
|
+ |
|
+ // The menu is created hidden and never hidded after being shown. Instead, a singlal |
|
+ // connected to its items destroy will set is insensitive if no more windows preview are shown. |
|
+ if (windows.length > 0){ |
|
+ this._allWindowsMenuItem.actor.show(); |
|
+ this._allWindowsMenuItem.setSensitive(true); |
|
+ } |
|
+ |
|
+ // Update separators |
|
+ this._getMenuItems().forEach(Lang.bind(this, this._updateSeparatorVisibility)); |
|
+ } |
|
+ |
|
+ |
|
+ }, |
|
+ |
|
+ _populateAllWindowMenu: function(windows) { |
|
+ |
|
+ this._allWindowsMenuItem.menu.removeAll(); |
|
+ |
|
+ if (windows.length > 0) { |
|
+ |
|
+ let activeWorkspace = global.screen.get_active_workspace(); |
|
+ let separatorShown = windows[0].get_workspace() != activeWorkspace; |
|
+ |
|
+ for (let i = 0; i < windows.length; i++) { |
|
+ let window = windows[i]; |
|
+ if (!separatorShown && window.get_workspace() != activeWorkspace) { |
|
+ this._allWindowsMenuItem.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); |
|
+ separatorShown = true; |
|
+ } |
|
+ |
|
+ let item = new WindowPreview.WindowPreviewMenuItem(window); |
|
+ this._allWindowsMenuItem.menu.addMenuItem(item); |
|
+ item.connect('activate', Lang.bind(this, function() { |
|
+ this.emit('activate-window', window); |
|
+ })); |
|
+ |
|
+ // This is to achieve a more gracefull transition when the last windows is closed. |
|
+ item.connect('destroy', Lang.bind(this, function() { |
|
+ if(this._allWindowsMenuItem.menu._getMenuItems().length == 1) // It's still counting the item just going to be destroyed |
|
+ this._allWindowsMenuItem.setSensitive(false); |
|
+ })); |
|
+ } |
|
+ } |
|
+ }, |
|
+}); |
|
+Signals.addSignalMethods(MyAppIconMenu.prototype); |
|
+ |
|
+// Filter out unnecessary windows, for instance |
|
+// nautilus desktop window. |
|
+function getInterestingWindows(app, settings, monitorIndex) { |
|
+ let windows = app.get_windows().filter(function(w) { |
|
+ return !w.skip_taskbar; |
|
+ }); |
|
+ |
|
+ // When using workspace isolation, we filter out windows |
|
+ // that are not in the current workspace |
|
+ if (settings.get_boolean('isolate-workspaces')) |
|
+ windows = windows.filter(function(w) { |
|
+ return w.get_workspace().index() == global.screen.get_active_workspace_index(); |
|
+ }); |
|
+ |
|
+ if (settings.get_boolean('isolate-monitors')) |
|
+ windows = windows.filter(function(w) { |
|
+ return w.get_monitor() == monitorIndex; |
|
+ }); |
|
+ |
|
+ return windows; |
|
+} |
|
+ |
|
+/** |
|
+ * 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. |
|
+ * |
|
+ */ |
|
+ |
|
+ var ShowAppsIconWrapper = new Lang.Class({ |
|
+ Name: 'DashToDock.ShowAppsIconWrapper', |
|
+ |
|
+ _init: function(settings) { |
|
+ this._dtdSettings = settings; |
|
+ 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; |
|
+ |
|
+ // Re-use appIcon methods |
|
+ this._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout; |
|
+ this._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout; |
|
+ this._onButtonPress = AppDisplay.AppIcon.prototype._onButtonPress; |
|
+ this._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu; |
|
+ this._onLeaveEvent = AppDisplay.AppIcon.prototype._onLeaveEvent; |
|
+ this._onTouchEvent = AppDisplay.AppIcon.prototype._onTouchEvent; |
|
+ this._onMenuPoppedDown = AppDisplay.AppIcon.prototype._onMenuPoppedDown; |
|
+ |
|
+ // No action on clicked (showing of the appsview is controlled elsewhere) |
|
+ this._onClicked = Lang.bind(this, function(actor, button) { |
|
+ this._removeMenuTimeout(); |
|
+ }); |
|
+ |
|
+ this.actor.connect('leave-event', Lang.bind(this, this._onLeaveEvent)); |
|
+ this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); |
|
+ this.actor.connect('touch-event', Lang.bind(this, this._onTouchEvent)); |
|
+ this.actor.connect('clicked', Lang.bind(this, this._onClicked)); |
|
+ this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu)); |
|
+ |
|
+ this._menu = null; |
|
+ this._menuManager = new PopupMenu.PopupMenuManager(this); |
|
+ this._menuTimeoutId = 0; |
|
+ |
|
+ this.showLabel = itemShowLabel; |
|
+ }, |
|
+ |
|
+ popupMenu: function() { |
|
+ this._removeMenuTimeout(); |
|
+ this.actor.fake_release(); |
|
+ |
|
+ if (!this._menu) { |
|
+ this._menu = new MyShowAppsIconMenu(this, this._dtdSettings); |
|
+ this._menu.connect('open-state-changed', Lang.bind(this, function(menu, isPoppedUp) { |
|
+ if (!isPoppedUp) |
|
+ this._onMenuPoppedDown(); |
|
+ })); |
|
+ let id = Main.overview.connect('hiding', Lang.bind(this, function() { |
|
+ this._menu.close(); |
|
+ })); |
|
+ this._menu.actor.connect('destroy', function() { |
|
+ Main.overview.disconnect(id); |
|
+ }); |
|
+ this._menuManager.addMenu(this._menu); |
|
+ } |
|
+ |
|
+ //this.emit('menu-state-changed', true); |
|
+ |
|
+ this.actor.set_hover(true); |
|
+ this._menu.popup(); |
|
+ this._menuManager.ignoreRelease(); |
|
+ this.emit('sync-tooltip'); |
|
+ |
|
+ return false; |
|
+ } |
|
+}); |
|
+Signals.addSignalMethods(ShowAppsIconWrapper.prototype); |
|
+ |
|
+ |
|
+/** |
|
+ * A menu for the showAppsIcon |
|
+ */ |
|
+const MyShowAppsIconMenu = new Lang.Class({ |
|
+ Name: 'DashToDock.ShowAppsIconMenu', |
|
+ Extends: MyAppIconMenu, |
|
+ |
|
+ _redisplay: function() { |
|
+ this.removeAll(); |
|
+ |
|
+ /* Translators: %s is "Settings", which is automatically translated. You |
|
+ can also translate the full message if this fits better your language. */ |
|
+ let name = __('Dash to Dock %s').format(_('Settings')) |
|
+ let item = this._appendMenuItem(name); |
|
+ |
|
+ item.connect('activate', function () { |
|
+ Util.spawn(["gnome-shell-extension-prefs", Me.metadata.uuid]); |
|
+ }); |
|
+ } |
|
+}); |
|
+ |
|
+/** |
|
+ * This function is used for both extendShowAppsIcon and extendDashItemContainer |
|
+ */ |
|
+function itemShowLabel() { |
|
+ // Check if the label is still present at all. When switching workpaces, the |
|
+ // item might have been destroyed in between. |
|
+ if (!this._labelText || this.label.get_stage() == null) |
|
+ 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 x, y, xOffset, yOffset; |
|
+ |
|
+ let position = Utils.getPosition(this._dtdSettings); |
|
+ this._isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM)); |
|
+ let labelOffset = node.get_length('-x-offset'); |
|
+ |
|
+ switch (position) { |
|
+ case St.Side.LEFT: |
|
+ yOffset = Math.floor((itemHeight - labelHeight) / 2); |
|
+ y = stageY + yOffset; |
|
+ xOffset = labelOffset; |
|
+ x = stageX + this.get_width() + xOffset; |
|
+ break; |
|
+ case St.Side.RIGHT: |
|
+ yOffset = Math.floor((itemHeight - labelHeight) / 2); |
|
+ y = stageY + yOffset; |
|
+ xOffset = labelOffset; |
|
+ x = Math.round(stageX) - labelWidth - xOffset; |
|
+ break; |
|
+ case St.Side.TOP: |
|
+ y = stageY + labelOffset + itemHeight; |
|
+ xOffset = Math.floor((itemWidth - labelWidth) / 2); |
|
+ x = stageX + xOffset; |
|
+ break; |
|
+ case St.Side.BOTTOM: |
|
+ yOffset = labelOffset; |
|
+ y = stageY - labelHeight - yOffset; |
|
+ xOffset = Math.floor((itemWidth - labelWidth) / 2); |
|
+ x = stageX + xOffset; |
|
+ break; |
|
+ } |
|
+ |
|
+ // keep the label inside the screen border |
|
+ // Only needed fot the x coordinate. |
|
+ |
|
+ // Leave a few pixel gap |
|
+ let gap = 5; |
|
+ 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(x, y); |
|
+ Tweener.addTween(this.label, { |
|
+ opacity: 255, |
|
+ time: DASH_ITEM_LABEL_SHOW_TIME, |
|
+ transition: 'easeOutQuad', |
|
+ }); |
|
+} |
|
diff --git a/extensions/dash-to-dock/convenience.js b/extensions/dash-to-dock/convenience.js |
|
new file mode 100644 |
|
index 0000000..bc50419 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/convenience.js |
|
@@ -0,0 +1,74 @@ |
|
+/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */ |
|
+ |
|
+/* |
|
+ * Part of this file comes from gnome-shell-extensions: |
|
+ * https://gitlab.gnome.org/GNOME/gnome-shell-extensions/ |
|
+ */ |
|
+ |
|
+const Gettext = imports.gettext; |
|
+const Gio = imports.gi.Gio; |
|
+ |
|
+const Config = imports.misc.config; |
|
+const ExtensionUtils = imports.misc.extensionUtils; |
|
+ |
|
+/** |
|
+ * initTranslations: |
|
+ * @domain: (optional): the gettext domain to use |
|
+ * |
|
+ * Initialize Gettext to load translations from extensionsdir/locale. |
|
+ * If @domain is not provided, it will be taken from metadata['gettext-domain'] |
|
+ */ |
|
+function initTranslations(domain) { |
|
+ let extension = ExtensionUtils.getCurrentExtension(); |
|
+ |
|
+ domain = domain || extension.metadata['gettext-domain']; |
|
+ |
|
+ // Check if this extension was built with "make zip-file", and thus |
|
+ // has the locale files in a subfolder |
|
+ // otherwise assume that extension has been installed in the |
|
+ // same prefix as gnome-shell |
|
+ let localeDir = extension.dir.get_child('locale'); |
|
+ if (localeDir.query_exists(null)) |
|
+ Gettext.bindtextdomain(domain, localeDir.get_path()); |
|
+ else |
|
+ Gettext.bindtextdomain(domain, Config.LOCALEDIR); |
|
+} |
|
+ |
|
+/** |
|
+ * getSettings: |
|
+ * @schema: (optional): the GSettings schema id |
|
+ * |
|
+ * Builds and return a GSettings schema for @schema, using schema files |
|
+ * in extensionsdir/schemas. If @schema is not provided, it is taken from |
|
+ * metadata['settings-schema']. |
|
+ */ |
|
+function getSettings(schema) { |
|
+ let extension = ExtensionUtils.getCurrentExtension(); |
|
+ |
|
+ schema = schema || extension.metadata['settings-schema']; |
|
+ |
|
+ const GioSSS = Gio.SettingsSchemaSource; |
|
+ |
|
+ // Check if this extension was built with "make zip-file", and thus |
|
+ // has the schema files in a subfolder |
|
+ // otherwise assume that extension has been installed in the |
|
+ // same prefix as gnome-shell (and therefore schemas are available |
|
+ // in the standard folders) |
|
+ let schemaDir = extension.dir.get_child('schemas'); |
|
+ let schemaSource; |
|
+ if (schemaDir.query_exists(null)) |
|
+ schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), |
|
+ GioSSS.get_default(), |
|
+ false); |
|
+ else |
|
+ schemaSource = GioSSS.get_default(); |
|
+ |
|
+ let schemaObj = schemaSource.lookup(schema, true); |
|
+ if (!schemaObj) |
|
+ throw new Error('Schema ' + schema + ' could not be found for extension ' |
|
+ + extension.metadata.uuid + '. Please check your installation.'); |
|
+ |
|
+ return new Gio.Settings({ |
|
+ settings_schema: schemaObj |
|
+ }); |
|
+} |
|
diff --git a/extensions/dash-to-dock/dash.js b/extensions/dash-to-dock/dash.js |
|
new file mode 100644 |
|
index 0000000..4cf5aa2 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/dash.js |
|
@@ -0,0 +1,1175 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const Gio = imports.gi.Gio; |
|
+const GLib = imports.gi.GLib; |
|
+const Gtk = imports.gi.Gtk; |
|
+const Signals = imports.signals; |
|
+const Lang = imports.lang; |
|
+const Meta = imports.gi.Meta; |
|
+const Shell = imports.gi.Shell; |
|
+const St = imports.gi.St; |
|
+const Mainloop = imports.mainloop; |
|
+ |
|
+const AppDisplay = imports.ui.appDisplay; |
|
+const AppFavorites = imports.ui.appFavorites; |
|
+const Dash = imports.ui.dash; |
|
+const DND = imports.ui.dnd; |
|
+const IconGrid = imports.ui.iconGrid; |
|
+const Main = imports.ui.main; |
|
+const PopupMenu = imports.ui.popupMenu; |
|
+const Tweener = imports.ui.tweener; |
|
+const Util = imports.misc.util; |
|
+const Workspace = imports.ui.workspace; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Utils = Me.imports.utils; |
|
+const AppIcons = Me.imports.appIcons; |
|
+ |
|
+let DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME; |
|
+let DASH_ITEM_LABEL_HIDE_TIME = Dash.DASH_ITEM_LABEL_HIDE_TIME; |
|
+let DASH_ITEM_HOVER_TIMEOUT = Dash.DASH_ITEM_HOVER_TIMEOUT; |
|
+ |
|
+/** |
|
+ * Extend DashItemContainer |
|
+ * |
|
+ * - Pass settings to the constructor |
|
+ * - set label position based on dash 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. |
|
+ */ |
|
+function extendDashItemContainer(dashItemContainer, settings) { |
|
+ dashItemContainer._dtdSettings = settings; |
|
+ dashItemContainer.showLabel = AppIcons.itemShowLabel; |
|
+} |
|
+ |
|
+/** |
|
+ * This class is a fork of the upstream DashActor class (ui.dash.js) |
|
+ * |
|
+ * Summary of changes: |
|
+ * - passed settings to class as parameter |
|
+ * - modified chldBox calculations for when 'show-apps-at-top' option is checked |
|
+ * - handle horizontal dash |
|
+ */ |
|
+const MyDashActor = new Lang.Class({ |
|
+ Name: 'DashToDock.MyDashActor', |
|
+ |
|
+ _init: function(settings) { |
|
+ // a prefix is required to avoid conflicting with the parent class variable |
|
+ this._dtdSettings = settings; |
|
+ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL); |
|
+ |
|
+ this._position = Utils.getPosition(settings); |
|
+ this._isHorizontal = ((this._position == St.Side.TOP) || |
|
+ (this._position == St.Side.BOTTOM)); |
|
+ |
|
+ let layout = new Clutter.BoxLayout({ |
|
+ orientation: this._isHorizontal ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL |
|
+ }); |
|
+ |
|
+ this.actor = new Shell.GenericContainer({ |
|
+ name: 'dash', |
|
+ layout_manager: layout, |
|
+ clip_to_allocation: true |
|
+ }); |
|
+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); |
|
+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); |
|
+ this.actor.connect('allocate', Lang.bind(this, this._allocate)); |
|
+ |
|
+ this.actor._delegate = this; |
|
+ }, |
|
+ |
|
+ _allocate: function(actor, box, flags) { |
|
+ let contentBox = box; |
|
+ let availWidth = contentBox.x2 - contentBox.x1; |
|
+ let availHeight = contentBox.y2 - contentBox.y1; |
|
+ |
|
+ let [appIcons, showAppsButton] = actor.get_children(); |
|
+ let [showAppsMinHeight, showAppsNatHeight] = showAppsButton.get_preferred_height(availWidth); |
|
+ let [showAppsMinWidth, showAppsNatWidth] = showAppsButton.get_preferred_width(availHeight); |
|
+ |
|
+ let offset_x = this._isHorizontal?showAppsNatWidth:0; |
|
+ let offset_y = this._isHorizontal?0:showAppsNatHeight; |
|
+ |
|
+ let childBox = new Clutter.ActorBox(); |
|
+ if ((this._dtdSettings.get_boolean('show-apps-at-top') && !this._isHorizontal) |
|
+ || (this._dtdSettings.get_boolean('show-apps-at-top') && !this._rtl) |
|
+ || (!this._dtdSettings.get_boolean('show-apps-at-top') && this._isHorizontal && this._rtl)) { |
|
+ childBox.x1 = contentBox.x1 + offset_x; |
|
+ childBox.y1 = contentBox.y1 + offset_y; |
|
+ childBox.x2 = contentBox.x2; |
|
+ childBox.y2 = contentBox.y2; |
|
+ appIcons.allocate(childBox, flags); |
|
+ |
|
+ childBox.y1 = contentBox.y1; |
|
+ childBox.x1 = contentBox.x1; |
|
+ childBox.x2 = contentBox.x1 + showAppsNatWidth; |
|
+ childBox.y2 = contentBox.y1 + showAppsNatHeight; |
|
+ showAppsButton.allocate(childBox, flags); |
|
+ } |
|
+ else { |
|
+ childBox.x1 = contentBox.x1; |
|
+ childBox.y1 = contentBox.y1; |
|
+ childBox.x2 = contentBox.x2 - offset_x; |
|
+ childBox.y2 = contentBox.y2 - offset_y; |
|
+ appIcons.allocate(childBox, flags); |
|
+ |
|
+ childBox.x2 = contentBox.x2; |
|
+ childBox.y2 = contentBox.y2; |
|
+ childBox.x1 = contentBox.x2 - showAppsNatWidth; |
|
+ childBox.y1 = contentBox.y2 - showAppsNatHeight; |
|
+ showAppsButton.allocate(childBox, flags); |
|
+ } |
|
+ }, |
|
+ |
|
+ _getPreferredWidth: function(actor, forHeight, alloc) { |
|
+ // We want to request the natural height of all our children |
|
+ // as our natural height, so we chain up to StWidget (which |
|
+ // then calls BoxLayout), but we only request the showApps |
|
+ // button as the minimum size |
|
+ |
|
+ let [, natWidth] = this.actor.layout_manager.get_preferred_width(this.actor, forHeight); |
|
+ |
|
+ let themeNode = this.actor.get_theme_node(); |
|
+ let [, showAppsButton] = this.actor.get_children(); |
|
+ let [minWidth, ] = showAppsButton.get_preferred_height(forHeight); |
|
+ |
|
+ alloc.min_size = minWidth; |
|
+ alloc.natural_size = natWidth; |
|
+ |
|
+ }, |
|
+ |
|
+ _getPreferredHeight: function(actor, forWidth, alloc) { |
|
+ // We want to request the natural height of all our children |
|
+ // as our natural height, so we chain up to StWidget (which |
|
+ // then calls BoxLayout), but we only request the showApps |
|
+ // button as the minimum size |
|
+ |
|
+ let [, natHeight] = this.actor.layout_manager.get_preferred_height(this.actor, forWidth); |
|
+ |
|
+ let themeNode = this.actor.get_theme_node(); |
|
+ let [, showAppsButton] = this.actor.get_children(); |
|
+ let [minHeight, ] = showAppsButton.get_preferred_height(forWidth); |
|
+ |
|
+ alloc.min_size = minHeight; |
|
+ alloc.natural_size = natHeight; |
|
+ } |
|
+}); |
|
+ |
|
+const baseIconSizes = [16, 22, 24, 32, 48, 64, 96, 128]; |
|
+ |
|
+/** |
|
+ * 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 |
|
+ * - hide showApps label when the custom menu is shown. |
|
+ * - add scrollview |
|
+ * ensure actor is visible on keyfocus inseid the scrollview |
|
+ * - add 128px icon size, might be usefull for hidpi display |
|
+ * - sync minimization application target position. |
|
+ * - keep running apps ordered. |
|
+ */ |
|
+var MyDash = new Lang.Class({ |
|
+ Name: 'DashToDock.MyDash', |
|
+ |
|
+ _init: function(settings, remoteModel, monitorIndex) { |
|
+ this._dtdSettings = settings; |
|
+ |
|
+ // Initialize icon variables and size |
|
+ this._maxHeight = -1; |
|
+ this.iconSize = this._dtdSettings.get_int('dash-max-icon-size'); |
|
+ this._availableIconSizes = baseIconSizes; |
|
+ this._shownInitially = false; |
|
+ this._initializeIconSize(this.iconSize); |
|
+ |
|
+ this._remoteModel = remoteModel; |
|
+ this._monitorIndex = monitorIndex; |
|
+ this._position = Utils.getPosition(settings); |
|
+ this._isHorizontal = ((this._position == St.Side.TOP) || |
|
+ (this._position == St.Side.BOTTOM)); |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ |
|
+ this._dragPlaceholder = null; |
|
+ this._dragPlaceholderPos = -1; |
|
+ this._animatingPlaceholdersCount = 0; |
|
+ this._showLabelTimeoutId = 0; |
|
+ this._resetHoverTimeoutId = 0; |
|
+ this._ensureAppIconVisibilityTimeoutId = 0; |
|
+ this._labelShowing = false; |
|
+ |
|
+ this._containerObject = new MyDashActor(settings); |
|
+ this._container = this._containerObject.actor; |
|
+ this._scrollView = new St.ScrollView({ |
|
+ name: 'dashtodockDashScrollview', |
|
+ hscrollbar_policy: Gtk.PolicyType.NEVER, |
|
+ vscrollbar_policy: Gtk.PolicyType.NEVER, |
|
+ enable_mouse_scrolling: false |
|
+ }); |
|
+ |
|
+ this._scrollView.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); |
|
+ |
|
+ this._box = new St.BoxLayout({ |
|
+ vertical: !this._isHorizontal, |
|
+ clip_to_allocation: false, |
|
+ x_align: Clutter.ActorAlign.START, |
|
+ y_align: Clutter.ActorAlign.START |
|
+ }); |
|
+ this._box._delegate = this; |
|
+ this._container.add_actor(this._scrollView); |
|
+ this._scrollView.add_actor(this._box); |
|
+ |
|
+ // Create a wrapper around the real showAppsIcon in order to add a popupMenu. |
|
+ let showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(this._dtdSettings); |
|
+ showAppsIconWrapper.connect('menu-state-changed', Lang.bind(this, function(showAppsIconWrapper, opened) { |
|
+ this._itemMenuStateChanged(showAppsIconWrapper, opened); |
|
+ })); |
|
+ // an instance of the showAppsIcon class is encapsulated in the wrapper |
|
+ this._showAppsIcon = showAppsIconWrapper.realShowAppsIcon; |
|
+ |
|
+ this._showAppsIcon.childScale = 1; |
|
+ this._showAppsIcon.childOpacity = 255; |
|
+ this._showAppsIcon.icon.setIconSize(this.iconSize); |
|
+ this._hookUpLabel(this._showAppsIcon); |
|
+ |
|
+ this.showAppsButton = this._showAppsIcon.toggleButton; |
|
+ |
|
+ this._container.add_actor(this._showAppsIcon); |
|
+ |
|
+ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; |
|
+ this.actor = new St.Bin({ |
|
+ child: this._container, |
|
+ y_align: St.Align.START, |
|
+ x_align: rtl ? St.Align.END : St.Align.START |
|
+ }); |
|
+ |
|
+ if (this._isHorizontal) { |
|
+ this.actor.connect('notify::width', Lang.bind(this, function() { |
|
+ if (this._maxHeight != this.actor.width) |
|
+ this._queueRedisplay(); |
|
+ this._maxHeight = this.actor.width; |
|
+ })); |
|
+ } |
|
+ else { |
|
+ this.actor.connect('notify::height', Lang.bind(this, function() { |
|
+ if (this._maxHeight != this.actor.height) |
|
+ this._queueRedisplay(); |
|
+ this._maxHeight = this.actor.height; |
|
+ })); |
|
+ } |
|
+ |
|
+ // Update minimization animation target position on allocation of the |
|
+ // container and on scrollview change. |
|
+ this._box.connect('notify::allocation', Lang.bind(this, this._updateAppsIconGeometry)); |
|
+ let scrollViewAdjustment = this._isHorizontal ? this._scrollView.hscroll.adjustment : this._scrollView.vscroll.adjustment; |
|
+ scrollViewAdjustment.connect('notify::value', Lang.bind(this, this._updateAppsIconGeometry)); |
|
+ |
|
+ this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); |
|
+ |
|
+ this._settings = new Gio.Settings({ |
|
+ schema_id: 'org.gnome.shell' |
|
+ }); |
|
+ |
|
+ this._appSystem = Shell.AppSystem.get_default(); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ this._appSystem, |
|
+ 'installed-changed', |
|
+ Lang.bind(this, function() { |
|
+ AppFavorites.getAppFavorites().reload(); |
|
+ this._queueRedisplay(); |
|
+ }) |
|
+ ], [ |
|
+ AppFavorites.getAppFavorites(), |
|
+ 'changed', |
|
+ Lang.bind(this, this._queueRedisplay) |
|
+ ], [ |
|
+ this._appSystem, |
|
+ 'app-state-changed', |
|
+ Lang.bind(this, this._queueRedisplay) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'item-drag-begin', |
|
+ Lang.bind(this, this._onDragBegin) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'item-drag-end', |
|
+ Lang.bind(this, this._onDragEnd) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'item-drag-cancelled', |
|
+ Lang.bind(this, this._onDragCancelled) |
|
+ ]); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this._signalsHandler.destroy(); |
|
+ }, |
|
+ |
|
+ _onScrollEvent: function(actor, event) { |
|
+ // If scroll is not used because the icon is resized, let the scroll event propagate. |
|
+ if (!this._dtdSettings.get_boolean('icon-size-fixed')) |
|
+ return Clutter.EVENT_PROPAGATE; |
|
+ |
|
+ // reset timeout to avid conflicts with the mousehover event |
|
+ if (this._ensureAppIconVisibilityTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId); |
|
+ this._ensureAppIconVisibilityTimeoutId = 0; |
|
+ } |
|
+ |
|
+ // Skip to avoid double events mouse |
|
+ if (event.is_pointer_emulated()) |
|
+ return Clutter.EVENT_STOP; |
|
+ |
|
+ let adjustment, delta; |
|
+ |
|
+ if (this._isHorizontal) |
|
+ adjustment = this._scrollView.get_hscroll_bar().get_adjustment(); |
|
+ else |
|
+ adjustment = this._scrollView.get_vscroll_bar().get_adjustment(); |
|
+ |
|
+ let increment = adjustment.step_increment; |
|
+ |
|
+ switch (event.get_scroll_direction()) { |
|
+ case Clutter.ScrollDirection.UP: |
|
+ delta = -increment; |
|
+ break; |
|
+ case Clutter.ScrollDirection.DOWN: |
|
+ delta = +increment; |
|
+ break; |
|
+ case Clutter.ScrollDirection.SMOOTH: |
|
+ let [dx, dy] = event.get_scroll_delta(); |
|
+ delta = dy * increment; |
|
+ // Also consider horizontal component, for instance touchpad |
|
+ if (this._isHorizontal) |
|
+ delta += dx * increment; |
|
+ break; |
|
+ } |
|
+ |
|
+ adjustment.set_value(adjustment.get_value() + delta); |
|
+ |
|
+ return Clutter.EVENT_STOP; |
|
+ }, |
|
+ |
|
+ _onDragBegin: function() { |
|
+ this._dragCancelled = false; |
|
+ this._dragMonitor = { |
|
+ dragMotion: Lang.bind(this, this._onDragMotion) |
|
+ }; |
|
+ 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); |
|
+ } |
|
+ }, |
|
+ |
|
+ _onDragCancelled: function() { |
|
+ this._dragCancelled = true; |
|
+ this._endDrag(); |
|
+ }, |
|
+ |
|
+ _onDragEnd: function() { |
|
+ if (this._dragCancelled) |
|
+ return; |
|
+ |
|
+ this._endDrag(); |
|
+ }, |
|
+ |
|
+ _endDrag: function() { |
|
+ this._clearDragPlaceholder(); |
|
+ this._clearEmptyDropTarget(); |
|
+ this._showAppsIcon.setDragApp(null); |
|
+ DND.removeDragMonitor(this._dragMonitor); |
|
+ }, |
|
+ |
|
+ _onDragMotion: function(dragEvent) { |
|
+ let app = Dash.getAppFromSource(dragEvent.source); |
|
+ if (app == null) |
|
+ return DND.DragMotionResult.CONTINUE; |
|
+ |
|
+ let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor); |
|
+ |
|
+ if (!this._box.contains(dragEvent.targetActor) || showAppsHovered) |
|
+ this._clearDragPlaceholder(); |
|
+ |
|
+ if (showAppsHovered) |
|
+ this._showAppsIcon.setDragApp(app); |
|
+ else |
|
+ this._showAppsIcon.setDragApp(null); |
|
+ |
|
+ return DND.DragMotionResult.CONTINUE; |
|
+ }, |
|
+ |
|
+ _appIdListToHash: function(apps) { |
|
+ let ids = {}; |
|
+ for (let i = 0; i < apps.length; i++) |
|
+ ids[apps[i].get_id()] = apps[i]; |
|
+ return ids; |
|
+ }, |
|
+ |
|
+ _queueRedisplay: function() { |
|
+ Main.queueDeferredWork(this._workId); |
|
+ }, |
|
+ |
|
+ _hookUpLabel: function(item, appIcon) { |
|
+ item.child.connect('notify::hover', Lang.bind(this, function() { |
|
+ this._syncLabel(item, appIcon); |
|
+ })); |
|
+ |
|
+ let id = Main.overview.connect('hiding', Lang.bind(this, function() { |
|
+ this._labelShowing = false; |
|
+ item.hideLabel(); |
|
+ })); |
|
+ item.child.connect('destroy', function() { |
|
+ Main.overview.disconnect(id); |
|
+ }); |
|
+ |
|
+ if (appIcon) { |
|
+ appIcon.connect('sync-tooltip', Lang.bind(this, function() { |
|
+ this._syncLabel(item, appIcon); |
|
+ })); |
|
+ } |
|
+ }, |
|
+ |
|
+ _createAppItem: function(app) { |
|
+ let appIcon = new AppIcons.MyAppIcon(this._dtdSettings, this._remoteModel, app, this._monitorIndex, |
|
+ { setSizeManually: true, |
|
+ showLabel: false }); |
|
+ |
|
+ if (appIcon._draggable) { |
|
+ appIcon._draggable.connect('drag-begin', Lang.bind(this, function() { |
|
+ appIcon.actor.opacity = 50; |
|
+ })); |
|
+ appIcon._draggable.connect('drag-end', Lang.bind(this, function() { |
|
+ appIcon.actor.opacity = 255; |
|
+ })); |
|
+ } |
|
+ |
|
+ appIcon.connect('menu-state-changed', Lang.bind(this, function(appIcon, opened) { |
|
+ this._itemMenuStateChanged(item, opened); |
|
+ })); |
|
+ |
|
+ let item = new Dash.DashItemContainer(); |
|
+ |
|
+ extendDashItemContainer(item, this._dtdSettings); |
|
+ item.setChild(appIcon.actor); |
|
+ |
|
+ appIcon.actor.connect('notify::hover', Lang.bind(this, function() { |
|
+ if (appIcon.actor.hover) { |
|
+ this._ensureAppIconVisibilityTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function() { |
|
+ ensureActorVisibleInScrollView(this._scrollView, appIcon.actor); |
|
+ this._ensureAppIconVisibilityTimeoutId = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ } |
|
+ else { |
|
+ if (this._ensureAppIconVisibilityTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._ensureAppIconVisibilityTimeoutId); |
|
+ this._ensureAppIconVisibilityTimeoutId = 0; |
|
+ } |
|
+ } |
|
+ })); |
|
+ |
|
+ appIcon.actor.connect('clicked', Lang.bind(this, function(actor) { |
|
+ ensureActorVisibleInScrollView(this._scrollView, actor); |
|
+ })); |
|
+ |
|
+ appIcon.actor.connect('key-focus-in', Lang.bind(this, function(actor) { |
|
+ let [x_shift, y_shift] = ensureActorVisibleInScrollView(this._scrollView, actor); |
|
+ |
|
+ // 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.actor.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 dash |
|
+ */ |
|
+ getAppIcons: function() { |
|
+ // Only consider children which are "proper" |
|
+ // icons (i.e. ignoring drag placeholders) and which are not |
|
+ // animating out (which means they will be destroyed at the end of |
|
+ // the animation) |
|
+ let iconChildren = this._box.get_children().filter(function(actor) { |
|
+ return actor.child && |
|
+ actor.child._delegate && |
|
+ actor.child._delegate.icon && |
|
+ !actor.animatingOut; |
|
+ }); |
|
+ |
|
+ let appIcons = iconChildren.map(function(actor) { |
|
+ return actor.child._delegate; |
|
+ }); |
|
+ |
|
+ return appIcons; |
|
+ }, |
|
+ |
|
+ _updateAppsIconGeometry: function() { |
|
+ let appIcons = this.getAppIcons(); |
|
+ appIcons.forEach(function(icon) { |
|
+ icon.updateIconGeometry(); |
|
+ }); |
|
+ }, |
|
+ |
|
+ _itemMenuStateChanged: function(item, opened) { |
|
+ // When the menu closes, it calls sync_hover, which means |
|
+ // that the notify::hover handler does everything we need to. |
|
+ if (opened) { |
|
+ if (this._showLabelTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._showLabelTimeoutId); |
|
+ this._showLabelTimeoutId = 0; |
|
+ } |
|
+ |
|
+ 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: function(item, appIcon) { |
|
+ let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover(); |
|
+ |
|
+ if (shouldShow) { |
|
+ if (this._showLabelTimeoutId == 0) { |
|
+ let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; |
|
+ this._showLabelTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, function() { |
|
+ this._labelShowing = true; |
|
+ item.showLabel(); |
|
+ this._showLabelTimeoutId = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel'); |
|
+ if (this._resetHoverTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._resetHoverTimeoutId); |
|
+ this._resetHoverTimeoutId = 0; |
|
+ } |
|
+ } |
|
+ } |
|
+ else { |
|
+ if (this._showLabelTimeoutId > 0) |
|
+ Mainloop.source_remove(this._showLabelTimeoutId); |
|
+ this._showLabelTimeoutId = 0; |
|
+ item.hideLabel(); |
|
+ if (this._labelShowing) { |
|
+ this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT, Lang.bind(this, function() { |
|
+ this._labelShowing = false; |
|
+ this._resetHoverTimeoutId = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing'); |
|
+ } |
|
+ } |
|
+ }, |
|
+ |
|
+ _adjustIconSize: function() { |
|
+ // For the icon size, we only consider children which are "proper" |
|
+ // icons (i.e. ignoring drag placeholders) and which are not |
|
+ // animating out (which means they will be destroyed at the end of |
|
+ // the animation) |
|
+ let iconChildren = this._box.get_children().filter(function(actor) { |
|
+ return actor.child && |
|
+ actor.child._delegate && |
|
+ actor.child._delegate.icon && |
|
+ !actor.animatingOut; |
|
+ }); |
|
+ |
|
+ iconChildren.push(this._showAppsIcon); |
|
+ |
|
+ if (this._maxHeight == -1) |
|
+ return; |
|
+ |
|
+ // Check if the container is present in the stage. This avoids critical |
|
+ // errors when unlocking the screen |
|
+ if (!this._container.get_stage()) |
|
+ return; |
|
+ |
|
+ let themeNode = this._container.get_theme_node(); |
|
+ let maxAllocation = new Clutter.ActorBox({ |
|
+ x1: 0, |
|
+ y1: 0, |
|
+ x2: this._isHorizontal ? this._maxHeight : 42 /* whatever */, |
|
+ y2: this._isHorizontal ? 42 : this._maxHeight |
|
+ }); |
|
+ let maxContent = themeNode.get_content_box(maxAllocation); |
|
+ let availHeight; |
|
+ if (this._isHorizontal) |
|
+ availHeight = maxContent.x2 - maxContent.x1; |
|
+ else |
|
+ availHeight = maxContent.y2 - maxContent.y1; |
|
+ let spacing = themeNode.get_length('spacing'); |
|
+ |
|
+ let firstButton = iconChildren[0].child; |
|
+ let firstIcon = firstButton._delegate.icon; |
|
+ |
|
+ let minHeight, natHeight, minWidth, natWidth; |
|
+ |
|
+ // Enforce the current icon size during the size request |
|
+ firstIcon.setIconSize(this.iconSize); |
|
+ [minHeight, natHeight] = firstButton.get_preferred_height(-1); |
|
+ [minWidth, natWidth] = firstButton.get_preferred_width(-1); |
|
+ |
|
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ let iconSizes = this._availableIconSizes.map(function(s) { |
|
+ return s * scaleFactor; |
|
+ }); |
|
+ |
|
+ // Subtract icon padding and box spacing from the available height |
|
+ if (this._isHorizontal) |
|
+ availHeight -= iconChildren.length * (natWidth - this.iconSize * scaleFactor) + |
|
+ (iconChildren.length - 1) * spacing; |
|
+ else |
|
+ availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) + |
|
+ (iconChildren.length - 1) * spacing; |
|
+ |
|
+ let availSize = availHeight / iconChildren.length; |
|
+ |
|
+ |
|
+ let newIconSize = this._availableIconSizes[0]; |
|
+ for (let i = 0; i < iconSizes.length; i++) { |
|
+ if (iconSizes[i] < availSize) |
|
+ newIconSize = this._availableIconSizes[i]; |
|
+ } |
|
+ |
|
+ if (newIconSize == this.iconSize) |
|
+ return; |
|
+ |
|
+ let oldIconSize = this.iconSize; |
|
+ this.iconSize = newIconSize; |
|
+ this.emit('icon-size-changed'); |
|
+ |
|
+ let scale = oldIconSize / newIconSize; |
|
+ 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 dash |
|
+ 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); |
|
+ |
|
+ Tweener.addTween(icon.icon, |
|
+ { width: targetWidth, |
|
+ height: targetHeight, |
|
+ time: DASH_ANIMATION_TIME, |
|
+ transition: 'easeOutQuad', |
|
+ }); |
|
+ } |
|
+ }, |
|
+ |
|
+ _redisplay: function() { |
|
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); |
|
+ |
|
+ let running = this._appSystem.get_running(); |
|
+ if (this._dtdSettings.get_boolean('isolate-workspaces') || |
|
+ this._dtdSettings.get_boolean('isolate-monitors')) { |
|
+ // When using isolation, we filter out apps that have no windows in |
|
+ // the current workspace |
|
+ let settings = this._dtdSettings; |
|
+ let monitorIndex = this._monitorIndex; |
|
+ running = running.filter(function(_app) { |
|
+ return AppIcons.getInterestingWindows(_app, settings, monitorIndex).length != 0; |
|
+ }); |
|
+ } |
|
+ |
|
+ let children = this._box.get_children().filter(function(actor) { |
|
+ return actor.child && |
|
+ actor.child._delegate && |
|
+ actor.child._delegate.app; |
|
+ }); |
|
+ // Apps currently in the dash |
|
+ let oldApps = children.map(function(actor) { |
|
+ return actor.child._delegate.app; |
|
+ }); |
|
+ // Apps supposed to be in the dash |
|
+ let newApps = []; |
|
+ |
|
+ if (this._dtdSettings.get_boolean('show-favorites')) { |
|
+ for (let id in favorites) |
|
+ newApps.push(favorites[id]); |
|
+ } |
|
+ |
|
+ // We reorder the running apps so that they don't change position on the |
|
+ // dash with every redisplay() call |
|
+ if (this._dtdSettings.get_boolean('show-running')) { |
|
+ // First: add the apps from the oldApps list that are still running |
|
+ for (let i = 0; i < oldApps.length; i++) { |
|
+ let index = running.indexOf(oldApps[i]); |
|
+ if (index > -1) { |
|
+ let app = running.splice(index, 1)[0]; |
|
+ if (this._dtdSettings.get_boolean('show-favorites') && (app.get_id() in favorites)) |
|
+ continue; |
|
+ newApps.push(app); |
|
+ } |
|
+ } |
|
+ // Second: add the new apps |
|
+ for (let i = 0; i < running.length; i++) { |
|
+ let app = running[i]; |
|
+ if (this._dtdSettings.get_boolean('show-favorites') && (app.get_id() in favorites)) |
|
+ continue; |
|
+ newApps.push(app); |
|
+ } |
|
+ } |
|
+ |
|
+ // Figure out the actual changes to the list of items; we iterate |
|
+ // over both the list of items currently in the dash and the list |
|
+ // of items expected there, and collect additions and removals. |
|
+ // Moves are both an addition and a removal, where the order of |
|
+ // the operations depends on whether we encounter the position |
|
+ // where the item has been added first or the one from where it |
|
+ // was removed. |
|
+ // There is an assumption that only one item is moved at a given |
|
+ // time; when moving several items at once, everything will still |
|
+ // end up at the right position, but there might be additional |
|
+ // additions/removals (e.g. it might remove all the launchers |
|
+ // and add them back in the new order even if a smaller set of |
|
+ // additions and removals is possible). |
|
+ // If above assumptions turns out to be a problem, we might need |
|
+ // to use a more sophisticated algorithm, e.g. Longest Common |
|
+ // Subsequence as used by diff. |
|
+ |
|
+ let addedItems = []; |
|
+ let removedActors = []; |
|
+ |
|
+ let newIndex = 0; |
|
+ let oldIndex = 0; |
|
+ while ((newIndex < newApps.length) || (oldIndex < oldApps.length)) { |
|
+ // No change at oldIndex/newIndex |
|
+ if (oldApps[oldIndex] && oldApps[oldIndex] == newApps[newIndex]) { |
|
+ oldIndex++; |
|
+ newIndex++; |
|
+ continue; |
|
+ } |
|
+ |
|
+ // App removed at oldIndex |
|
+ if (oldApps[oldIndex] && (newApps.indexOf(oldApps[oldIndex]) == -1)) { |
|
+ removedActors.push(children[oldIndex]); |
|
+ oldIndex++; |
|
+ continue; |
|
+ } |
|
+ |
|
+ // App added at newIndex |
|
+ if (newApps[newIndex] && (oldApps.indexOf(newApps[newIndex]) == -1)) { |
|
+ let newItem = this._createAppItem(newApps[newIndex]); |
|
+ addedItems.push({ app: newApps[newIndex], |
|
+ item: newItem, |
|
+ pos: newIndex }); |
|
+ newIndex++; |
|
+ continue; |
|
+ } |
|
+ |
|
+ // App moved |
|
+ let insertHere = newApps[newIndex + 1] && (newApps[newIndex + 1] == oldApps[oldIndex]); |
|
+ let alreadyRemoved = removedActors.reduce(function(result, actor) { |
|
+ let removedApp = actor.child._delegate.app; |
|
+ return result || removedApp == newApps[newIndex]; |
|
+ }, false); |
|
+ |
|
+ if (insertHere || alreadyRemoved) { |
|
+ let newItem = this._createAppItem(newApps[newIndex]); |
|
+ addedItems.push({ |
|
+ app: newApps[newIndex], |
|
+ item: newItem, |
|
+ pos: newIndex + removedActors.length |
|
+ }); |
|
+ newIndex++; |
|
+ } |
|
+ else { |
|
+ removedActors.push(children[oldIndex]); |
|
+ oldIndex++; |
|
+ } |
|
+ } |
|
+ |
|
+ for (let i = 0; i < addedItems.length; i++) |
|
+ this._box.insert_child_at_index(addedItems[i].item, |
|
+ addedItems[i].pos); |
|
+ |
|
+ for (let i = 0; i < removedActors.length; i++) { |
|
+ let item = removedActors[i]; |
|
+ |
|
+ // Don't animate item removal when the overview is transitioning |
|
+ if (!Main.overview.animationInProgress) |
|
+ item.animateOutAndDestroy(); |
|
+ else |
|
+ item.destroy(); |
|
+ } |
|
+ |
|
+ this._adjustIconSize(); |
|
+ |
|
+ for (let i = 0; i < addedItems.length; i++) |
|
+ // Emit a custom signal notifying that a new item has been added |
|
+ this.emit('item-added', addedItems[i]); |
|
+ |
|
+ // Skip animations on first run when adding the initial set |
|
+ // of items, to avoid all items zooming in at once |
|
+ |
|
+ let animate = this._shownInitially && |
|
+ !Main.overview.animationInProgress; |
|
+ |
|
+ if (!this._shownInitially) |
|
+ this._shownInitially = true; |
|
+ |
|
+ for (let i = 0; i < addedItems.length; i++) |
|
+ addedItems[i].item.show(animate); |
|
+ |
|
+ // 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._updateAppsIconGeometry(); |
|
+ |
|
+ // This will update the size, and the corresponding number for each icon |
|
+ this._updateNumberOverlay(); |
|
+ }, |
|
+ |
|
+ _updateNumberOverlay: function() { |
|
+ let appIcons = this.getAppIcons(); |
|
+ let counter = 1; |
|
+ appIcons.forEach(function(icon) { |
|
+ if (counter < 10){ |
|
+ icon.setNumberOverlay(counter); |
|
+ counter++; |
|
+ } |
|
+ else if (counter == 10) { |
|
+ icon.setNumberOverlay(0); |
|
+ counter++; |
|
+ } |
|
+ else { |
|
+ // No overlay after 10 |
|
+ icon.setNumberOverlay(-1); |
|
+ } |
|
+ icon.updateNumberOverlay(); |
|
+ }); |
|
+ |
|
+ }, |
|
+ |
|
+ toggleNumberOverlay: function(activate) { |
|
+ let appIcons = this.getAppIcons(); |
|
+ appIcons.forEach(function(icon) { |
|
+ icon.toggleNumberOverlay(activate); |
|
+ }); |
|
+ }, |
|
+ |
|
+ _initializeIconSize: function(max_size) { |
|
+ let max_allowed = baseIconSizes[baseIconSizes.length-1]; |
|
+ max_size = Math.min(max_size, max_allowed); |
|
+ |
|
+ if (this._dtdSettings.get_boolean('icon-size-fixed')) |
|
+ this._availableIconSizes = [max_size]; |
|
+ else { |
|
+ this._availableIconSizes = baseIconSizes.filter(function(val) { |
|
+ return (val<max_size); |
|
+ }); |
|
+ this._availableIconSizes.push(max_size); |
|
+ } |
|
+ }, |
|
+ |
|
+ setIconSize: function(max_size, doNotAnimate) { |
|
+ this._initializeIconSize(max_size); |
|
+ |
|
+ if (doNotAnimate) |
|
+ this._shownInitially = false; |
|
+ |
|
+ this._queueRedisplay(); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Reset the displayed apps icon to mantain the correct order when changing |
|
+ * show favorites/show running settings |
|
+ */ |
|
+ resetAppIcons: function() { |
|
+ let children = this._box.get_children().filter(function(actor) { |
|
+ return actor.child && |
|
+ actor.child._delegate && |
|
+ actor.child._delegate.icon; |
|
+ }); |
|
+ for (let i = 0; i < children.length; i++) { |
|
+ let item = children[i]; |
|
+ item.destroy(); |
|
+ } |
|
+ |
|
+ // to avoid ugly animations, just suppress them like when dash is first loaded. |
|
+ this._shownInitially = false; |
|
+ this._redisplay(); |
|
+ |
|
+ }, |
|
+ |
|
+ _clearDragPlaceholder: function() { |
|
+ if (this._dragPlaceholder) { |
|
+ this._animatingPlaceholdersCount++; |
|
+ this._dragPlaceholder.animateOutAndDestroy(); |
|
+ this._dragPlaceholder.connect('destroy', Lang.bind(this, function() { |
|
+ this._animatingPlaceholdersCount--; |
|
+ })); |
|
+ this._dragPlaceholder = null; |
|
+ } |
|
+ this._dragPlaceholderPos = -1; |
|
+ }, |
|
+ |
|
+ _clearEmptyDropTarget: function() { |
|
+ if (this._emptyDropTarget) { |
|
+ this._emptyDropTarget.animateOutAndDestroy(); |
|
+ this._emptyDropTarget = null; |
|
+ } |
|
+ }, |
|
+ |
|
+ handleDragOver: function(source, actor, x, y, time) { |
|
+ let app = Dash.getAppFromSource(source); |
|
+ |
|
+ // Don't allow favoriting of transient apps |
|
+ if (app == null || app.is_window_backed()) |
|
+ return DND.DragMotionResult.NO_DROP; |
|
+ |
|
+ if (!this._settings.is_writable('favorite-apps') || !this._dtdSettings.get_boolean('show-favorites')) |
|
+ return DND.DragMotionResult.NO_DROP; |
|
+ |
|
+ let favorites = AppFavorites.getAppFavorites().getFavorites(); |
|
+ let numFavorites = favorites.length; |
|
+ |
|
+ let favPos = favorites.indexOf(app); |
|
+ |
|
+ let children = this._box.get_children(); |
|
+ let numChildren = children.length; |
|
+ let boxHeight = 0; |
|
+ for (let i = 0; i < numChildren; i++) |
|
+ boxHeight += this._isHorizontal?children[i].width:children[i].height; |
|
+ |
|
+ // Keep the placeholder out of the index calculation; assuming that |
|
+ // the remove target has the same size as "normal" items, we don't |
|
+ // need to do the same adjustment there. |
|
+ if (this._dragPlaceholder) { |
|
+ boxHeight -= this._isHorizontal?this._dragPlaceholder.width:this._dragPlaceholder.height; |
|
+ numChildren--; |
|
+ } |
|
+ |
|
+ let pos; |
|
+ if (!this._emptyDropTarget) { |
|
+ pos = Math.floor((this._isHorizontal?x:y) * numChildren / boxHeight); |
|
+ if (pos > numChildren) |
|
+ pos = numChildren; |
|
+ } |
|
+ else |
|
+ pos = 0; // always insert at the top when dash is empty |
|
+ |
|
+ // Take into account childredn position in rtl |
|
+ if (this._isHorizontal && (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)) |
|
+ pos = numChildren - pos; |
|
+ |
|
+ if ((pos != this._dragPlaceholderPos) && (pos <= numFavorites) && (this._animatingPlaceholdersCount == 0)) { |
|
+ this._dragPlaceholderPos = pos; |
|
+ |
|
+ // Don't allow positioning before or after self |
|
+ if ((favPos != -1) && (pos == favPos || pos == favPos + 1)) { |
|
+ this._clearDragPlaceholder(); |
|
+ return DND.DragMotionResult.CONTINUE; |
|
+ } |
|
+ |
|
+ // If the placeholder already exists, we just move |
|
+ // it, but if we are adding it, expand its size in |
|
+ // an animation |
|
+ let fadeIn; |
|
+ if (this._dragPlaceholder) { |
|
+ this._dragPlaceholder.destroy(); |
|
+ fadeIn = false; |
|
+ } |
|
+ else |
|
+ fadeIn = true; |
|
+ |
|
+ this._dragPlaceholder = new Dash.DragPlaceholderItem(); |
|
+ this._dragPlaceholder.child.set_width (this.iconSize); |
|
+ this._dragPlaceholder.child.set_height (this.iconSize / 2); |
|
+ this._box.insert_child_at_index(this._dragPlaceholder, |
|
+ this._dragPlaceholderPos); |
|
+ this._dragPlaceholder.show(fadeIn); |
|
+ // Ensure the next and previous icon are visible when moving the placeholder |
|
+ // (I assume there's room for both of them) |
|
+ if (this._dragPlaceholderPos > 1) |
|
+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos-1]); |
|
+ if (this._dragPlaceholderPos < this._box.get_children().length-1) |
|
+ ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[this._dragPlaceholderPos+1]); |
|
+ } |
|
+ |
|
+ // Remove the drag placeholder if we are not in the |
|
+ // "favorites zone" |
|
+ if (pos > numFavorites) |
|
+ this._clearDragPlaceholder(); |
|
+ |
|
+ if (!this._dragPlaceholder) |
|
+ return DND.DragMotionResult.NO_DROP; |
|
+ |
|
+ let srcIsFavorite = (favPos != -1); |
|
+ |
|
+ if (srcIsFavorite) |
|
+ return DND.DragMotionResult.MOVE_DROP; |
|
+ |
|
+ return DND.DragMotionResult.COPY_DROP; |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Draggable target interface |
|
+ */ |
|
+ acceptDrop: function(source, actor, x, y, time) { |
|
+ let app = Dash.getAppFromSource(source); |
|
+ |
|
+ // Don't allow favoriting of transient apps |
|
+ if (app == null || app.is_window_backed()) |
|
+ return false; |
|
+ |
|
+ if (!this._settings.is_writable('favorite-apps') || !this._dtdSettings.get_boolean('show-favorites')) |
|
+ return false; |
|
+ |
|
+ let id = app.get_id(); |
|
+ |
|
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); |
|
+ |
|
+ let srcIsFavorite = (id in favorites); |
|
+ |
|
+ let favPos = 0; |
|
+ let children = this._box.get_children(); |
|
+ for (let i = 0; i < this._dragPlaceholderPos; i++) { |
|
+ if (this._dragPlaceholder && (children[i] == this._dragPlaceholder)) |
|
+ continue; |
|
+ |
|
+ let childId = children[i].child._delegate.app.get_id(); |
|
+ if (childId == id) |
|
+ continue; |
|
+ if (childId in favorites) |
|
+ favPos++; |
|
+ } |
|
+ |
|
+ // No drag placeholder means we don't wan't to favorite the app |
|
+ // and we are dragging it to its original position |
|
+ if (!this._dragPlaceholder) |
|
+ return true; |
|
+ |
|
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { |
|
+ let appFavorites = AppFavorites.getAppFavorites(); |
|
+ if (srcIsFavorite) |
|
+ appFavorites.moveFavoriteToPos(id, favPos); |
|
+ else |
|
+ appFavorites.addFavoriteAtPos(id, favPos); |
|
+ return false; |
|
+ })); |
|
+ |
|
+ return true; |
|
+ }, |
|
+ |
|
+ showShowAppsButton: function() { |
|
+ this.showAppsButton.visible = true |
|
+ this.showAppsButton.set_width(-1) |
|
+ this.showAppsButton.set_height(-1) |
|
+ }, |
|
+ |
|
+ hideShowAppsButton: function() { |
|
+ this.showAppsButton.hide() |
|
+ this.showAppsButton.set_width(0) |
|
+ this.showAppsButton.set_height(0) |
|
+ } |
|
+}); |
|
+ |
|
+Signals.addSignalMethods(MyDash.prototype); |
|
+ |
|
+/** |
|
+ * 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 |
|
+ */ |
|
+function ensureActorVisibleInScrollView(scrollView, actor) { |
|
+ let adjust_v = true; |
|
+ let adjust_h = true; |
|
+ |
|
+ let vadjustment = scrollView.vscroll.adjustment; |
|
+ let hadjustment = scrollView.hscroll.adjustment; |
|
+ let [vvalue, vlower, vupper, vstepIncrement, vpageIncrement, vpageSize] = vadjustment.get_values(); |
|
+ let [hvalue, hlower, hupper, hstepIncrement, hpageIncrement, hpageSize] = hadjustment.get_values(); |
|
+ |
|
+ let [hvalue0, vvalue0] = [hvalue, vvalue]; |
|
+ |
|
+ let voffset = 0; |
|
+ let hoffset = 0; |
|
+ let fade = scrollView.get_effect('fade'); |
|
+ if (fade) { |
|
+ voffset = fade.vfade_offset; |
|
+ hoffset = fade.hfade_offset; |
|
+ } |
|
+ |
|
+ 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); |
|
+ |
|
+ if (vvalue !== vvalue0) { |
|
+ Tweener.addTween(vadjustment, { value: vvalue, |
|
+ time: Util.SCROLL_TIME, |
|
+ transition: 'easeOutQuad' |
|
+ }); |
|
+ } |
|
+ |
|
+ if (hvalue !== hvalue0) { |
|
+ Tweener.addTween(hadjustment, |
|
+ { value: hvalue, |
|
+ time: Util.SCROLL_TIME, |
|
+ transition: 'easeOutQuad' }); |
|
+ } |
|
+ |
|
+ return [hvalue- hvalue0, vvalue - vvalue0]; |
|
+} |
|
diff --git a/extensions/dash-to-dock/docking.js b/extensions/dash-to-dock/docking.js |
|
new file mode 100644 |
|
index 0000000..11810a1 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/docking.js |
|
@@ -0,0 +1,1925 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const GLib = imports.gi.GLib; |
|
+const Gtk = imports.gi.Gtk; |
|
+const Lang = imports.lang; |
|
+const Meta = imports.gi.Meta; |
|
+const Shell = imports.gi.Shell; |
|
+const St = imports.gi.St; |
|
+const Mainloop = imports.mainloop; |
|
+const Params = imports.misc.params; |
|
+ |
|
+const Main = imports.ui.main; |
|
+const Dash = imports.ui.dash; |
|
+const IconGrid = imports.ui.iconGrid; |
|
+const Overview = imports.ui.overview; |
|
+const OverviewControls = imports.ui.overviewControls; |
|
+const PointerWatcher = imports.ui.pointerWatcher; |
|
+const Tweener = imports.ui.tweener; |
|
+const Signals = imports.signals; |
|
+const ViewSelector = imports.ui.viewSelector; |
|
+const WorkspaceSwitcherPopup= imports.ui.workspaceSwitcherPopup; |
|
+const Layout = imports.ui.layout; |
|
+const LayoutManager = imports.ui.main.layoutManager; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Convenience = Me.imports.convenience; |
|
+const Utils = Me.imports.utils; |
|
+const Intellihide = Me.imports.intellihide; |
|
+const Theming = Me.imports.theming; |
|
+const MyDash = Me.imports.dash; |
|
+const LauncherAPI = Me.imports.launcherAPI; |
|
+ |
|
+const DOCK_DWELL_CHECK_INTERVAL = 100; |
|
+ |
|
+var State = { |
|
+ HIDDEN: 0, |
|
+ SHOWING: 1, |
|
+ SHOWN: 2, |
|
+ HIDING: 3 |
|
+}; |
|
+ |
|
+const scrollAction = { |
|
+ DO_NOTHING: 0, |
|
+ CYCLE_WINDOWS: 1, |
|
+ SWITCH_WORKSPACE: 2 |
|
+}; |
|
+ |
|
+/** |
|
+ * A simple St.Widget with one child whose allocation takes into account the |
|
+ * slide out of its child via the _slidex parameter ([0:1]). |
|
+ * |
|
+ * Required since I want to track the input region of this container which is |
|
+ * based on its allocation even if the child overlows the parent actor. By doing |
|
+ * this the region of the dash that is slideout is not steling anymore the input |
|
+ * regions making the extesion usable when the primary monitor is the right one. |
|
+ * |
|
+ * The slidex parameter can be used to directly animate the sliding. The parent |
|
+ * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM) |
|
+ * side. |
|
+ * |
|
+ * It can't be an extended object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. |
|
+ * thus use the Shell.GenericContainer pattern. |
|
+*/ |
|
+const DashSlideContainer = new Lang.Class({ |
|
+ Name: 'DashToDock.DashSlideContainer', |
|
+ |
|
+ _init: function(params) { |
|
+ // Default local params |
|
+ let localDefaults = { |
|
+ side: St.Side.LEFT, |
|
+ initialSlideValue: 1 |
|
+ } |
|
+ |
|
+ let localParams = Params.parse(params, localDefaults, true); |
|
+ |
|
+ if (params) { |
|
+ // Remove local params before passing the params to the parent |
|
+ // constructor to avoid errors. |
|
+ let prop; |
|
+ for (prop in localDefaults) { |
|
+ if ((prop in params)) |
|
+ delete params[prop]; |
|
+ } |
|
+ } |
|
+ |
|
+ this.actor = new Shell.GenericContainer(params); |
|
+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); |
|
+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); |
|
+ this.actor.connect('allocate', Lang.bind(this, this._allocate)); |
|
+ |
|
+ this.actor._delegate = this; |
|
+ |
|
+ this._child = null; |
|
+ |
|
+ // slide parameter: 1 = visible, 0 = hidden. |
|
+ this._slidex = localParams.initialSlideValue; |
|
+ this._side = localParams.side; |
|
+ this._slideoutSize = 0; // minimum size when slided out |
|
+ }, |
|
+ |
|
+ _allocate: function(actor, box, flags) { |
|
+ if (this._child == null) |
|
+ return; |
|
+ |
|
+ let availWidth = box.x2 - box.x1; |
|
+ let availHeight = box.y2 - box.y1; |
|
+ let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = |
|
+ this._child.get_preferred_size(); |
|
+ |
|
+ let childWidth = natChildWidth; |
|
+ let childHeight = natChildHeight; |
|
+ |
|
+ let childBox = new Clutter.ActorBox(); |
|
+ |
|
+ let slideoutSize = this._slideoutSize; |
|
+ |
|
+ if (this._side == St.Side.LEFT) { |
|
+ childBox.x1 = (this._slidex -1) * (childWidth - slideoutSize); |
|
+ childBox.x2 = slideoutSize + this._slidex*(childWidth - slideoutSize); |
|
+ childBox.y1 = 0; |
|
+ childBox.y2 = childBox.y1 + childHeight; |
|
+ } |
|
+ else if ((this._side == St.Side.RIGHT) || (this._side == St.Side.BOTTOM)) { |
|
+ childBox.x1 = 0; |
|
+ childBox.x2 = childWidth; |
|
+ childBox.y1 = 0; |
|
+ childBox.y2 = childBox.y1 + childHeight; |
|
+ } |
|
+ else if (this._side == St.Side.TOP) { |
|
+ childBox.x1 = 0; |
|
+ childBox.x2 = childWidth; |
|
+ childBox.y1 = (this._slidex -1) * (childHeight - slideoutSize); |
|
+ childBox.y2 = slideoutSize + this._slidex * (childHeight - slideoutSize); |
|
+ } |
|
+ |
|
+ this._child.allocate(childBox, flags); |
|
+ this._child.set_clip(-childBox.x1, -childBox.y1, |
|
+ -childBox.x1+availWidth, -childBox.y1 + availHeight); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Just the child width but taking into account the slided out part |
|
+ */ |
|
+ _getPreferredWidth: function(actor, forHeight, alloc) { |
|
+ let [minWidth, natWidth] = this._child.get_preferred_width(forHeight); |
|
+ if ((this._side == St.Side.LEFT) || (this._side == St.Side.RIGHT)) { |
|
+ minWidth = (minWidth - this._slideoutSize) * this._slidex + this._slideoutSize; |
|
+ natWidth = (natWidth - this._slideoutSize) * this._slidex + this._slideoutSize; |
|
+ } |
|
+ |
|
+ alloc.min_size = minWidth; |
|
+ alloc.natural_size = natWidth; |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Just the child height but taking into account the slided out part |
|
+ */ |
|
+ _getPreferredHeight: function(actor, forWidth, alloc) { |
|
+ let [minHeight, natHeight] = this._child.get_preferred_height(forWidth); |
|
+ if ((this._side == St.Side.TOP) || (this._side == St.Side.BOTTOM)) { |
|
+ minHeight = (minHeight - this._slideoutSize) * this._slidex + this._slideoutSize; |
|
+ natHeight = (natHeight - this._slideoutSize) * this._slidex + this._slideoutSize; |
|
+ } |
|
+ alloc.min_size = minHeight; |
|
+ alloc.natural_size = natHeight; |
|
+ }, |
|
+ |
|
+ /** |
|
+ * I was expecting it to be a virtual function... stil I don't understand |
|
+ * how things work. |
|
+ */ |
|
+ add_child: function(actor) { |
|
+ // I'm supposed to have only on child |
|
+ if (this._child !== null) |
|
+ this.actor.remove_child(actor); |
|
+ |
|
+ this._child = actor; |
|
+ this.actor.add_child(actor); |
|
+ }, |
|
+ |
|
+ set slidex(value) { |
|
+ this._slidex = value; |
|
+ this._child.queue_relayout(); |
|
+ }, |
|
+ |
|
+ get slidex() { |
|
+ return this._slidex; |
|
+ } |
|
+}); |
|
+ |
|
+const DockedDash = new Lang.Class({ |
|
+ Name: 'DashToDock.DockedDash', |
|
+ |
|
+ _init: function(settings, remoteModel, monitorIndex) { |
|
+ this._rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL); |
|
+ |
|
+ // Load settings |
|
+ this._settings = settings; |
|
+ this._remoteModel = remoteModel; |
|
+ this._monitorIndex = monitorIndex; |
|
+ // Connect global signals |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ |
|
+ this._bindSettingsChanges(); |
|
+ |
|
+ this._position = Utils.getPosition(settings); |
|
+ this._isHorizontal = ((this._position == St.Side.TOP) || (this._position == St.Side.BOTTOM)); |
|
+ |
|
+ // Temporary ignore hover events linked to autohide for whatever reason |
|
+ this._ignoreHover = false; |
|
+ this._oldignoreHover = null; |
|
+ // This variables are linked to the settings regardles of autohide or intellihide |
|
+ // being temporary disable. Get set by _updateVisibilityMode; |
|
+ this._autohideIsEnabled = null; |
|
+ this._intellihideIsEnabled = null; |
|
+ this._fixedIsEnabled = null; |
|
+ |
|
+ // Create intellihide object to monitor windows overlapping |
|
+ this._intellihide = new Intellihide.Intellihide(this._settings, this._monitorIndex); |
|
+ |
|
+ // initialize dock state |
|
+ this._dockState = State.HIDDEN; |
|
+ |
|
+ // Put dock on the required monitor |
|
+ this._monitor = Main.layoutManager.monitors[this._monitorIndex]; |
|
+ |
|
+ // this store size and the position where the dash is shown; |
|
+ // used by intellihide module to check window overlap. |
|
+ this.staticBox = new Clutter.ActorBox(); |
|
+ |
|
+ // Initialize pressure barrier variables |
|
+ this._canUsePressure = false; |
|
+ this._pressureBarrier = null; |
|
+ this._barrier = null; |
|
+ this._removeBarrierTimeoutId = 0; |
|
+ |
|
+ // Initialize dwelling system variables |
|
+ this._dockDwelling = false; |
|
+ this._dockWatch = null; |
|
+ this._dockDwellUserTime = 0; |
|
+ this._dockDwellTimeoutId = 0 |
|
+ |
|
+ // Create a new dash object |
|
+ this.dash = new MyDash.MyDash(this._settings, this._remoteModel, this._monitorIndex); |
|
+ |
|
+ if (!this._settings.get_boolean('show-show-apps-button')) |
|
+ this.dash.hideShowAppsButton(); |
|
+ |
|
+ // Create the main actor and the containers for sliding in and out and |
|
+ // centering, turn on track hover |
|
+ |
|
+ let positionStyleClass = ['top', 'right', 'bottom', 'left']; |
|
+ // This is the centering actor |
|
+ this.actor = new St.Bin({ |
|
+ name: 'dashtodockContainer', |
|
+ reactive: false, |
|
+ style_class: positionStyleClass[this._position], |
|
+ x_align: this._isHorizontal?St.Align.MIDDLE:St.Align.START, |
|
+ y_align: this._isHorizontal?St.Align.START:St.Align.MIDDLE |
|
+ }); |
|
+ this.actor._delegate = this; |
|
+ |
|
+ // This is the sliding actor whose allocation is to be tracked for input regions |
|
+ this._slider = new DashSlideContainer({ |
|
+ side: this._position, |
|
+ initialSlideValue: 0 |
|
+ }); |
|
+ |
|
+ // This is the actor whose hover status us tracked for autohide |
|
+ this._box = new St.BoxLayout({ |
|
+ name: 'dashtodockBox', |
|
+ reactive: true, |
|
+ track_hover: true |
|
+ }); |
|
+ this._box.connect('notify::hover', Lang.bind(this, this._hoverChanged)); |
|
+ |
|
+ // Create and apply height constraint to the dash. It's controlled by this.actor height |
|
+ this.constrainSize = new Clutter.BindConstraint({ |
|
+ source: this.actor, |
|
+ coordinate: this._isHorizontal?Clutter.BindCoordinate.WIDTH:Clutter.BindCoordinate.HEIGHT |
|
+ }); |
|
+ this.dash.actor.add_constraint(this.constrainSize); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ Main.overview, |
|
+ 'item-drag-begin', |
|
+ Lang.bind(this, this._onDragStart) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'item-drag-end', |
|
+ Lang.bind(this, this._onDragEnd) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'item-drag-cancelled', |
|
+ Lang.bind(this, this._onDragEnd) |
|
+ ], [ |
|
+ // update when workarea changes, for instance if other extensions modify the struts |
|
+ //(like moving th panel at the bottom) |
|
+ global.screen, |
|
+ 'workareas-changed', |
|
+ Lang.bind(this, this._resetPosition) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'showing', |
|
+ Lang.bind(this, this._onOverviewShowing) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'hiding', |
|
+ Lang.bind(this, this._onOverviewHiding) |
|
+ ], [ |
|
+ // Hide on appview |
|
+ Main.overview.viewSelector, |
|
+ 'page-changed', |
|
+ Lang.bind(this, this._pageChanged) |
|
+ ], [ |
|
+ Main.overview.viewSelector, |
|
+ 'page-empty', |
|
+ Lang.bind(this, this._onPageEmpty) |
|
+ ], [ |
|
+ // Ensure the ShowAppsButton status is kept in sync |
|
+ Main.overview.viewSelector._showAppsButton, |
|
+ 'notify::checked', |
|
+ Lang.bind(this, this._syncShowAppsButtonToggled) |
|
+ ], [ |
|
+ global.screen, |
|
+ 'in-fullscreen-changed', |
|
+ Lang.bind(this, this._updateBarrier) |
|
+ ], [ |
|
+ // Monitor windows overlapping |
|
+ this._intellihide, |
|
+ 'status-changed', |
|
+ Lang.bind(this, this._updateDashVisibility) |
|
+ ], [ |
|
+ // Keep dragged icon consistent in size with this dash |
|
+ this.dash, |
|
+ 'icon-size-changed', |
|
+ Lang.bind(this, function() { |
|
+ Main.overview.dashIconSize = this.dash.iconSize; |
|
+ }) |
|
+ ], [ |
|
+ // This duplicate the similar signal which is in owerview.js. |
|
+ // Being connected and thus executed later this effectively |
|
+ // overwrite any attempt to use the size of the default dash |
|
+ //which given the customization is usually much smaller. |
|
+ // I can't easily disconnect the original signal |
|
+ Main.overview._controls.dash, |
|
+ 'icon-size-changed', |
|
+ Lang.bind(this, function() { |
|
+ Main.overview.dashIconSize = this.dash.iconSize; |
|
+ }) |
|
+ ]); |
|
+ |
|
+ this._injectionsHandler = new Utils.InjectionsHandler(); |
|
+ this._themeManager = new Theming.ThemeManager(this._settings, this); |
|
+ |
|
+ // Since the actor is not a topLevel child and its parent is now not added to the Chrome, |
|
+ // the allocation change of the parent container (slide in and slideout) doesn't trigger |
|
+ // anymore an update of the input regions. Force the update manually. |
|
+ this.actor.connect('notify::allocation', |
|
+ Lang.bind(Main.layoutManager, Main.layoutManager._queueUpdateRegions)); |
|
+ |
|
+ this.dash._container.connect('allocation-changed', Lang.bind(this, this._updateStaticBox)); |
|
+ this._slider.actor.connect(this._isHorizontal ? 'notify::x' : 'notify::y', Lang.bind(this, this._updateStaticBox)); |
|
+ |
|
+ // sync hover after a popupmenu is closed |
|
+ this.dash.connect('menu-closed', Lang.bind(this, function() { |
|
+ this._box.sync_hover(); |
|
+ })); |
|
+ |
|
+ // Load optional features that need to be activated for one dock only |
|
+ if (this._monitorIndex == this._settings.get_int('preferred-monitor')) |
|
+ this._enableExtraFeatures(); |
|
+ // Load optional features that need to be activated once per dock |
|
+ this._optionalScrollWorkspaceSwitch(); |
|
+ |
|
+ // Delay operations that require the shell to be fully loaded and with |
|
+ // user theme applied. |
|
+ |
|
+ this._paintId = this.actor.connect('paint', Lang.bind(this, this._initialize)); |
|
+ |
|
+ // Manage the which is used to reserve space in the overview for the dock |
|
+ // Add and additional dashSpacer positioned according to the dash positioning. |
|
+ // It gets restored on extension unload. |
|
+ this._dashSpacer = new OverviewControls.DashSpacer(); |
|
+ this._dashSpacer.setDashActor(this._box); |
|
+ |
|
+ if (this._position == St.Side.LEFT) |
|
+ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? -1 : 0); // insert on first |
|
+ else if (this._position == St.Side.RIGHT) |
|
+ Main.overview._controls._group.insert_child_at_index(this._dashSpacer, this._rtl ? 0 : -1); // insert on last |
|
+ else if (this._position == St.Side.TOP) |
|
+ Main.overview._overview.insert_child_at_index(this._dashSpacer, 0); |
|
+ else if (this._position == St.Side.BOTTOM) |
|
+ Main.overview._overview.insert_child_at_index(this._dashSpacer, -1); |
|
+ |
|
+ // Add dash container actor and the container to the Chrome. |
|
+ this.actor.set_child(this._slider.actor); |
|
+ this._slider.add_child(this._box); |
|
+ this._box.add_actor(this.dash.actor); |
|
+ |
|
+ // Add aligning container without tracking it for input region |
|
+ Main.uiGroup.add_child(this.actor); |
|
+ |
|
+ if (this._settings.get_boolean('dock-fixed')) { |
|
+ // Note: tracking the fullscreen directly on the slider actor causes some hiccups when fullscreening |
|
+ // windows of certain applications |
|
+ Main.layoutManager._trackActor(this.actor, {affectsInputRegion: false, trackFullscreen: true}); |
|
+ Main.layoutManager._trackActor(this._slider.actor, {affectsStruts: true}); |
|
+ } |
|
+ else |
|
+ Main.layoutManager._trackActor(this._slider.actor); |
|
+ |
|
+ // Set initial position |
|
+ this._resetDepth(); |
|
+ this._resetPosition(); |
|
+ }, |
|
+ |
|
+ _initialize: function() { |
|
+ if (this._paintId > 0) { |
|
+ this.actor.disconnect(this._paintId); |
|
+ this._paintId=0; |
|
+ } |
|
+ |
|
+ // Apply custome css class according to the settings |
|
+ this._themeManager.updateCustomTheme(); |
|
+ |
|
+ // Since Gnome 3.8 dragging an app without having opened the overview before cause the attemp to |
|
+ //animate a null target since some variables are not initialized when the viewSelector is created |
|
+ if (Main.overview.viewSelector._activePage == null) |
|
+ Main.overview.viewSelector._activePage = Main.overview.viewSelector._workspacesPage; |
|
+ |
|
+ this._updateVisibilityMode(); |
|
+ |
|
+ // In case we are already inside the overview when the extension is loaded, |
|
+ // for instance on unlocking the screen if it was locked with the overview open. |
|
+ if (Main.overview.visibleTarget) { |
|
+ this._onOverviewShowing(); |
|
+ this._pageChanged(); |
|
+ } |
|
+ |
|
+ // Setup pressure barrier (GS38+ only) |
|
+ this._updatePressureBarrier(); |
|
+ this._updateBarrier(); |
|
+ |
|
+ // setup dwelling system if pressure barriers are not available |
|
+ this._setupDockDwellIfNeeded(); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ // Disconnect global signals |
|
+ this._signalsHandler.destroy(); |
|
+ // The dash, intellihide and themeManager have global signals as well internally |
|
+ this.dash.destroy(); |
|
+ this._intellihide.destroy(); |
|
+ this._themeManager.destroy(); |
|
+ |
|
+ this._injectionsHandler.destroy(); |
|
+ |
|
+ // Destroy main clutter actor: this should be sufficient removing it and |
|
+ // destroying all its children |
|
+ this.actor.destroy(); |
|
+ |
|
+ // Remove barrier timeout |
|
+ if (this._removeBarrierTimeoutId > 0) |
|
+ Mainloop.source_remove(this._removeBarrierTimeoutId); |
|
+ |
|
+ // Remove existing barrier |
|
+ this._removeBarrier(); |
|
+ |
|
+ // Remove pointer watcher |
|
+ if (this._dockWatch) { |
|
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); |
|
+ this._dockWatch = null; |
|
+ } |
|
+ |
|
+ // Remove the dashSpacer |
|
+ this._dashSpacer.destroy(); |
|
+ |
|
+ // Restore legacyTray position |
|
+ this._resetLegacyTray(); |
|
+ |
|
+ }, |
|
+ |
|
+ _bindSettingsChanges: function() { |
|
+ this._signalsHandler.add([ |
|
+ this._settings, |
|
+ 'changed::scroll-action', |
|
+ Lang.bind(this, function() { |
|
+ this._optionalScrollWorkspaceSwitch(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::dash-max-icon-size', |
|
+ Lang.bind(this, function() { |
|
+ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::icon-size-fixed', |
|
+ Lang.bind(this, function() { |
|
+ this.dash.setIconSize(this._settings.get_int('dash-max-icon-size')); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::show-favorites', |
|
+ Lang.bind(this, function() { |
|
+ this.dash.resetAppIcons(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::show-running', |
|
+ Lang.bind(this, function() { |
|
+ this.dash.resetAppIcons(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::show-apps-at-top', |
|
+ Lang.bind(this, function() { |
|
+ this.dash.resetAppIcons(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::show-show-apps-button', |
|
+ Lang.bind(this, function() { |
|
+ if (this._settings.get_boolean('show-show-apps-button')) |
|
+ this.dash.showShowAppsButton(); |
|
+ else |
|
+ this.dash.hideShowAppsButton(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::dock-fixed', |
|
+ Lang.bind(this, function() { |
|
+ if (this._settings.get_boolean('dock-fixed')) { |
|
+ Main.layoutManager._untrackActor(this.actor); |
|
+ Main.layoutManager._trackActor(this.actor, {affectsInputRegion: false, trackFullscreen: true}); |
|
+ Main.layoutManager._untrackActor(this._slider.actor); |
|
+ Main.layoutManager._trackActor(this._slider.actor, {affectsStruts: true}); |
|
+ } else { |
|
+ Main.layoutManager._untrackActor(this.actor); |
|
+ Main.layoutManager._untrackActor(this._slider.actor); |
|
+ Main.layoutManager._trackActor(this._slider.actor); |
|
+ } |
|
+ |
|
+ this._resetPosition(); |
|
+ |
|
+ // Add or remove barrier depending on if dock-fixed |
|
+ this._updateBarrier(); |
|
+ |
|
+ this._updateVisibilityMode(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::intellihide', |
|
+ Lang.bind(this, this._updateVisibilityMode) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::intellihide-mode', |
|
+ Lang.bind(this, function() { |
|
+ this._intellihide.forceUpdate(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::autohide', |
|
+ Lang.bind(this, function() { |
|
+ this._updateVisibilityMode(); |
|
+ this._updateBarrier(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::autohide-in-fullscreen', |
|
+ Lang.bind(this, this._updateBarrier) |
|
+ ], |
|
+ [ |
|
+ this._settings, |
|
+ 'changed::extend-height', |
|
+ Lang.bind(this, this._resetPosition) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::height-fraction', |
|
+ Lang.bind(this, this._resetPosition) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::require-pressure-to-show', |
|
+ Lang.bind(this, function() { |
|
+ // Remove pointer watcher |
|
+ if (this._dockWatch) { |
|
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch); |
|
+ this._dockWatch = null; |
|
+ } |
|
+ this._setupDockDwellIfNeeded(); |
|
+ this._updateBarrier(); |
|
+ }) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::pressure-threshold', |
|
+ Lang.bind(this, function() { |
|
+ this._updatePressureBarrier(); |
|
+ this._updateBarrier(); |
|
+ }) |
|
+ ]); |
|
+ |
|
+ }, |
|
+ |
|
+ /** |
|
+ * This is call when visibility settings change |
|
+ */ |
|
+ _updateVisibilityMode: function() { |
|
+ if (this._settings.get_boolean('dock-fixed')) { |
|
+ this._fixedIsEnabled = true; |
|
+ this._autohideIsEnabled = false; |
|
+ this._intellihideIsEnabled = false; |
|
+ } |
|
+ else { |
|
+ this._fixedIsEnabled = false; |
|
+ this._autohideIsEnabled = this._settings.get_boolean('autohide') |
|
+ this._intellihideIsEnabled = this._settings.get_boolean('intellihide') |
|
+ } |
|
+ |
|
+ if (this._intellihideIsEnabled) |
|
+ this._intellihide.enable(); |
|
+ else |
|
+ this._intellihide.disable(); |
|
+ |
|
+ this._updateDashVisibility(); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Show/hide dash based on, in order of priority: |
|
+ * overview visibility |
|
+ * fixed mode |
|
+ * intellihide |
|
+ * autohide |
|
+ * overview visibility |
|
+ */ |
|
+ _updateDashVisibility: function() { |
|
+ if (Main.overview.visibleTarget) |
|
+ return; |
|
+ |
|
+ if (this._fixedIsEnabled) { |
|
+ this._removeAnimations(); |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ } |
|
+ else if (this._intellihideIsEnabled) { |
|
+ if (this._intellihide.getOverlapStatus()) { |
|
+ this._ignoreHover = false; |
|
+ // Do not hide if autohide is enabled and mouse is hover |
|
+ if (!this._box.hover || !this._autohideIsEnabled) |
|
+ this._animateOut(this._settings.get_double('animation-time'), 0); |
|
+ } |
|
+ else { |
|
+ this._ignoreHover = true; |
|
+ this._removeAnimations(); |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ } |
|
+ } |
|
+ else { |
|
+ if (this._autohideIsEnabled) { |
|
+ this._ignoreHover = false; |
|
+ global.sync_pointer(); |
|
+ |
|
+ if (this._box.hover) |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ else |
|
+ this._animateOut(this._settings.get_double('animation-time'), 0); |
|
+ } |
|
+ else |
|
+ this._animateOut(this._settings.get_double('animation-time'), 0); |
|
+ } |
|
+ }, |
|
+ |
|
+ _onOverviewShowing: function() { |
|
+ this._ignoreHover = true; |
|
+ this._intellihide.disable(); |
|
+ this._removeAnimations(); |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ }, |
|
+ |
|
+ _onOverviewHiding: function() { |
|
+ this._ignoreHover = false; |
|
+ this._intellihide.enable(); |
|
+ this._updateDashVisibility(); |
|
+ }, |
|
+ |
|
+ _hoverChanged: function() { |
|
+ if (!this._ignoreHover) { |
|
+ // Skip if dock is not in autohide mode for instance because it is shown |
|
+ // by intellihide. |
|
+ if (this._autohideIsEnabled) { |
|
+ if (this._box.hover) |
|
+ this._show(); |
|
+ else |
|
+ this._hide(); |
|
+ } |
|
+ } |
|
+ }, |
|
+ |
|
+ getDockState: function() { |
|
+ return this._dockState; |
|
+ }, |
|
+ |
|
+ _show: function() { |
|
+ if ((this._dockState == State.HIDDEN) || (this._dockState == State.HIDING)) { |
|
+ if (this._dockState == State.HIDING) |
|
+ // suppress all potential queued hiding animations - i.e. added to Tweener but not started, |
|
+ // always give priority to show |
|
+ this._removeAnimations(); |
|
+ |
|
+ this.emit('showing'); |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ } |
|
+ }, |
|
+ |
|
+ _hide: function() { |
|
+ // If no hiding animation is running or queued |
|
+ if ((this._dockState == State.SHOWN) || (this._dockState == State.SHOWING)) { |
|
+ let delay; |
|
+ |
|
+ if (this._dockState == State.SHOWING) |
|
+ //if a show already started, let it finish; queue hide without removing the show. |
|
+ // to obtain this I increase the delay to avoid the overlap and interference |
|
+ // between the animations |
|
+ delay = this._settings.get_double('hide-delay') + this._settings.get_double('animation-time'); |
|
+ else |
|
+ delay = this._settings.get_double('hide-delay'); |
|
+ |
|
+ this.emit('hiding'); |
|
+ this._animateOut(this._settings.get_double('animation-time'), delay); |
|
+ } |
|
+ }, |
|
+ |
|
+ _animateIn: function(time, delay) { |
|
+ this._dockState = State.SHOWING; |
|
+ |
|
+ Tweener.addTween(this._slider, { |
|
+ slidex: 1, |
|
+ time: time, |
|
+ delay: delay, |
|
+ transition: 'easeOutQuad', |
|
+ onComplete: Lang.bind(this, function() { |
|
+ this._dockState = State.SHOWN; |
|
+ // Remove barrier so that mouse pointer is released and can access monitors on other side of dock |
|
+ // NOTE: Delay needed to keep mouse from moving past dock and re-hiding dock immediately. This |
|
+ // gives users an opportunity to hover over the dock |
|
+ if (this._removeBarrierTimeoutId > 0) |
|
+ Mainloop.source_remove(this._removeBarrierTimeoutId); |
|
+ this._removeBarrierTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, this._removeBarrier)); |
|
+ }) |
|
+ }); |
|
+ }, |
|
+ |
|
+ _animateOut: function(time, delay) { |
|
+ this._dockState = State.HIDING; |
|
+ Tweener.addTween(this._slider, { |
|
+ slidex: 0, |
|
+ time: time, |
|
+ delay: delay , |
|
+ transition: 'easeOutQuad', |
|
+ onComplete: Lang.bind(this, function() { |
|
+ this._dockState = State.HIDDEN; |
|
+ // Remove queued barried removal if any |
|
+ if (this._removeBarrierTimeoutId > 0) |
|
+ Mainloop.source_remove(this._removeBarrierTimeoutId); |
|
+ this._updateBarrier(); |
|
+ }) |
|
+ }); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Dwelling system based on the GNOME Shell 3.14 messageTray code. |
|
+ */ |
|
+ _setupDockDwellIfNeeded: function() { |
|
+ // If we don't have extended barrier features, then we need |
|
+ // to support the old tray dwelling mechanism. |
|
+ if (!global.display.supports_extended_barriers() || !this._settings.get_boolean('require-pressure-to-show')) { |
|
+ let pointerWatcher = PointerWatcher.getPointerWatcher(); |
|
+ this._dockWatch = pointerWatcher.addWatch(DOCK_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkDockDwell)); |
|
+ this._dockDwelling = false; |
|
+ this._dockDwellUserTime = 0; |
|
+ } |
|
+ }, |
|
+ |
|
+ _checkDockDwell: function(x, y) { |
|
+ |
|
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index) |
|
+ let shouldDwell; |
|
+ // Check for the correct screen edge, extending the sensitive area to the whole workarea, |
|
+ // minus 1 px to avoid conflicting with other active corners. |
|
+ if (this._position == St.Side.LEFT) |
|
+ shouldDwell = (x == this._monitor.x) && (y > workArea.y) && (y < workArea.y + workArea.height); |
|
+ else if (this._position == St.Side.RIGHT) |
|
+ shouldDwell = (x == this._monitor.x + this._monitor.width - 1) && (y > workArea.y) && (y < workArea.y + workArea.height); |
|
+ else if (this._position == St.Side.TOP) |
|
+ shouldDwell = (y == this._monitor.y) && (x > workArea.x) && (x < workArea.x + workArea.width); |
|
+ else if (this._position == St.Side.BOTTOM) |
|
+ shouldDwell = (y == this._monitor.y + this._monitor.height - 1) && (x > workArea.x) && (x < workArea.x + workArea.width); |
|
+ |
|
+ if (shouldDwell) { |
|
+ // We only set up dwell timeout when the user is not hovering over the dock |
|
+ // already (!this._box.hover). |
|
+ // The _dockDwelling variable is used so that we only try to |
|
+ // fire off one dock dwell - if it fails (because, say, the user has the mouse down), |
|
+ // we don't try again until the user moves the mouse up and down again. |
|
+ if (!this._dockDwelling && !this._box.hover && (this._dockDwellTimeoutId == 0)) { |
|
+ // Save the interaction timestamp so we can detect user input |
|
+ let focusWindow = global.display.focus_window; |
|
+ this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0; |
|
+ |
|
+ this._dockDwellTimeoutId = Mainloop.timeout_add(this._settings.get_double('show-delay') * 1000, |
|
+ Lang.bind(this, this._dockDwellTimeout)); |
|
+ GLib.Source.set_name_by_id(this._dockDwellTimeoutId, '[dash-to-dock] this._dockDwellTimeout'); |
|
+ } |
|
+ this._dockDwelling = true; |
|
+ } |
|
+ else { |
|
+ this._cancelDockDwell(); |
|
+ this._dockDwelling = false; |
|
+ } |
|
+ }, |
|
+ |
|
+ _cancelDockDwell: function() { |
|
+ if (this._dockDwellTimeoutId != 0) { |
|
+ Mainloop.source_remove(this._dockDwellTimeoutId); |
|
+ this._dockDwellTimeoutId = 0; |
|
+ } |
|
+ }, |
|
+ |
|
+ _dockDwellTimeout: function() { |
|
+ this._dockDwellTimeoutId = 0; |
|
+ |
|
+ if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen) |
|
+ return GLib.SOURCE_REMOVE; |
|
+ |
|
+ // We don't want to open the tray when a modal dialog |
|
+ // is up, so we check the modal count for that. When we are in the |
|
+ // overview we have to take the overview's modal push into account |
|
+ if (Main.modalCount > (Main.overview.visible ? 1 : 0)) |
|
+ return GLib.SOURCE_REMOVE; |
|
+ |
|
+ // If the user interacted with the focus window since we started the tray |
|
+ // dwell (by clicking or typing), don't activate the message tray |
|
+ let focusWindow = global.display.focus_window; |
|
+ let currentUserTime = focusWindow ? focusWindow.user_time : 0; |
|
+ if (currentUserTime != this._dockDwellUserTime) |
|
+ return GLib.SOURCE_REMOVE; |
|
+ |
|
+ // Reuse the pressure version function, the logic is the same |
|
+ this._onPressureSensed(); |
|
+ return GLib.SOURCE_REMOVE; |
|
+ }, |
|
+ |
|
+ _updatePressureBarrier: function() { |
|
+ this._canUsePressure = global.display.supports_extended_barriers(); |
|
+ let pressureThreshold = this._settings.get_double('pressure-threshold'); |
|
+ |
|
+ // Remove existing pressure barrier |
|
+ if (this._pressureBarrier) { |
|
+ this._pressureBarrier.destroy(); |
|
+ this._pressureBarrier = null; |
|
+ } |
|
+ |
|
+ if (this._barrier) { |
|
+ this._barrier.destroy(); |
|
+ this._barrier = null; |
|
+ } |
|
+ |
|
+ // Create new pressure barrier based on pressure threshold setting |
|
+ if (this._canUsePressure) { |
|
+ this._pressureBarrier = new Layout.PressureBarrier(pressureThreshold, this._settings.get_double('show-delay')*1000, |
|
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW); |
|
+ this._pressureBarrier.connect('trigger', Lang.bind(this, function(barrier) { |
|
+ if (!this._settings.get_boolean('autohide-in-fullscreen') && this._monitor.inFullscreen) |
|
+ return; |
|
+ this._onPressureSensed(); |
|
+ })); |
|
+ } |
|
+ }, |
|
+ |
|
+ /** |
|
+ * handler for mouse pressure sensed |
|
+ */ |
|
+ _onPressureSensed: function() { |
|
+ if (Main.overview.visibleTarget) |
|
+ return; |
|
+ |
|
+ // In case the mouse move away from the dock area before hovering it, in such case the leave event |
|
+ // would never be triggered and the dock would stay visible forever. |
|
+ let triggerTimeoutId = Mainloop.timeout_add(250, Lang.bind(this, function() { |
|
+ triggerTimeoutId = 0; |
|
+ |
|
+ let [x, y, mods] = global.get_pointer(); |
|
+ let shouldHide = true; |
|
+ switch (this._position) { |
|
+ case St.Side.LEFT: |
|
+ if (x <= this.staticBox.x2 && |
|
+ x >= this._monitor.x && |
|
+ y >= this._monitor.y && |
|
+ y <= this._monitor.y + this._monitor.height) { |
|
+ shouldHide = false; |
|
+ } |
|
+ break; |
|
+ case St.Side.RIGHT: |
|
+ if (x >= this.staticBox.x1 && |
|
+ x <= this._monitor.x + this._monitor.width && |
|
+ y >= this._monitor.y && |
|
+ y <= this._monitor.y + this._monitor.height) { |
|
+ shouldHide = false; |
|
+ } |
|
+ break; |
|
+ case St.Side.TOP: |
|
+ if (x >= this._monitor.x && |
|
+ x <= this._monitor.x + this._monitor.width && |
|
+ y <= this.staticBox.y2 && |
|
+ y >= this._monitor.y) { |
|
+ shouldHide = false; |
|
+ } |
|
+ break; |
|
+ case St.Side.BOTTOM: |
|
+ if (x >= this._monitor.x && |
|
+ x <= this._monitor.x + this._monitor.width && |
|
+ y >= this.staticBox.y1 && |
|
+ y <= this._monitor.y + this._monitor.height) { |
|
+ shouldHide = false; |
|
+ } |
|
+ } |
|
+ if (shouldHide) { |
|
+ this._hoverChanged(); |
|
+ return GLib.SOURCE_REMOVE; |
|
+ } |
|
+ else { |
|
+ return GLib.SOURCE_CONTINUE; |
|
+ } |
|
+ |
|
+ })); |
|
+ |
|
+ this._show(); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Remove pressure barrier |
|
+ */ |
|
+ _removeBarrier: function() { |
|
+ if (this._barrier) { |
|
+ if (this._pressureBarrier) |
|
+ this._pressureBarrier.removeBarrier(this._barrier); |
|
+ this._barrier.destroy(); |
|
+ this._barrier = null; |
|
+ } |
|
+ this._removeBarrierTimeoutId = 0; |
|
+ return false; |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Update pressure barrier size |
|
+ */ |
|
+ _updateBarrier: function() { |
|
+ // Remove existing barrier |
|
+ this._removeBarrier(); |
|
+ |
|
+ // The barrier needs to be removed in fullscreen with autohide disabled, otherwise the mouse can |
|
+ // get trapped on monitor. |
|
+ if (this._monitor.inFullscreen && !this._settings.get_boolean('autohide-in-fullscreen')) |
|
+ return |
|
+ |
|
+ // Manually reset pressure barrier |
|
+ // This is necessary because we remove the pressure barrier when it is triggered to show the dock |
|
+ if (this._pressureBarrier) { |
|
+ this._pressureBarrier._reset(); |
|
+ this._pressureBarrier._isTriggered = false; |
|
+ } |
|
+ |
|
+ // Create new barrier |
|
+ // The barrier extends to the whole workarea, minus 1 px to avoid conflicting with other active corners |
|
+ // Note: dash in fixed position doesn't use pressure barrier. |
|
+ if (this._canUsePressure && this._autohideIsEnabled && this._settings.get_boolean('require-pressure-to-show')) { |
|
+ let x1, x2, y1, y2, direction; |
|
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index) |
|
+ |
|
+ if (this._position == St.Side.LEFT) { |
|
+ x1 = this._monitor.x + 1; |
|
+ x2 = x1; |
|
+ y1 = workArea.y + 1; |
|
+ y2 = workArea.y + workArea.height - 1; |
|
+ direction = Meta.BarrierDirection.POSITIVE_X; |
|
+ } |
|
+ else if (this._position == St.Side.RIGHT) { |
|
+ x1 = this._monitor.x + this._monitor.width - 1; |
|
+ x2 = x1; |
|
+ y1 = workArea.y + 1; |
|
+ y2 = workArea.y + workArea.height - 1; |
|
+ direction = Meta.BarrierDirection.NEGATIVE_X; |
|
+ } |
|
+ else if (this._position == St.Side.TOP) { |
|
+ x1 = workArea.x + 1; |
|
+ x2 = workArea.x + workArea.width - 1; |
|
+ y1 = this._monitor.y; |
|
+ y2 = y1; |
|
+ direction = Meta.BarrierDirection.POSITIVE_Y; |
|
+ } |
|
+ else if (this._position == St.Side.BOTTOM) { |
|
+ x1 = workArea.x + 1; |
|
+ x2 = workArea.x + workArea.width - 1; |
|
+ y1 = this._monitor.y + this._monitor.height; |
|
+ y2 = y1; |
|
+ direction = Meta.BarrierDirection.NEGATIVE_Y; |
|
+ } |
|
+ |
|
+ this._barrier = new Meta.Barrier({ |
|
+ display: global.display, |
|
+ x1: x1, |
|
+ x2: x2, |
|
+ y1: y1, |
|
+ y2: y2, |
|
+ directions: direction |
|
+ }); |
|
+ if (this._pressureBarrier) |
|
+ this._pressureBarrier.addBarrier(this._barrier); |
|
+ } |
|
+ }, |
|
+ |
|
+ _isPrimaryMonitor: function() { |
|
+ return (this._monitorIndex == Main.layoutManager.primaryIndex); |
|
+ }, |
|
+ |
|
+ _resetPosition: function() { |
|
+ // Ensure variables linked to settings are updated. |
|
+ this._updateVisibilityMode(); |
|
+ |
|
+ let extendHeight = this._settings.get_boolean('extend-height'); |
|
+ |
|
+ // Note: do not use the workarea coordinates in the direction on which the dock is placed, |
|
+ // to avoid a loop [position change -> workArea change -> position change] with |
|
+ // fixed dock. |
|
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex); |
|
+ |
|
+ // Reserve space for the dash on the overview |
|
+ // if the dock is on the primary monitor |
|
+ if (this._isPrimaryMonitor()) |
|
+ this._dashSpacer.show(); |
|
+ else |
|
+ // No space is required in the overview of the dash |
|
+ this._dashSpacer.hide(); |
|
+ |
|
+ let fraction = this._settings.get_double('height-fraction'); |
|
+ |
|
+ if (extendHeight) |
|
+ fraction = 1; |
|
+ else if ((fraction < 0) || (fraction > 1)) |
|
+ fraction = 0.95; |
|
+ |
|
+ let anchor_point; |
|
+ |
|
+ if (this._isHorizontal) { |
|
+ this.actor.width = Math.round( fraction * workArea.width); |
|
+ |
|
+ let pos_y; |
|
+ if (this._position == St.Side.BOTTOM) { |
|
+ pos_y = this._monitor.y + this._monitor.height; |
|
+ anchor_point = Clutter.Gravity.SOUTH_WEST; |
|
+ } |
|
+ else { |
|
+ pos_y = this._monitor.y; |
|
+ anchor_point = Clutter.Gravity.NORTH_WEST; |
|
+ } |
|
+ |
|
+ this.actor.move_anchor_point_from_gravity(anchor_point); |
|
+ this.actor.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width); |
|
+ this.actor.y = pos_y; |
|
+ |
|
+ if (extendHeight) { |
|
+ this.dash._container.set_width(this.actor.width); |
|
+ this.actor.add_style_class_name('extended'); |
|
+ } |
|
+ else { |
|
+ this.dash._container.set_width(-1); |
|
+ this.actor.remove_style_class_name('extended'); |
|
+ } |
|
+ } |
|
+ else { |
|
+ this.actor.height = Math.round(fraction * workArea.height); |
|
+ |
|
+ let pos_x; |
|
+ if (this._position == St.Side.RIGHT) { |
|
+ pos_x = this._monitor.x + this._monitor.width; |
|
+ anchor_point = Clutter.Gravity.NORTH_EAST; |
|
+ } |
|
+ else { |
|
+ pos_x = this._monitor.x; |
|
+ anchor_point = Clutter.Gravity.NORTH_WEST; |
|
+ } |
|
+ |
|
+ this.actor.move_anchor_point_from_gravity(anchor_point); |
|
+ this.actor.x = pos_x; |
|
+ this.actor.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height); |
|
+ |
|
+ if (extendHeight) { |
|
+ this.dash._container.set_height(this.actor.height); |
|
+ this.actor.add_style_class_name('extended'); |
|
+ } |
|
+ else { |
|
+ this.dash._container.set_height(-1); |
|
+ this.actor.remove_style_class_name('extended'); |
|
+ } |
|
+ } |
|
+ |
|
+ this._y0 = this.actor.y; |
|
+ |
|
+ this._adjustLegacyTray(); |
|
+ }, |
|
+ |
|
+ // Set the dash at the correct depth in z |
|
+ _resetDepth: function() { |
|
+ // Keep the dash below the modalDialogGroup and the legacyTray |
|
+ if (Main.legacyTray && Main.legacyTray.actor) |
|
+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.legacyTray.actor); |
|
+ else |
|
+ Main.layoutManager.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); |
|
+ }, |
|
+ |
|
+ _adjustLegacyTray: function() { |
|
+ // The legacyTray has been removed in GNOME Shell 3.26. |
|
+ // Once we drop support for previous releases this fuction can be dropped too. |
|
+ if (!Main.legacyTray) |
|
+ return; |
|
+ |
|
+ let use_work_area = true; |
|
+ |
|
+ if (this._fixedIsEnabled && !this._settings.get_boolean('extend-height') |
|
+ && this._isPrimaryMonitor() |
|
+ && ((this._position == St.Side.BOTTOM) || (this._position == St.Side.LEFT))) |
|
+ use_work_area = false; |
|
+ |
|
+ Main.legacyTray.actor.clear_constraints(); |
|
+ let constraint = new Layout.MonitorConstraint({ |
|
+ primary: true, |
|
+ work_area: use_work_area |
|
+ }); |
|
+ Main.legacyTray.actor.add_constraint(constraint); |
|
+ }, |
|
+ |
|
+ _resetLegacyTray: function() { |
|
+ // The legacyTray has been removed in GNOME Shell 3.26. |
|
+ // Once we drop support for previous releases this fuction can be dropped too. |
|
+ if (!Main.legacyTray) |
|
+ return; |
|
+ Main.legacyTray.actor.clear_constraints(); |
|
+ let constraint = new Layout.MonitorConstraint({ |
|
+ primary: true, |
|
+ work_area: true |
|
+ }); |
|
+ Main.legacyTray.actor.add_constraint(constraint); |
|
+ }, |
|
+ |
|
+ _updateStaticBox: function() { |
|
+ this.staticBox.init_rect( |
|
+ this.actor.x + this._slider.actor.x - (this._position == St.Side.RIGHT ? this._box.width : 0), |
|
+ this.actor.y + this._slider.actor.y - (this._position == St.Side.BOTTOM ? this._box.height : 0), |
|
+ this._box.width, |
|
+ this._box.height |
|
+ ); |
|
+ |
|
+ this._intellihide.updateTargetBox(this.staticBox); |
|
+ }, |
|
+ |
|
+ _removeAnimations: function() { |
|
+ Tweener.removeTweens(this._slider); |
|
+ }, |
|
+ |
|
+ _onDragStart: function() { |
|
+ // The dash need to be above the top_window_group, otherwise it doesn't |
|
+ // accept dnd of app icons when not in overiew mode. |
|
+ Main.layoutManager.uiGroup.set_child_above_sibling(this.actor, global.top_window_group); |
|
+ this._oldignoreHover = this._ignoreHover; |
|
+ this._ignoreHover = true; |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ }, |
|
+ |
|
+ _onDragEnd: function() { |
|
+ // Restore drag default dash stack order |
|
+ this._resetDepth(); |
|
+ if (this._oldignoreHover !== null) |
|
+ this._ignoreHover = this._oldignoreHover; |
|
+ this._oldignoreHover = null; |
|
+ this._box.sync_hover(); |
|
+ if (Main.overview._shown) |
|
+ this._pageChanged(); |
|
+ }, |
|
+ |
|
+ _pageChanged: function() { |
|
+ let activePage = Main.overview.viewSelector.getActivePage(); |
|
+ let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS || |
|
+ activePage == ViewSelector.ViewPage.APPS); |
|
+ |
|
+ if (dashVisible) |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ else |
|
+ this._animateOut(this._settings.get_double('animation-time'), 0); |
|
+ }, |
|
+ |
|
+ _onPageEmpty: function() { |
|
+ /* The dash spacer is required only in the WINDOWS view if in the default position. |
|
+ * The 'page-empty' signal is emitted in between a change of view, |
|
+ * signalling the spacer can be added and removed without visible effect, |
|
+ * as it's done for the upstream dashSpacer. |
|
+ * |
|
+ * Moreover, hiding the spacer ensure the appGrid allocaton is triggered. |
|
+ * This matter as the appview spring animation is triggered by to first reallocaton of the appGrid, |
|
+ * (See appDisplay.js, line 202 on GNOME Shell 3.14: |
|
+ * this._grid.actor.connect('notify::allocation', ...) |
|
+ * which in turn seems to be triggered by changes in the other actors in the overview. |
|
+ * Normally, as far as I could understand, either the dashSpacer being hidden or the workspacesThumbnails |
|
+ * sliding out would trigger the allocation. However, with no stock dash |
|
+ * and no thumbnails, which happen if the user configured only 1 and static workspace, |
|
+ * the animation out of icons is not played. |
|
+ */ |
|
+ |
|
+ let activePage = Main.overview.viewSelector.getActivePage(); |
|
+ this._dashSpacer.visible = (this._isHorizontal || activePage == ViewSelector.ViewPage.WINDOWS); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Show dock and give key focus to it |
|
+ */ |
|
+ _onAccessibilityFocus: function() { |
|
+ this._box.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); |
|
+ this._animateIn(this._settings.get_double('animation-time'), 0); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Keep ShowAppsButton status in sync with the overview status |
|
+ */ |
|
+ _syncShowAppsButtonToggled: function() { |
|
+ let status = Main.overview.viewSelector._showAppsButton.checked; |
|
+ if (this.dash.showAppsButton.checked !== status) |
|
+ this.dash.showAppsButton.checked = status; |
|
+ }, |
|
+ |
|
+ // Optional features to be enabled only for the main Dock |
|
+ _enableExtraFeatures: function() { |
|
+ // Restore dash accessibility |
|
+ Main.ctrlAltTabManager.addGroup( |
|
+ this.dash.actor, _('Dash'), 'user-bookmarks-symbolic', |
|
+ {focusCallback: Lang.bind(this, this._onAccessibilityFocus)}); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Switch workspace by scrolling over the dock |
|
+ */ |
|
+ _optionalScrollWorkspaceSwitch: function() { |
|
+ let label = 'optionalScrollWorkspaceSwitch'; |
|
+ |
|
+ function isEnabled() { |
|
+ return this._settings.get_enum('scroll-action') === scrollAction.SWITCH_WORKSPACE; |
|
+ } |
|
+ |
|
+ this._settings.connect('changed::scroll-action', Lang.bind(this, function() { |
|
+ if (Lang.bind(this, isEnabled)()) |
|
+ Lang.bind(this, enable)(); |
|
+ else |
|
+ Lang.bind(this, disable)(); |
|
+ })); |
|
+ |
|
+ if (Lang.bind(this, isEnabled)()) |
|
+ Lang.bind(this, enable)(); |
|
+ |
|
+ function enable() { |
|
+ this._signalsHandler.removeWithLabel(label); |
|
+ |
|
+ this._signalsHandler.addWithLabel(label, [ |
|
+ this._box, |
|
+ 'scroll-event', |
|
+ Lang.bind(this, onScrollEvent) |
|
+ ]); |
|
+ |
|
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0; |
|
+ } |
|
+ |
|
+ function disable() { |
|
+ this._signalsHandler.removeWithLabel(label); |
|
+ |
|
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId > 0) { |
|
+ Mainloop.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId); |
|
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0; |
|
+ } |
|
+ } |
|
+ |
|
+ // This was inspired to desktop-scroller@obsidien.github.com |
|
+ function onScrollEvent(actor, event) { |
|
+ // When in overview change workscape only in windows view |
|
+ if (Main.overview.visible && Main.overview.viewSelector.getActivePage() !== ViewSelector.ViewPage.WINDOWS) |
|
+ return false; |
|
+ |
|
+ let activeWs = global.screen.get_active_workspace(); |
|
+ let direction = null; |
|
+ |
|
+ switch (event.get_scroll_direction()) { |
|
+ case Clutter.ScrollDirection.UP: |
|
+ direction = Meta.MotionDirection.UP; |
|
+ break; |
|
+ case Clutter.ScrollDirection.DOWN: |
|
+ direction = Meta.MotionDirection.DOWN; |
|
+ break; |
|
+ case Clutter.ScrollDirection.SMOOTH: |
|
+ let [dx, dy] = event.get_scroll_delta(); |
|
+ if (dy < 0) |
|
+ direction = Meta.MotionDirection.UP; |
|
+ else if (dy > 0) |
|
+ direction = Meta.MotionDirection.DOWN; |
|
+ break; |
|
+ } |
|
+ |
|
+ if (direction !== null) { |
|
+ // Prevent scroll events from triggering too many workspace switches |
|
+ // by adding a 250ms deadtime between each scroll event. |
|
+ // Usefull on laptops when using a touchpad. |
|
+ |
|
+ // During the deadtime do nothing |
|
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId > 0) |
|
+ return false; |
|
+ else |
|
+ this._optionalScrollWorkspaceSwitchDeadTimeId = Mainloop.timeout_add(250, Lang.bind(this, function() { |
|
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0; |
|
+ })); |
|
+ |
|
+ let ws; |
|
+ |
|
+ ws = activeWs.get_neighbor(direction) |
|
+ |
|
+ if (Main.wm._workspaceSwitcherPopup == null) |
|
+ Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); |
|
+ // Set the actor non reactive, so that it doesn't prevent the |
|
+ // clicks events from reaching the dash actor. I can't see a reason |
|
+ // why it should be reactive. |
|
+ Main.wm._workspaceSwitcherPopup.actor.reactive = false; |
|
+ Main.wm._workspaceSwitcherPopup.connect('destroy', function() { |
|
+ Main.wm._workspaceSwitcherPopup = null; |
|
+ }); |
|
+ |
|
+ // Do not show wokspaceSwithcer in overview |
|
+ if (!Main.overview.visible) |
|
+ Main.wm._workspaceSwitcherPopup.display(direction, ws.index()); |
|
+ Main.wm.actionMoveWorkspace(ws); |
|
+ |
|
+ return true; |
|
+ } |
|
+ else |
|
+ return false; |
|
+ } |
|
+ }, |
|
+ |
|
+ _activateApp: function(appIndex) { |
|
+ let children = this.dash._box.get_children().filter(function(actor) { |
|
+ return actor.child && |
|
+ actor.child._delegate && |
|
+ actor.child._delegate.app; |
|
+ }); |
|
+ |
|
+ // Apps currently in the dash |
|
+ let apps = children.map(function(actor) { |
|
+ return actor.child._delegate; |
|
+ }); |
|
+ |
|
+ // Activate with button = 1, i.e. same as left click |
|
+ let button = 1; |
|
+ if (appIndex < apps.length) |
|
+ apps[appIndex].activate(button); |
|
+ } |
|
+ |
|
+}); |
|
+ |
|
+Signals.addSignalMethods(DockedDash.prototype); |
|
+ |
|
+/* |
|
+ * Handle keybaord shortcuts |
|
+ */ |
|
+const KeyboardShortcuts = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.KeyboardShortcuts', |
|
+ |
|
+ _numHotkeys: 10, |
|
+ |
|
+ _init: function(settings, allDocks){ |
|
+ |
|
+ this._settings = settings; |
|
+ this._allDocks = allDocks; |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ |
|
+ this._hotKeysEnabled = false; |
|
+ if (this._settings.get_boolean('hot-keys')) |
|
+ this._enableHotKeys(); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ this._settings, |
|
+ 'changed::hot-keys', |
|
+ Lang.bind(this, function() { |
|
+ if (this._settings.get_boolean('hot-keys')) |
|
+ Lang.bind(this, this._enableHotKeys)(); |
|
+ else |
|
+ Lang.bind(this, this._disableHotKeys)(); |
|
+ }) |
|
+ ]); |
|
+ |
|
+ this._optionalNumberOverlay(); |
|
+ }, |
|
+ |
|
+ destroy: function (){ |
|
+ // Remove keybindings |
|
+ this._disableHotKeys(); |
|
+ this._disableExtraShortcut(); |
|
+ this._signalsHandler.destroy(); |
|
+ }, |
|
+ |
|
+ _enableHotKeys: function() { |
|
+ if (this._hotKeysEnabled) |
|
+ return; |
|
+ |
|
+ // Setup keyboard bindings for dash elements |
|
+ let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-']; |
|
+ keys.forEach( function(key) { |
|
+ for (let i = 0; i < this._numHotkeys; i++) { |
|
+ let appNum = i; |
|
+ Main.wm.addKeybinding(key + (i + 1), this._settings, |
|
+ Meta.KeyBindingFlags.NONE, |
|
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, |
|
+ Lang.bind(this, function() { |
|
+ this._allDocks[0]._activateApp(appNum); |
|
+ this._showOverlay(); |
|
+ })); |
|
+ } |
|
+ }, this); |
|
+ |
|
+ this._hotKeysEnabled = true; |
|
+ }, |
|
+ |
|
+ _disableHotKeys: function() { |
|
+ if (!this._hotKeysEnabled) |
|
+ return; |
|
+ |
|
+ let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-']; |
|
+ keys.forEach( function(key) { |
|
+ for (let i = 0; i < this._numHotkeys; i++) |
|
+ Main.wm.removeKeybinding(key + (i + 1)); |
|
+ }, this); |
|
+ |
|
+ this._hotKeysEnabled = false; |
|
+ }, |
|
+ |
|
+ _optionalNumberOverlay: function() { |
|
+ this._shortcutIsSet = false; |
|
+ // Enable extra shortcut if either 'overlay' or 'show-dock' are true |
|
+ if (this._settings.get_boolean('hot-keys') && |
|
+ (this._settings.get_boolean('hotkeys-overlay') || this._settings.get_boolean('hotkeys-show-dock'))) |
|
+ this._enableExtraShortcut(); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ this._settings, |
|
+ 'changed::hot-keys', |
|
+ Lang.bind(this, this._checkHotkeysOptions) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::hotkeys-overlay', |
|
+ Lang.bind(this, this._checkHotkeysOptions) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::hotkeys-show-dock', |
|
+ Lang.bind(this, this._checkHotkeysOptions) |
|
+ ]); |
|
+ }, |
|
+ |
|
+ _checkHotkeysOptions: function() { |
|
+ if (this._settings.get_boolean('hot-keys') && |
|
+ (this._settings.get_boolean('hotkeys-overlay') || this._settings.get_boolean('hotkeys-show-dock'))) |
|
+ this._enableExtraShortcut(); |
|
+ else |
|
+ this._disableExtraShortcut(); |
|
+ }, |
|
+ |
|
+ _enableExtraShortcut: function() { |
|
+ if (!this._shortcutIsSet) { |
|
+ Main.wm.addKeybinding('shortcut', this._settings, |
|
+ Meta.KeyBindingFlags.NONE, |
|
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, |
|
+ Lang.bind(this, this._showOverlay)); |
|
+ this._shortcutIsSet = true; |
|
+ } |
|
+ }, |
|
+ |
|
+ _disableExtraShortcut: function() { |
|
+ if (this._shortcutIsSet) { |
|
+ Main.wm.removeKeybinding('shortcut'); |
|
+ this._shortcutIsSet = false; |
|
+ } |
|
+ }, |
|
+ |
|
+ _showOverlay: function() { |
|
+ for (let i = 0; i < this._allDocks.length; i++) { |
|
+ let dock = this._allDocks[i]; |
|
+ if (dock._settings.get_boolean('hotkeys-overlay')) |
|
+ dock.dash.toggleNumberOverlay(true); |
|
+ |
|
+ // Restart the counting if the shortcut is pressed again |
|
+ if (dock._numberOverlayTimeoutId) { |
|
+ Mainloop.source_remove(dock._numberOverlayTimeoutId); |
|
+ dock._numberOverlayTimeoutId = 0; |
|
+ } |
|
+ |
|
+ // Hide the overlay/dock after the timeout |
|
+ let timeout = dock._settings.get_double('shortcut-timeout') * 1000; |
|
+ dock._numberOverlayTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(dock, function() { |
|
+ dock._numberOverlayTimeoutId = 0; |
|
+ dock.dash.toggleNumberOverlay(false); |
|
+ // Hide the dock again if necessary |
|
+ dock._updateDashVisibility(); |
|
+ })); |
|
+ |
|
+ // Show the dock if it is hidden |
|
+ if (dock._settings.get_boolean('hotkeys-show-dock')) { |
|
+ let showDock = (dock._intellihideIsEnabled || dock._autohideIsEnabled); |
|
+ if (showDock) |
|
+ dock._show(); |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+}); |
|
+ |
|
+/** |
|
+ * Isolate overview to open new windows for inactive apps |
|
+ * Note: the future implementaion is not fully contained here. Some bits are around in other methods of other classes. |
|
+ * This class just take care of enabling/disabling the option. |
|
+ */ |
|
+const WorkspaceIsolation = new Lang.Class({ |
|
+ |
|
+ Name: 'DashToDock.WorkspaceIsolation', |
|
+ |
|
+ _init: function(settings, allDocks) { |
|
+ |
|
+ this._settings = settings; |
|
+ this._allDocks = allDocks; |
|
+ |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ this._injectionsHandler = new Utils.InjectionsHandler(); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ this._settings, |
|
+ 'changed::isolate-workspaces', |
|
+ Lang.bind(this, function() { |
|
+ this._allDocks.forEach(function(dock) { |
|
+ dock.dash.resetAppIcons(); |
|
+ }); |
|
+ if (this._settings.get_boolean('isolate-workspaces') || |
|
+ this._settings.get_boolean('isolate-monitors')) |
|
+ Lang.bind(this, this._enable)(); |
|
+ else |
|
+ Lang.bind(this, this._disable)(); |
|
+ }) |
|
+ ],[ |
|
+ this._settings, |
|
+ 'changed::isolate-monitors', |
|
+ Lang.bind(this, function() { |
|
+ this._allDocks.forEach(function(dock) { |
|
+ dock.dash.resetAppIcons(); |
|
+ }); |
|
+ if (this._settings.get_boolean('isolate-workspaces') || |
|
+ this._settings.get_boolean('isolate-monitors')) |
|
+ Lang.bind(this, this._enable)(); |
|
+ else |
|
+ Lang.bind(this, this._disable)(); |
|
+ }) |
|
+ ]); |
|
+ |
|
+ if (this._settings.get_boolean('isolate-workspaces') || |
|
+ this._settings.get_boolean('isolate-monitors')) |
|
+ this._enable(); |
|
+ |
|
+ }, |
|
+ |
|
+ _enable: function() { |
|
+ |
|
+ // ensure I never double-register/inject |
|
+ // although it should never happen |
|
+ this._disable(); |
|
+ |
|
+ this._allDocks.forEach(function(dock) { |
|
+ this._signalsHandler.addWithLabel('isolation', [ |
|
+ global.screen, |
|
+ 'restacked', |
|
+ Lang.bind(dock.dash, dock.dash._queueRedisplay) |
|
+ ], [ |
|
+ global.window_manager, |
|
+ 'switch-workspace', |
|
+ Lang.bind(dock.dash, dock.dash._queueRedisplay) |
|
+ ]); |
|
+ |
|
+ // This last signal is only needed for monitor isolation, as windows |
|
+ // might migrate from one monitor to another without triggering 'restacked' |
|
+ if (this._settings.get_boolean('isolate-monitors')) |
|
+ this._signalsHandler.addWithLabel('isolation', [ |
|
+ global.screen, |
|
+ 'window-entered-monitor', |
|
+ Lang.bind(dock.dash, dock.dash._queueRedisplay) |
|
+ ]); |
|
+ |
|
+ }, this); |
|
+ |
|
+ // here this is the Shell.App |
|
+ function IsolatedOverview() { |
|
+ // These lines take care of Nautilus for icons on Desktop |
|
+ let windows = this.get_windows().filter(function(w) { |
|
+ return w.get_workspace().index() == global.screen.get_active_workspace_index(); |
|
+ }); |
|
+ if (windows.length == 1) |
|
+ if (windows[0].skip_taskbar) |
|
+ return this.open_new_window(-1); |
|
+ |
|
+ if (this.is_on_workspace(global.screen.get_active_workspace())) |
|
+ return Main.activateWindow(windows[0]); |
|
+ return this.open_new_window(-1); |
|
+ } |
|
+ |
|
+ this._injectionsHandler.addWithLabel('isolation', [ |
|
+ Shell.App.prototype, |
|
+ 'activate', |
|
+ IsolatedOverview |
|
+ ]); |
|
+ }, |
|
+ |
|
+ _disable: function () { |
|
+ this._signalsHandler.removeWithLabel('isolation'); |
|
+ this._injectionsHandler.removeWithLabel('isolation'); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this._signalsHandler.destroy(); |
|
+ this._injectionsHandler.destroy(); |
|
+ } |
|
+ |
|
+}); |
|
+ |
|
+ |
|
+var DockManager = new Lang.Class({ |
|
+ Name: 'DashToDock.DockManager', |
|
+ |
|
+ _init: function() { |
|
+ this._remoteModel = new LauncherAPI.LauncherEntryRemoteModel(); |
|
+ this._settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock'); |
|
+ this._oldDash = Main.overview._dash; |
|
+ /* Array of all the docks created */ |
|
+ this._allDocks = []; |
|
+ this._createDocks(); |
|
+ |
|
+ // status variable: true when the overview is shown through the dash |
|
+ // applications button. |
|
+ this._forcedOverview = false; |
|
+ |
|
+ // Connect relevant signals to the toggling function |
|
+ this._bindSettingsChanges(); |
|
+ }, |
|
+ |
|
+ _toggle: function() { |
|
+ this._deleteDocks(); |
|
+ this._createDocks(); |
|
+ this.emit('toggled'); |
|
+ }, |
|
+ |
|
+ _bindSettingsChanges: function() { |
|
+ // Connect relevant signals to the toggling function |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ this._signalsHandler.add([ |
|
+ global.screen, |
|
+ 'monitors-changed', |
|
+ Lang.bind(this, this._toggle) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::multi-monitor', |
|
+ Lang.bind(this, this._toggle) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::preferred-monitor', |
|
+ Lang.bind(this, this._toggle) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::dock-position', |
|
+ Lang.bind(this, this._toggle) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::extend-height', |
|
+ Lang.bind(this, this._adjustPanelCorners) |
|
+ ], [ |
|
+ this._settings, |
|
+ 'changed::dock-fixed', |
|
+ Lang.bind(this, this._adjustPanelCorners) |
|
+ ]); |
|
+ }, |
|
+ |
|
+ _createDocks: function() { |
|
+ |
|
+ this._preferredMonitorIndex = this._settings.get_int('preferred-monitor'); |
|
+ // In case of multi-monitor, we consider the dock on the primary monitor to be the preferred (main) one |
|
+ // regardless of the settings |
|
+ // The dock goes on the primary monitor also if the settings are incosistent (e.g. desired monitor not connected). |
|
+ if (this._settings.get_boolean('multi-monitor') || |
|
+ this._preferredMonitorIndex < 0 || this._preferredMonitorIndex > Main.layoutManager.monitors.length - 1 |
|
+ ) { |
|
+ this._preferredMonitorIndex = Main.layoutManager.primaryIndex; |
|
+ } else { |
|
+ // Gdk and shell monitors numbering differ at least under wayland: |
|
+ // While the primary monitor appears to be always index 0 in Gdk, |
|
+ // the shell can assign a different number (Main.layoutManager.primaryMonitor) |
|
+ // This ensure the indexing in the settings (Gdk) and in the shell are matched, |
|
+ // i.e. that we start counting from the primaryMonitorIndex |
|
+ this._preferredMonitorIndex = (Main.layoutManager.primaryIndex + this._preferredMonitorIndex) % Main.layoutManager.monitors.length ; |
|
+ } |
|
+ |
|
+ // First we create the main Dock, to get the extra features to bind to this one |
|
+ let dock = new DockedDash(this._settings, this._remoteModel, this._preferredMonitorIndex); |
|
+ this._mainShowAppsButton = dock.dash.showAppsButton; |
|
+ this._allDocks.push(dock); |
|
+ |
|
+ // connect app icon into the view selector |
|
+ dock.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); |
|
+ |
|
+ // Make the necessary changes to Main.overview._dash |
|
+ this._prepareMainDash(); |
|
+ |
|
+ // Adjust corners if necessary |
|
+ this._adjustPanelCorners(); |
|
+ |
|
+ if (this._settings.get_boolean('multi-monitor')) { |
|
+ let nMon = Main.layoutManager.monitors.length; |
|
+ for (let iMon = 0; iMon < nMon; iMon++) { |
|
+ if (iMon == this._preferredMonitorIndex) |
|
+ continue; |
|
+ let dock = new DockedDash(this._settings, this._remoteModel, iMon); |
|
+ this._allDocks.push(dock); |
|
+ // connect app icon into the view selector |
|
+ dock.dash.showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); |
|
+ } |
|
+ } |
|
+ |
|
+ // Load optional features. We load *after* the docks are created, since |
|
+ // we need to connect the signals to all dock instances. |
|
+ this._workspaceIsolation = new WorkspaceIsolation(this._settings, this._allDocks); |
|
+ this._keyboardShortcuts = new KeyboardShortcuts(this._settings, this._allDocks); |
|
+ }, |
|
+ |
|
+ _prepareMainDash: function() { |
|
+ // Pretend I'm the dash: meant to make appgrd swarm animation come from the |
|
+ // right position of the appShowButton. |
|
+ Main.overview._dash = this._allDocks[0].dash; |
|
+ |
|
+ // set stored icon size to the new dash |
|
+ Main.overview.dashIconSize = this._allDocks[0].dash.iconSize; |
|
+ |
|
+ // Hide usual Dash |
|
+ Main.overview._controls.dash.actor.hide(); |
|
+ |
|
+ // Also set dash width to 1, so it's almost not taken into account by code |
|
+ // calculaing the reserved space in the overview. The reason to keep it at 1 is |
|
+ // to allow its visibility change to trigger an allocaion of the appGrid which |
|
+ // in turn is triggergin the appsIcon spring animation, required when no other |
|
+ // actors has this effect, i.e in horizontal mode and without the workspaceThumnails |
|
+ // 1 static workspace only) |
|
+ Main.overview._controls.dash.actor.set_width(1); |
|
+ }, |
|
+ |
|
+ _deleteDocks: function() { |
|
+ // Remove extra features |
|
+ this._workspaceIsolation.destroy(); |
|
+ this._keyboardShortcuts.destroy(); |
|
+ |
|
+ // Delete all docks |
|
+ let nDocks = this._allDocks.length; |
|
+ for (let i = nDocks-1; i >= 0; i--) { |
|
+ this._allDocks[i].destroy(); |
|
+ this._allDocks.pop(); |
|
+ } |
|
+ }, |
|
+ |
|
+ _restoreDash: function() { |
|
+ Main.overview._controls.dash.actor.show(); |
|
+ Main.overview._controls.dash.actor.set_width(-1); //reset default dash size |
|
+ // This force the recalculation of the icon size |
|
+ Main.overview._controls.dash._maxHeight = -1; |
|
+ |
|
+ // reset stored icon size to the default dash |
|
+ Main.overview.dashIconSize = Main.overview._controls.dash.iconSize; |
|
+ |
|
+ Main.overview._dash = this._oldDash; |
|
+ }, |
|
+ |
|
+ _onShowAppsButtonToggled: function(button) { |
|
+ // 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 animate = this._settings.get_boolean('animate-show-apps'); |
|
+ let selector = Main.overview.viewSelector; |
|
+ |
|
+ if (selector._showAppsButton.checked !== button.checked) { |
|
+ // find visible view |
|
+ let visibleView; |
|
+ Main.overview.viewSelector.appDisplay._views.every(function(v, index) { |
|
+ if (v.view.actor.visible) { |
|
+ visibleView = index; |
|
+ return false; |
|
+ } |
|
+ else |
|
+ return true; |
|
+ }); |
|
+ |
|
+ if (button.checked) { |
|
+ // force spring animation triggering.By default the animation only |
|
+ // runs if we are already inside the overview. |
|
+ if (!Main.overview._shown) { |
|
+ this._forcedOverview = true; |
|
+ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; |
|
+ let grid = view._grid; |
|
+ if (animate) { |
|
+ // Animate in the the appview, hide the appGrid to avoiud flashing |
|
+ // Go to the appView before entering the overview, skipping the workspaces. |
|
+ // Do this manually avoiding opacity in transitions so that the setting of the opacity |
|
+ // to 0 doesn't get overwritten. |
|
+ Main.overview.viewSelector._activePage.opacity = 0; |
|
+ Main.overview.viewSelector._activePage.hide(); |
|
+ Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage; |
|
+ Main.overview.viewSelector._activePage.show(); |
|
+ grid.actor.opacity = 0; |
|
+ |
|
+ // The animation has to be trigered manually because the AppDisplay.animate |
|
+ // method is waiting for an allocation not happening, as we skip the workspace view |
|
+ // and the appgrid could already be allocated from previous shown. |
|
+ // It has to be triggered after the overview is shown as wrong coordinates are obtained |
|
+ // otherwise. |
|
+ let overviewShownId = Main.overview.connect('shown', Lang.bind(this, function() { |
|
+ Main.overview.disconnect(overviewShownId); |
|
+ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { |
|
+ grid.actor.opacity = 255; |
|
+ grid.animateSpring(IconGrid.AnimationDirection.IN, this._allDocks[0].dash.showAppsButton); |
|
+ })); |
|
+ })); |
|
+ } |
|
+ else { |
|
+ Main.overview.viewSelector._activePage = Main.overview.viewSelector._appsPage; |
|
+ Main.overview.viewSelector._activePage.show(); |
|
+ grid.actor.opacity = 255; |
|
+ } |
|
+ |
|
+ } |
|
+ |
|
+ // Finally show the overview |
|
+ selector._showAppsButton.checked = true; |
|
+ Main.overview.show(); |
|
+ } |
|
+ else { |
|
+ if (this._forcedOverview) { |
|
+ // force exiting overview if needed |
|
+ |
|
+ if (animate) { |
|
+ // Manually trigger springout animation without activating the |
|
+ // workspaceView to avoid the zoomout animation. Hide the appPage |
|
+ // onComplete to avoid ugly flashing of original icons. |
|
+ let view = Main.overview.viewSelector.appDisplay._views[visibleView].view; |
|
+ let grid = view._grid; |
|
+ view.animate(IconGrid.AnimationDirection.OUT, Lang.bind(this, function() { |
|
+ Main.overview.viewSelector._appsPage.hide(); |
|
+ Main.overview.hide(); |
|
+ selector._showAppsButton.checked = false; |
|
+ this._forcedOverview = false; |
|
+ })); |
|
+ } |
|
+ else { |
|
+ Main.overview.hide(); |
|
+ this._forcedOverview = false; |
|
+ } |
|
+ } |
|
+ else { |
|
+ selector._showAppsButton.checked = false; |
|
+ this._forcedOverview = false; |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ // whenever the button is unactivated even if not by the user still reset the |
|
+ // forcedOverview flag |
|
+ if (button.checked == false) |
|
+ this._forcedOverview = false; |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this._signalsHandler.destroy(); |
|
+ this._deleteDocks(); |
|
+ this._revertPanelCorners(); |
|
+ this._restoreDash(); |
|
+ this._remoteModel.destroy(); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Adjust Panel corners |
|
+ */ |
|
+ _adjustPanelCorners: function() { |
|
+ let position = Utils.getPosition(this._settings); |
|
+ let isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM)); |
|
+ let extendHeight = this._settings.get_boolean('extend-height'); |
|
+ let fixedIsEnabled = this._settings.get_boolean('dock-fixed'); |
|
+ let dockOnPrimary = this._settings.get_boolean('multi-monitor') || |
|
+ this._preferredMonitorIndex == Main.layoutManager.primaryIndex; |
|
+ |
|
+ if (!isHorizontal && dockOnPrimary && extendHeight && fixedIsEnabled) { |
|
+ Main.panel._rightCorner.actor.hide(); |
|
+ Main.panel._leftCorner.actor.hide(); |
|
+ } |
|
+ else |
|
+ this._revertPanelCorners(); |
|
+ }, |
|
+ |
|
+ _revertPanelCorners: function() { |
|
+ Main.panel._leftCorner.actor.show(); |
|
+ Main.panel._rightCorner.actor.show(); |
|
+ } |
|
+}); |
|
+Signals.addSignalMethods(DockManager.prototype); |
|
diff --git a/extensions/dash-to-dock/extension.js b/extensions/dash-to-dock/extension.js |
|
new file mode 100644 |
|
index 0000000..97c1dbb |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/extension.js |
|
@@ -0,0 +1,23 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Docking = Me.imports.docking; |
|
+const Convenience = Me.imports.convenience; |
|
+ |
|
+// We declare this with var so it can be accessed by other extensions in |
|
+// GNOME Shell 3.26+ (mozjs52+). |
|
+var dockManager; |
|
+ |
|
+function init() { |
|
+ Convenience.initTranslations('dashtodock'); |
|
+} |
|
+ |
|
+function enable() { |
|
+ dockManager = new Docking.DockManager(); |
|
+} |
|
+ |
|
+function disable() { |
|
+ dockManager.destroy(); |
|
+ |
|
+ dockManager=null; |
|
+} |
|
diff --git a/extensions/dash-to-dock/intellihide.js b/extensions/dash-to-dock/intellihide.js |
|
new file mode 100644 |
|
index 0000000..1fd2699 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/intellihide.js |
|
@@ -0,0 +1,323 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const GLib = imports.gi.GLib; |
|
+const Lang = imports.lang; |
|
+const Mainloop = imports.mainloop; |
|
+const Meta = imports.gi.Meta; |
|
+const Shell = imports.gi.Shell; |
|
+ |
|
+const Main = imports.ui.main; |
|
+const Signals = imports.signals; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Utils = Me.imports.utils; |
|
+ |
|
+// A good compromise between reactivity and efficiency; to be tuned. |
|
+const INTELLIHIDE_CHECK_INTERVAL = 100; |
|
+ |
|
+const OverlapStatus = { |
|
+ UNDEFINED: -1, |
|
+ FALSE: 0, |
|
+ TRUE: 1 |
|
+}; |
|
+ |
|
+const IntellihideMode = { |
|
+ ALL_WINDOWS: 0, |
|
+ FOCUS_APPLICATION_WINDOWS: 1, |
|
+ MAXIMIZED_WINDOWS : 2 |
|
+}; |
|
+ |
|
+// List of windows type taken into account. Order is important (keep the original |
|
+// enum order). |
|
+const handledWindowTypes = [ |
|
+ Meta.WindowType.NORMAL, |
|
+ Meta.WindowType.DOCK, |
|
+ Meta.WindowType.DIALOG, |
|
+ Meta.WindowType.MODAL_DIALOG, |
|
+ Meta.WindowType.TOOLBAR, |
|
+ Meta.WindowType.MENU, |
|
+ Meta.WindowType.UTILITY, |
|
+ Meta.WindowType.SPLASHSCREEN |
|
+]; |
|
+ |
|
+/** |
|
+ * A rough and ugly implementation of the intellihide behaviour. |
|
+ * Intallihide object: emit 'status-changed' signal when the overlap of windows |
|
+ * with the provided targetBoxClutter.ActorBox changes; |
|
+ */ |
|
+var Intellihide = new Lang.Class({ |
|
+ Name: 'DashToDock.Intellihide', |
|
+ |
|
+ _init: function(settings, monitorIndex) { |
|
+ // Load settings |
|
+ this._settings = settings; |
|
+ this._monitorIndex = monitorIndex; |
|
+ |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ this._tracker = Shell.WindowTracker.get_default(); |
|
+ this._focusApp = null; // The application whose window is focused. |
|
+ this._topApp = null; // The application whose window is on top on the monitor with the dock. |
|
+ |
|
+ this._isEnabled = false; |
|
+ this.status = OverlapStatus.UNDEFINED; |
|
+ this._targetBox = null; |
|
+ |
|
+ this._checkOverlapTimeoutContinue = false; |
|
+ this._checkOverlapTimeoutId = 0; |
|
+ |
|
+ this._trackedWindows = new Map(); |
|
+ |
|
+ // Connect global signals |
|
+ this._signalsHandler.add([ |
|
+ // Add signals on windows created from now on |
|
+ global.display, |
|
+ 'window-created', |
|
+ Lang.bind(this, this._windowCreated) |
|
+ ], [ |
|
+ // triggered for instance when the window list order changes, |
|
+ // included when the workspace is switched |
|
+ global.screen, |
|
+ 'restacked', |
|
+ Lang.bind(this, this._checkOverlap) |
|
+ ], [ |
|
+ // when windows are alwasy on top, the focus window can change |
|
+ // without the windows being restacked. Thus monitor window focus change. |
|
+ this._tracker, |
|
+ 'notify::focus-app', |
|
+ Lang.bind(this, this._checkOverlap) |
|
+ ], [ |
|
+ // update wne monitor changes, for instance in multimonitor when monitor are attached |
|
+ global.screen, |
|
+ 'monitors-changed', |
|
+ Lang.bind(this, this._checkOverlap ) |
|
+ ]); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ // Disconnect global signals |
|
+ this._signalsHandler.destroy(); |
|
+ |
|
+ // Remove residual windows signals |
|
+ this.disable(); |
|
+ }, |
|
+ |
|
+ enable: function() { |
|
+ this._isEnabled = true; |
|
+ this._status = OverlapStatus.UNDEFINED; |
|
+ global.get_window_actors().forEach(function(wa) { |
|
+ this._addWindowSignals(wa); |
|
+ }, this); |
|
+ this._doCheckOverlap(); |
|
+ }, |
|
+ |
|
+ disable: function() { |
|
+ this._isEnabled = false; |
|
+ |
|
+ for (let wa of this._trackedWindows.keys()) { |
|
+ this._removeWindowSignals(wa); |
|
+ } |
|
+ this._trackedWindows.clear(); |
|
+ |
|
+ if (this._checkOverlapTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._checkOverlapTimeoutId); |
|
+ this._checkOverlapTimeoutId = 0; |
|
+ } |
|
+ }, |
|
+ |
|
+ _windowCreated: function(display, metaWindow) { |
|
+ this._addWindowSignals(metaWindow.get_compositor_private()); |
|
+ }, |
|
+ |
|
+ _addWindowSignals: function(wa) { |
|
+ if (!this._handledWindow(wa)) |
|
+ return; |
|
+ let signalId = wa.connect('allocation-changed', Lang.bind(this, this._checkOverlap, wa.get_meta_window())); |
|
+ this._trackedWindows.set(wa, signalId); |
|
+ wa.connect('destroy', Lang.bind(this, this._removeWindowSignals)); |
|
+ }, |
|
+ |
|
+ _removeWindowSignals: function(wa) { |
|
+ if (this._trackedWindows.get(wa)) { |
|
+ wa.disconnect(this._trackedWindows.get(wa)); |
|
+ this._trackedWindows.delete(wa); |
|
+ } |
|
+ |
|
+ }, |
|
+ |
|
+ updateTargetBox: function(box) { |
|
+ this._targetBox = box; |
|
+ this._checkOverlap(); |
|
+ }, |
|
+ |
|
+ forceUpdate: function() { |
|
+ this._status = OverlapStatus.UNDEFINED; |
|
+ this._doCheckOverlap(); |
|
+ }, |
|
+ |
|
+ getOverlapStatus: function() { |
|
+ return (this._status == OverlapStatus.TRUE); |
|
+ }, |
|
+ |
|
+ _checkOverlap: function() { |
|
+ if (!this._isEnabled || (this._targetBox == null)) |
|
+ return; |
|
+ |
|
+ /* Limit the number of calls to the doCheckOverlap function */ |
|
+ if (this._checkOverlapTimeoutId) { |
|
+ this._checkOverlapTimeoutContinue = true; |
|
+ return |
|
+ } |
|
+ |
|
+ this._doCheckOverlap(); |
|
+ |
|
+ this._checkOverlapTimeoutId = Mainloop.timeout_add(INTELLIHIDE_CHECK_INTERVAL, Lang.bind(this, function() { |
|
+ this._doCheckOverlap(); |
|
+ if (this._checkOverlapTimeoutContinue) { |
|
+ this._checkOverlapTimeoutContinue = false; |
|
+ return GLib.SOURCE_CONTINUE; |
|
+ } else { |
|
+ this._checkOverlapTimeoutId = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ } |
|
+ })); |
|
+ }, |
|
+ |
|
+ _doCheckOverlap: function() { |
|
+ |
|
+ if (!this._isEnabled || (this._targetBox == null)) |
|
+ return; |
|
+ |
|
+ let overlaps = OverlapStatus.FALSE; |
|
+ let windows = global.get_window_actors(); |
|
+ |
|
+ if (windows.length > 0) { |
|
+ /* |
|
+ * Get the top window on the monitor where the dock is placed. |
|
+ * The idea is that we dont want to overlap with the windows of the topmost application, |
|
+ * event is it's not the focused app -- for instance because in multimonitor the user |
|
+ * select a window in the secondary monitor. |
|
+ */ |
|
+ |
|
+ let topWindow = null; |
|
+ for (let i = windows.length - 1; i >= 0; i--) { |
|
+ let meta_win = windows[i].get_meta_window(); |
|
+ if (this._handledWindow(windows[i]) && (meta_win.get_monitor() == this._monitorIndex)) { |
|
+ topWindow = meta_win; |
|
+ break; |
|
+ } |
|
+ } |
|
+ |
|
+ if (topWindow !== null) { |
|
+ this._topApp = this._tracker.get_window_app(topWindow); |
|
+ // If there isn't a focused app, use that of the window on top |
|
+ this._focusApp = this._tracker.focus_app || this._topApp |
|
+ |
|
+ windows = windows.filter(this._intellihideFilterInteresting, this); |
|
+ |
|
+ for (let i = 0; i < windows.length; i++) { |
|
+ let win = windows[i].get_meta_window(); |
|
+ |
|
+ if (win) { |
|
+ let rect = win.get_frame_rect(); |
|
+ |
|
+ let test = (rect.x < this._targetBox.x2) && |
|
+ (rect.x + rect.width > this._targetBox.x1) && |
|
+ (rect.y < this._targetBox.y2) && |
|
+ (rect.y + rect.height > this._targetBox.y1); |
|
+ |
|
+ if (test) { |
|
+ overlaps = OverlapStatus.TRUE; |
|
+ break; |
|
+ } |
|
+ } |
|
+ } |
|
+ } |
|
+ } |
|
+ |
|
+ if (this._status !== overlaps) { |
|
+ this._status = overlaps; |
|
+ this.emit('status-changed', this._status); |
|
+ } |
|
+ |
|
+ }, |
|
+ |
|
+ // Filter interesting windows to be considered for intellihide. |
|
+ // Consider all windows visible on the current workspace. |
|
+ // Optionally skip windows of other applications |
|
+ _intellihideFilterInteresting: function(wa) { |
|
+ let meta_win = wa.get_meta_window(); |
|
+ if (!this._handledWindow(wa)) |
|
+ return false; |
|
+ |
|
+ let currentWorkspace = global.screen.get_active_workspace_index(); |
|
+ let wksp = meta_win.get_workspace(); |
|
+ let wksp_index = wksp.index(); |
|
+ |
|
+ // Depending on the intellihide mode, exclude non-relevent windows |
|
+ switch (this._settings.get_enum('intellihide-mode')) { |
|
+ case IntellihideMode.ALL_WINDOWS: |
|
+ // Do nothing |
|
+ break; |
|
+ |
|
+ case IntellihideMode.FOCUS_APPLICATION_WINDOWS: |
|
+ // Skip windows of other apps |
|
+ if (this._focusApp) { |
|
+ // The DropDownTerminal extension is not an application per se |
|
+ // so we match its window by wm class instead |
|
+ if (meta_win.get_wm_class() == 'DropDownTerminalWindow') |
|
+ return true; |
|
+ |
|
+ let currentApp = this._tracker.get_window_app(meta_win); |
|
+ let focusWindow = global.display.get_focus_window() |
|
+ |
|
+ // Consider half maximized windows side by side |
|
+ // and windows which are alwayson top |
|
+ if((currentApp != this._focusApp) && (currentApp != this._topApp) |
|
+ && !((focusWindow && focusWindow.maximized_vertically && !focusWindow.maximized_horizontally) |
|
+ && (meta_win.maximized_vertically && !meta_win.maximized_horizontally) |
|
+ && meta_win.get_monitor() == focusWindow.get_monitor()) |
|
+ && !meta_win.is_above()) |
|
+ return false; |
|
+ } |
|
+ break; |
|
+ |
|
+ case IntellihideMode.MAXIMIZED_WINDOWS: |
|
+ // Skip unmaximized windows |
|
+ if (!meta_win.maximized_vertically && !meta_win.maximized_horizontally) |
|
+ return false; |
|
+ break; |
|
+ } |
|
+ |
|
+ if ( wksp_index == currentWorkspace && meta_win.showing_on_its_workspace() ) |
|
+ return true; |
|
+ else |
|
+ return false; |
|
+ |
|
+ }, |
|
+ |
|
+ // Filter windows by type |
|
+ // inspired by Opacify@gnome-shell.localdomain.pl |
|
+ _handledWindow: function(wa) { |
|
+ let metaWindow = wa.get_meta_window(); |
|
+ |
|
+ if (!metaWindow) |
|
+ return false; |
|
+ |
|
+ // The DropDownTerminal extension uses the POPUP_MENU window type hint |
|
+ // so we match its window by wm class instead |
|
+ if (metaWindow.get_wm_class() == 'DropDownTerminalWindow') |
|
+ return true; |
|
+ |
|
+ let wtype = metaWindow.get_window_type(); |
|
+ for (let i = 0; i < handledWindowTypes.length; i++) { |
|
+ var hwtype = handledWindowTypes[i]; |
|
+ if (hwtype == wtype) |
|
+ return true; |
|
+ else if (hwtype > wtype) |
|
+ return false; |
|
+ } |
|
+ return false; |
|
+ } |
|
+}); |
|
+ |
|
+Signals.addSignalMethods(Intellihide.prototype); |
|
diff --git a/extensions/dash-to-dock/launcherAPI.js b/extensions/dash-to-dock/launcherAPI.js |
|
new file mode 100644 |
|
index 0000000..d051a70 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/launcherAPI.js |
|
@@ -0,0 +1,244 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Gio = imports.gi.Gio; |
|
+const Lang = imports.lang; |
|
+const Signals = imports.signals; |
|
+ |
|
+var LauncherEntryRemoteModel = new Lang.Class({ |
|
+ Name: 'DashToDock.LauncherEntryRemoteModel', |
|
+ |
|
+ _init: function () { |
|
+ 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, |
|
+ Lang.bind(this, this._onEntrySignalReceived)); |
|
+ |
|
+ 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, |
|
+ Lang.bind(this, this._onDBusNameOwnerChanged)); |
|
+ |
|
+ this._acquireUnityDBus(); |
|
+ }, |
|
+ |
|
+ destroy: function () { |
|
+ 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: function () { |
|
+ return Object.keys(this._entriesByDBusName).length; |
|
+ }, |
|
+ |
|
+ lookupByDBusName: function (dbusName) { |
|
+ return this._entriesByDBusName.hasOwnProperty(dbusName) ? this._entriesByDBusName[dbusName] : null; |
|
+ }, |
|
+ |
|
+ lookupById: function (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: function (entry) { |
|
+ let existingEntry = this.lookupByDBusName(entry.dbusName()); |
|
+ if (existingEntry) { |
|
+ existingEntry.update(entry); |
|
+ } else { |
|
+ this._entriesByDBusName[entry.dbusName()] = entry; |
|
+ this.emit('entry-added', entry); |
|
+ } |
|
+ }, |
|
+ |
|
+ removeEntry: function (entry) { |
|
+ delete this._entriesByDBusName[entry.dbusName()] |
|
+ this.emit('entry-removed', entry); |
|
+ }, |
|
+ |
|
+ _acquireUnityDBus: function () { |
|
+ if (!this._unity_bus_id) { |
|
+ Gio.DBus.session.own_name('com.canonical.Unity', |
|
+ Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null); |
|
+ } |
|
+ }, |
|
+ |
|
+ _releaseUnityDBus: function () { |
|
+ if (this._unity_bus_id) { |
|
+ Gio.DBus.session.unown_name(this._unity_bus_id); |
|
+ this._unity_bus_id = 0; |
|
+ } |
|
+ }, |
|
+ |
|
+ _onEntrySignalReceived: function (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: function (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: function (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 LauncherEntryRemote(senderName, appId, properties); |
|
+ this.addEntry(entry); |
|
+ } |
|
+ }, |
|
+}); |
|
+ |
|
+Signals.addSignalMethods(LauncherEntryRemoteModel.prototype); |
|
+ |
|
+var LauncherEntryRemote = new Lang.Class({ |
|
+ Name: 'DashToDock.LauncherEntryRemote', |
|
+ |
|
+ _init: function (dbusName, appId, properties) { |
|
+ this._dbusName = dbusName; |
|
+ this._appId = appId; |
|
+ this._count = 0; |
|
+ this._countVisible = false; |
|
+ this._progress = 0.0; |
|
+ this._progressVisible = false; |
|
+ this.update(properties); |
|
+ }, |
|
+ |
|
+ appId: function () { |
|
+ return this._appId; |
|
+ }, |
|
+ |
|
+ dbusName: function () { |
|
+ return this._dbusName; |
|
+ }, |
|
+ |
|
+ count: function () { |
|
+ return this._count; |
|
+ }, |
|
+ |
|
+ setCount: function (count) { |
|
+ if (this._count != count) { |
|
+ this._count = count; |
|
+ this.emit('count-changed', this._count); |
|
+ } |
|
+ }, |
|
+ |
|
+ countVisible: function () { |
|
+ return this._countVisible; |
|
+ }, |
|
+ |
|
+ setCountVisible: function (countVisible) { |
|
+ if (this._countVisible != countVisible) { |
|
+ this._countVisible = countVisible; |
|
+ this.emit('count-visible-changed', this._countVisible); |
|
+ } |
|
+ }, |
|
+ |
|
+ progress: function () { |
|
+ return this._progress; |
|
+ }, |
|
+ |
|
+ setProgress: function (progress) { |
|
+ if (this._progress != progress) { |
|
+ this._progress = progress; |
|
+ this.emit('progress-changed', this._progress); |
|
+ } |
|
+ }, |
|
+ |
|
+ progressVisible: function () { |
|
+ return this._progressVisible; |
|
+ }, |
|
+ |
|
+ setProgressVisible: function (progressVisible) { |
|
+ if (this._progressVisible != progressVisible) { |
|
+ this._progressVisible = progressVisible; |
|
+ this.emit('progress-visible-changed', this._progressVisible); |
|
+ } |
|
+ }, |
|
+ |
|
+ setDBusName: function (dbusName) { |
|
+ if (this._dbusName != dbusName) { |
|
+ let oldName = this._dbusName; |
|
+ this._dbusName = dbusName; |
|
+ this.emit('dbus-name-changed', oldName); |
|
+ } |
|
+ }, |
|
+ |
|
+ update: function (other) { |
|
+ if (other instanceof LauncherEntryRemote) { |
|
+ this.setDBusName(other.dbusName()) |
|
+ this.setCount(other.count()); |
|
+ this.setCountVisible(other.countVisible()); |
|
+ this.setProgress(other.progress()); |
|
+ this.setProgressVisible(other.progressVisible()) |
|
+ } 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(other[property].get_boolean()); |
|
+ } if (property == 'progress') { |
|
+ this.setProgress(other[property].get_double()); |
|
+ } else if (property == 'progress-visible') { |
|
+ this.setProgressVisible(other[property].get_boolean()); |
|
+ } else { |
|
+ // Not implemented yet |
|
+ } |
|
+ } |
|
+ } |
|
+ } |
|
+ }, |
|
+}); |
|
+ |
|
+Signals.addSignalMethods(LauncherEntryRemote.prototype); |
|
diff --git a/extensions/dash-to-dock/media/glossy.svg b/extensions/dash-to-dock/media/glossy.svg |
|
new file mode 100644 |
|
index 0000000..55b71ba |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/media/glossy.svg |
|
@@ -0,0 +1,139 @@ |
|
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
+<!-- Created with Inkscape (http://www.inkscape.org/) --> |
|
+ |
|
+<svg |
|
+ xmlns:dc="http://purl.org/dc/elements/1.1/" |
|
+ xmlns:cc="http://creativecommons.org/ns#" |
|
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
|
+ xmlns:svg="http://www.w3.org/2000/svg" |
|
+ xmlns="http://www.w3.org/2000/svg" |
|
+ xmlns:xlink="http://www.w3.org/1999/xlink" |
|
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
|
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
|
+ width="18.343554mm" |
|
+ height="18.343554mm" |
|
+ viewBox="0 0 14.674843 14.674842" |
|
+ version="1.1" |
|
+ id="svg4941" |
|
+ sodipodi:docname="glossy.svg" |
|
+ inkscape:version="0.92.1 r15371"> |
|
+ <defs |
|
+ id="defs4935"> |
|
+ <linearGradient |
|
+ id="linearGradient6812" |
|
+ inkscape:collect="always"> |
|
+ <stop |
|
+ style="stop-color:#ffffff;stop-opacity:0;" |
|
+ offset="0" |
|
+ id="stop6810" /> |
|
+ <stop |
|
+ style="stop-color:#ffffff;stop-opacity:1;" |
|
+ offset="1" |
|
+ id="stop6808" /> |
|
+ </linearGradient> |
|
+ <linearGradient |
|
+ inkscape:collect="always" |
|
+ xlink:href="#linearGradient18962" |
|
+ id="linearGradient35463" |
|
+ gradientUnits="userSpaceOnUse" |
|
+ gradientTransform="matrix(0.29132751,0,0,0.15428114,-54.210829,160.22776)" |
|
+ x1="214.71877" |
|
+ y1="404.36081" |
|
+ x2="214.71877" |
|
+ y2="443.54596" /> |
|
+ <linearGradient |
|
+ inkscape:collect="always" |
|
+ id="linearGradient18962"> |
|
+ <stop |
|
+ style="stop-color:#ffffff;stop-opacity:1;" |
|
+ offset="0" |
|
+ id="stop18964" /> |
|
+ <stop |
|
+ style="stop-color:#ffffff;stop-opacity:0;" |
|
+ offset="1" |
|
+ id="stop18966" /> |
|
+ </linearGradient> |
|
+ <linearGradient |
|
+ id="linearGradient18806"> |
|
+ <stop |
|
+ style="stop-color:#ff0101;stop-opacity:1;" |
|
+ offset="0" |
|
+ id="stop18808" /> |
|
+ <stop |
|
+ style="stop-color:#800000;stop-opacity:1;" |
|
+ offset="1" |
|
+ id="stop18810" /> |
|
+ </linearGradient> |
|
+ <radialGradient |
|
+ inkscape:collect="always" |
|
+ xlink:href="#linearGradient6812" |
|
+ id="radialGradient6798" |
|
+ cx="7.3538475" |
|
+ cy="230.28426" |
|
+ fx="7.3538475" |
|
+ fy="230.28426" |
|
+ r="7.2099228" |
|
+ gradientTransform="matrix(5.9484829,-0.0346444,0.01679088,3.0681664,-40.338609,-476.01412)" |
|
+ gradientUnits="userSpaceOnUse" /> |
|
+ </defs> |
|
+ <sodipodi:namedview |
|
+ id="base" |
|
+ pagecolor="#ffffff" |
|
+ bordercolor="#666666" |
|
+ borderopacity="1.0" |
|
+ inkscape:pageopacity="0.0" |
|
+ inkscape:pageshadow="2" |
|
+ inkscape:zoom="10.68" |
|
+ inkscape:cx="65.485107" |
|
+ inkscape:cy="29.432163" |
|
+ inkscape:document-units="mm" |
|
+ inkscape:current-layer="layer1" |
|
+ showgrid="false" |
|
+ inkscape:window-width="2560" |
|
+ inkscape:window-height="1406" |
|
+ inkscape:window-x="1920" |
|
+ inkscape:window-y="0" |
|
+ inkscape:window-maximized="1" |
|
+ scale-x="0.8" |
|
+ fit-margin-top="0" |
|
+ fit-margin-left="0" |
|
+ fit-margin-right="0" |
|
+ fit-margin-bottom="0" /> |
|
+ <metadata |
|
+ id="metadata4938"> |
|
+ <rdf:RDF> |
|
+ <cc:Work |
|
+ rdf:about=""> |
|
+ <dc:format>image/svg+xml</dc:format> |
|
+ <dc:type |
|
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
|
+ <dc:title></dc:title> |
|
+ </cc:Work> |
|
+ </rdf:RDF> |
|
+ </metadata> |
|
+ <g |
|
+ inkscape:label="Layer 1" |
|
+ inkscape:groupmode="layer" |
|
+ id="layer1" |
|
+ transform="translate(0,-222.92515)"> |
|
+ <rect |
|
+ inkscape:export-ydpi="180" |
|
+ inkscape:export-xdpi="180" |
|
+ inkscape:export-filename="C:\Arbeit\Blog\Tutorials\glossybutton\Glossy_Button_Tutorial.png" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.35100002;fill:url(#linearGradient35463);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.39747861;marker:none;enable-background:accumulate" |
|
+ id="rect19155" |
|
+ width="14.634871" |
|
+ height="3.7392156" |
|
+ x="0.039808333" |
|
+ y="222.98268" |
|
+ rx="1.5496143" |
|
+ ry="0.82064426" /> |
|
+ <rect |
|
+ style="opacity:0.427;fill:url(#radialGradient6798);fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" |
|
+ id="rect6706" |
|
+ width="14.363673" |
|
+ height="14.404656" |
|
+ x="0.090466507" |
|
+ y="223.07919" /> |
|
+ </g> |
|
+</svg> |
|
diff --git a/extensions/dash-to-dock/media/logo.svg b/extensions/dash-to-dock/media/logo.svg |
|
new file mode 100644 |
|
index 0000000..eebd0b1 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/media/logo.svg |
|
@@ -0,0 +1,528 @@ |
|
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
+<!-- Created with Inkscape (http://www.inkscape.org/) --> |
|
+ |
|
+<svg |
|
+ xmlns:dc="http://purl.org/dc/elements/1.1/" |
|
+ xmlns:cc="http://creativecommons.org/ns#" |
|
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
|
+ xmlns:svg="http://www.w3.org/2000/svg" |
|
+ xmlns="http://www.w3.org/2000/svg" |
|
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" |
|
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" |
|
+ width="33.866665mm" |
|
+ height="33.866684mm" |
|
+ viewBox="0 0 33.866665 33.866683" |
|
+ id="svg5179" |
|
+ version="1.1" |
|
+ inkscape:version="0.91 r13725" |
|
+ sodipodi:docname="logo.svg"> |
|
+ <defs |
|
+ id="defs5181"> |
|
+ <clipPath |
|
+ clipPathUnits="userSpaceOnUse" |
|
+ id="clipPath4379-92-4-9-6-8-0"> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4381-17-7-5-2-0-6" |
|
+ width="19.934219" |
|
+ height="33.52573" |
|
+ x="356.02826" |
|
+ y="457.71631" /> |
|
+ </clipPath> |
|
+ <filter |
|
+ style="color-interpolation-filters:sRGB" |
|
+ inkscape:collect="always" |
|
+ id="filter4435-8-5-3-2-13-8" |
|
+ x="-0.22881356" |
|
+ width="1.4576271" |
|
+ y="-0.22881356" |
|
+ height="1.4576271"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="1.0352993" |
|
+ id="feGaussianBlur4437-6-7-9-8-8-1" /> |
|
+ </filter> |
|
+ <filter |
|
+ style="color-interpolation-filters:sRGB" |
|
+ inkscape:collect="always" |
|
+ id="filter4365-71-5-7-0-6-2" |
|
+ x="-0.21864407" |
|
+ width="1.437288" |
|
+ y="-0.21864407" |
|
+ height="1.437288"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="0.98928601" |
|
+ id="feGaussianBlur4367-74-5-92-0-6-5" /> |
|
+ </filter> |
|
+ <clipPath |
|
+ clipPathUnits="userSpaceOnUse" |
|
+ id="clipPath4379-6-7-5-8-6-01-2"> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4381-1-8-5-2-0-2-7" |
|
+ width="19.934219" |
|
+ height="33.52573" |
|
+ x="356.02826" |
|
+ y="457.71631" /> |
|
+ </clipPath> |
|
+ <filter |
|
+ style="color-interpolation-filters:sRGB" |
|
+ inkscape:collect="always" |
|
+ id="filter4435-6-1-2-8-2-2-7" |
|
+ x="-0.22881356" |
|
+ width="1.4576271" |
|
+ y="-0.22881356" |
|
+ height="1.4576271"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="1.0352993" |
|
+ id="feGaussianBlur4437-1-1-3-60-1-4-4" /> |
|
+ </filter> |
|
+ <filter |
|
+ style="color-interpolation-filters:sRGB" |
|
+ inkscape:collect="always" |
|
+ id="filter4365-4-5-2-24-7-3-3" |
|
+ x="-0.21864407" |
|
+ width="1.437288" |
|
+ y="-0.21864407" |
|
+ height="1.437288"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="0.98928601" |
|
+ id="feGaussianBlur4367-7-0-7-7-9-0-3" /> |
|
+ </filter> |
|
+ <clipPath |
|
+ clipPathUnits="userSpaceOnUse" |
|
+ id="clipPath4379-5-6-0-9-8-7-9"> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4381-6-8-5-9-9-2-4" |
|
+ width="19.934219" |
|
+ height="33.52573" |
|
+ x="356.02826" |
|
+ y="457.71631" /> |
|
+ </clipPath> |
|
+ <filter |
|
+ style="color-interpolation-filters:sRGB" |
|
+ inkscape:collect="always" |
|
+ id="filter4435-63-9-2-4-1-2-6" |
|
+ x="-0.22881356" |
|
+ width="1.4576271" |
|
+ y="-0.22881356" |
|
+ height="1.4576271"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="1.0352993" |
|
+ id="feGaussianBlur4437-0-5-6-8-8-9-9" /> |
|
+ </filter> |
|
+ <filter |
|
+ style="color-interpolation-filters:sRGB" |
|
+ inkscape:collect="always" |
|
+ id="filter4365-2-4-3-6-3-1-7" |
|
+ x="-0.21864407" |
|
+ width="1.437288" |
|
+ y="-0.21864407" |
|
+ height="1.437288"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="0.98928601" |
|
+ id="feGaussianBlur4367-1-2-5-3-5-8-3" /> |
|
+ </filter> |
|
+ <filter |
|
+ inkscape:collect="always" |
|
+ style="color-interpolation-filters:sRGB" |
|
+ id="filter4255" |
|
+ x="-0.20374454" |
|
+ width="1.4074891" |
|
+ y="-0.13779147" |
|
+ height="1.2755829"> |
|
+ <feGaussianBlur |
|
+ inkscape:collect="always" |
|
+ stdDeviation="0.25863247" |
|
+ id="feGaussianBlur4257" /> |
|
+ </filter> |
|
+ </defs> |
|
+ <sodipodi:namedview |
|
+ id="base" |
|
+ pagecolor="#ffffff" |
|
+ bordercolor="#666666" |
|
+ borderopacity="1.0" |
|
+ inkscape:pageopacity="0.0" |
|
+ inkscape:pageshadow="2" |
|
+ inkscape:zoom="8" |
|
+ inkscape:cx="60.090739" |
|
+ inkscape:cy="60.108985" |
|
+ inkscape:document-units="mm" |
|
+ inkscape:current-layer="layer1" |
|
+ showgrid="false" |
|
+ fit-margin-top="0" |
|
+ fit-margin-left="0" |
|
+ fit-margin-right="0" |
|
+ fit-margin-bottom="0" |
|
+ inkscape:window-width="1861" |
|
+ inkscape:window-height="1023" |
|
+ inkscape:window-x="0" |
|
+ inkscape:window-y="27" |
|
+ inkscape:window-maximized="1" /> |
|
+ <metadata |
|
+ id="metadata5184"> |
|
+ <rdf:RDF> |
|
+ <cc:Work |
|
+ rdf:about=""> |
|
+ <dc:format>image/svg+xml</dc:format> |
|
+ <dc:type |
|
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> |
|
+ <dc:title /> |
|
+ </cc:Work> |
|
+ </rdf:RDF> |
|
+ </metadata> |
|
+ <g |
|
+ inkscape:label="Layer 1" |
|
+ inkscape:groupmode="layer" |
|
+ id="layer1" |
|
+ transform="translate(136.97858,-11.552354)"> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#0055d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4006-4-6-9-2-0-6" |
|
+ width="33.83363" |
|
+ height="33.859909" |
|
+ x="-136.9473" |
|
+ y="11.552354" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ inkscape:connector-curvature="0" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ d="m -130.12265,11.559157 c -4.30029,5.691881 -6.67207,12.608761 -6.82289,19.674442 -0.0115,0.54232 -0.0147,1.0766 0,1.62024 0.11433,4.23572 1.04846,8.50668 2.82497,12.565201 l 31.00865,0 0,-33.859883 -27.01073,0 z" |
|
+ id="path6097-2-6-0-89-4" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" |
|
+ d="m -136.9473,18.430158 0,0.7896 0,20.641361 0,0.7896 1.23782,0 2.26288,0 1.60528,0 c 0.68577,0 1.23783,-0.3548 1.23783,-0.7896 l 0,-20.641361 c 0,-0.4398 -0.55206,-0.7896 -1.23783,-0.7896 l -1.60528,0 -2.26288,0 z" |
|
+ id="rect4008-7-9-2-0-3-4" |
|
+ inkscape:connector-curvature="0" |
|
+ sodipodi:nodetypes="ccccccssssccc" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ inkscape:connector-curvature="0" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ d="m -119.36792,11.559157 c -10.47023,5.721881 -17.57762,16.847401 -17.57762,29.627402 0,1.43804 0.0897,2.841801 0.26432,4.232481 l 33.5693,0 0,-33.859883 -16.256,0 z" |
|
+ id="path6097-4-5-23-9" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4247-4-4-5-3-8-1" |
|
+ width="33.83363" |
|
+ height="2.1162443" |
|
+ x="-136.9473" |
|
+ y="11.552354" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ inkscape:connector-curvature="0" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ d="m -103.11365,13.668597 0,1.05812 c 0,-0.58196 -0.47338,-1.05812 -1.05731,-1.05812 l 1.05731,0 z" |
|
+ id="rect4272-0-7-8-1-1-3-3-1" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4031-9-9-2-4-2-5" |
|
+ width="4.2292037" |
|
+ height="4.2324886" |
|
+ x="-135.89" |
|
+ y="19.488146" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ inkscape:connector-curvature="0" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ d="m -136.94728,13.668597 0,1.05812 c 0,-0.58196 0.47337,-1.05812 1.0573,-1.05812 l -1.0573,0 z" |
|
+ id="rect4272-0-2-1-74-41-1-6" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <g |
|
+ id="g4353-9-2-1-5-5-4" |
|
+ transform="matrix(0.10331261,0,0,0.10339285,-173.76079,-27.453246)" |
|
+ clip-path="url(#clipPath4379-92-4-9-6-8-0)" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099"> |
|
+ <circle |
|
+ r="5.4295697" |
|
+ cy="477.71164" |
|
+ cx="274.13016" |
|
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" |
|
+ id="path3153-1-7-3-5-60-3-6" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-8-5-3-2-13-8);enable-background:accumulate" /> |
|
+ <circle |
|
+ r="5.4295697" |
|
+ cy="477.71164" |
|
+ cx="274.13016" |
|
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" |
|
+ id="path3153-2-4-1-6-6-9-4-1" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-71-5-7-0-6-2);enable-background:accumulate" /> |
|
+ </g> |
|
+ <g |
|
+ id="g4589-4-1-1-3-6-2" |
|
+ transform="matrix(0.49926208,0,0,0.49964988,-318.21072,-206.05794)" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099"> |
|
+ <g |
|
+ clip-path="url(#clipPath4379-6-7-5-8-6-01-2)" |
|
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,368.5622)" |
|
+ id="g4353-66-1-4-2-6-94-5"> |
|
+ <circle |
|
+ r="5.4295697" |
|
+ cy="477.71164" |
|
+ cx="274.13016" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-6-1-2-8-2-2-7);enable-background:accumulate" |
|
+ id="path3153-1-6-4-5-63-7-1-0" |
|
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" /> |
|
+ <circle |
|
+ r="5.4295697" |
|
+ cy="477.71164" |
|
+ cx="274.13016" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-4-5-2-24-7-3-3);enable-background:accumulate" |
|
+ id="path3153-2-4-7-6-5-8-5-9-5" |
|
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" /> |
|
+ </g> |
|
+ <g |
|
+ clip-path="url(#clipPath4379-5-6-0-9-8-7-9)" |
|
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,367.53449)" |
|
+ id="g4353-7-2-2-6-4-5-1"> |
|
+ <circle |
|
+ r="5.4295697" |
|
+ cy="477.71164" |
|
+ cx="274.13016" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-63-9-2-4-1-2-6);enable-background:accumulate" |
|
+ id="path3153-1-19-3-1-5-5-7-8" |
|
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" /> |
|
+ <circle |
|
+ r="5.4295697" |
|
+ cy="477.71164" |
|
+ cx="274.13016" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-2-4-3-6-3-1-7);enable-background:accumulate" |
|
+ id="path3153-2-4-5-7-9-9-9-7-6" |
|
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" /> |
|
+ </g> |
|
+ </g> |
|
+ <text |
|
+ xml:space="preserve" |
|
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none" |
|
+ x="-124.44726" |
|
+ y="13.10139" |
|
+ id="text4824-5-2-0-4-8" |
|
+ sodipodi:linespacing="125%" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" |
|
+ transform="scale(0.99961185,1.0003883)"><tspan |
|
+ sodipodi:role="line" |
|
+ id="tspan4826-16-3-8-8-1" |
|
+ x="-124.44726" |
|
+ y="13.10139">Dash to Dock</tspan></text> |
|
+ <text |
|
+ xml:space="preserve" |
|
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none" |
|
+ x="-136.50272" |
|
+ y="13.10139" |
|
+ id="text4824-8-8-6-8-7-4" |
|
+ sodipodi:linespacing="125%" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" |
|
+ transform="scale(0.99961185,1.0003883)"><tspan |
|
+ sodipodi:role="line" |
|
+ id="tspan4826-1-7-7-5-07-5" |
|
+ x="-136.50272" |
|
+ y="13.10139">Michele</tspan></text> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4031-9-0-8-5-4-0-7-6" |
|
+ width="4.2292037" |
|
+ height="4.2324886" |
|
+ x="-135.89" |
|
+ y="24.778917" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4031-9-0-7-3-3-6-0-1" |
|
+ width="4.2292037" |
|
+ height="4.2324886" |
|
+ x="-135.89" |
|
+ y="30.069445" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate" |
|
+ id="rect4031-9-0-6-5-1-3-9-0" |
|
+ width="4.2292037" |
|
+ height="4.2324886" |
|
+ x="-135.89" |
|
+ y="35.359974" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.5;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" |
|
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z" |
|
+ id="rect4008-7-0-0-3-3-3-7-9" |
|
+ inkscape:connector-curvature="0" |
|
+ sodipodi:nodetypes="cccsssscccccssssccc" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <path |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" |
|
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z" |
|
+ id="rect4008-7-0-0-3-1-5-0-5-5" |
|
+ inkscape:connector-curvature="0" |
|
+ sodipodi:nodetypes="cccsssscccccssssccc" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="44.99099" |
|
+ inkscape:export-ydpi="44.99099" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f2f2f2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ id="rect6777-7-9-6-9-8" |
|
+ width="20.108335" |
|
+ height="18.256252" |
|
+ x="-125.24149" |
|
+ y="19.139757" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="45" |
|
+ inkscape:export-ydpi="45" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" |
|
+ id="rect4923-8-7-8-2" |
|
+ width="3.7041669" |
|
+ height="3.7041669" |
|
+ x="-116.71888" |
|
+ y="30.163927" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" /> |
|
+ <path |
|
+ inkscape:connector-curvature="0" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ d="m -111.94623,19.146638 c -5.49508,1.3884 -10.21465,5.00036 -13.29531,9.92188 l 0,8.334361 20.10833,0 0,-18.256241 -6.81302,0 z" |
|
+ id="path6862-84-2-2-6-7" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="45" |
|
+ inkscape:export-ydpi="45" /> |
|
+ <path |
|
+ inkscape:connector-curvature="0" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ d="m -125.02657,18.882038 c -0.11728,0 -0.21496,0.0812 -0.21496,0.1984 l 0,0.44648 0,1.2568 0,0.2148 0.21496,0 19.67838,0 0.215,0 0,-0.2148 0,-1.2568 0,-0.44648 c 0,-0.1172 -0.0977,-0.1984 -0.215,-0.1984 l -19.67838,0 z" |
|
+ id="rect6779-5-8-6-4-6" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="45" |
|
+ inkscape:export-ydpi="45" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ id="rect6779-2-3-9-9-0-8" |
|
+ width="20.108335" |
|
+ height="0.5291667" |
|
+ x="-125.24149" |
|
+ y="20.991808" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="45" |
|
+ inkscape:export-ydpi="45" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ id="rect6779-2-4-8-0-7-1-1" |
|
+ width="15.875001" |
|
+ height="0.5291667" |
|
+ x="21.521105" |
|
+ y="105.13315" |
|
+ transform="rotate(90)" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="45" |
|
+ inkscape:export-ydpi="45" /> |
|
+ <g |
|
+ id="g6839-1-5-1-33-0" |
|
+ transform="matrix(0.02002288,0.02002284,-0.02002288,0.02002284,-106.62848,-6.0229242)" |
|
+ style="fill:#1a1a1a" |
|
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png" |
|
+ inkscape:export-xdpi="45" |
|
+ inkscape:export-ydpi="45"> |
|
+ <rect |
|
+ y="616.07727" |
|
+ x="653.01312" |
|
+ height="41.542522" |
|
+ width="11.313708" |
|
+ id="rect6819-8-9-2-56-9" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" /> |
|
+ <rect |
|
+ transform="rotate(90)" |
|
+ y="-679.44122" |
|
+ x="631.19165" |
|
+ height="41.542522" |
|
+ width="11.313708" |
|
+ id="rect6819-3-9-4-3-1-5" |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" /> |
|
+ </g> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" |
|
+ id="rect4923-6-8-9-1" |
|
+ width="3.7041669" |
|
+ height="3.7041669" |
|
+ x="-123.59805" |
|
+ y="30.163927" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" /> |
|
+ <path |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.26458335;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate;opacity:0.866;filter:url(#filter4255)" |
|
+ d="m -121.46776,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 0.99716,1.727136 1.33421,-0.770304 -0.99542,-1.724104 0.96903,-0.268366 -1.50959,-0.870995 z" |
|
+ id="path6155-6-0-01-4-5-6-0-0" |
|
+ inkscape:connector-curvature="0" |
|
+ sodipodi:nodetypes="cccccccccc" /> |
|
+ <path |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.13229167;stroke-miterlimit:4;stroke-dasharray:none;marker:none;filter:url(#filter4365-3);enable-background:accumulate" |
|
+ d="m -121.86464,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 1.05563,1.8284 1.3342,-0.770304 -1.05388,-1.825368 0.96903,-0.268366 -1.50959,-0.870995 z" |
|
+ id="path6155-6-0-8-0-7-97-5" |
|
+ inkscape:connector-curvature="0" |
|
+ sodipodi:nodetypes="cccccccccc" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" |
|
+ id="rect4923-4-8-4" |
|
+ width="3.7041669" |
|
+ height="3.7041669" |
|
+ x="-123.59805" |
|
+ y="23.020128" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" /> |
|
+ <rect |
|
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" |
|
+ id="rect4923-2-6-7-8" |
|
+ width="3.7041669" |
|
+ height="3.7041669" |
|
+ x="-116.71888" |
|
+ y="23.020128" |
|
+ rx="1.0583334" |
|
+ ry="1.0583334" /> |
|
+ </g> |
|
+</svg> |
|
diff --git a/extensions/dash-to-dock/meson.build b/extensions/dash-to-dock/meson.build |
|
new file mode 100644 |
|
index 0000000..e0906fa |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/meson.build |
|
@@ -0,0 +1,23 @@ |
|
+extension_data += configure_file( |
|
+ input: metadata_name + '.in', |
|
+ output: metadata_name, |
|
+ configuration: metadata_conf |
|
+) |
|
+ |
|
+extension_sources += files( |
|
+ 'appIconIndicators.js', |
|
+ 'appIcons.js', |
|
+ 'convenience.js', |
|
+ 'dash.js', |
|
+ 'docking.js', |
|
+ 'intellihide.js', |
|
+ 'launcherAPI.js', |
|
+ 'prefs.js', |
|
+ 'Settings.ui', |
|
+ 'theming.js', |
|
+ 'utils.js', |
|
+ 'windowPreview.js' |
|
+) |
|
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') |
|
+ |
|
+install_data(['media/logo.svg', 'media/glossy.svg'], install_dir: join_paths(extensiondir, uuid, 'media')) |
|
diff --git a/extensions/dash-to-dock/metadata.json.in b/extensions/dash-to-dock/metadata.json.in |
|
new file mode 100644 |
|
index 0000000..90eddb5 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/metadata.json.in |
|
@@ -0,0 +1,12 @@ |
|
+{ |
|
+"extension-id": "@extension_id@", |
|
+"uuid": "@uuid@", |
|
+"settings-schema": "@gschemaname@", |
|
+"gettext-domain": "@gettext_domain@", |
|
+"original-author": "micxgx@gmail.com", |
|
+"name": "Dash to Dock", |
|
+"description": "A dock for the Gnome Shell. This extension moves the dash out of the overview transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops. Side and bottom placement options are available.", |
|
+"shell-version": [ "@shell_current@" ], |
|
+"version": 45, |
|
+"url": "https://micheleg.github.io/dash-to-dock/" |
|
+} |
|
diff --git a/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml |
|
new file mode 100644 |
|
index 0000000..3e4f68a |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/org.gnome.shell.extensions.dash-to-dock.gschema.xml |
|
@@ -0,0 +1,540 @@ |
|
+<?xml version="1.0" encoding="UTF-8"?> |
|
+<schemalist gettext-domain="gnome-shell-extensions"> |
|
+ <enum id='org.gnome.shell.extensions.dash-to-dock.clickAction'> |
|
+ <value value='0' nick='skip'/> |
|
+ <value value='1' nick='minimize'/> |
|
+ <value value='2' nick='launch'/> |
|
+ <value value='3' nick='cycle-windows'/> |
|
+ <value value='4' nick='minimize-or-overview'/> |
|
+ <value value='5' nick='previews'/> |
|
+ <value value='6' nick='minimize-or-previews'/> |
|
+ <value value='7' nick='quit'/> |
|
+ </enum> |
|
+ <enum id='org.gnome.shell.extensions.dash-to-dock.scrollAction'> |
|
+ <value value='0' nick='do-nothing'/> |
|
+ <value value='1' nick='cycle-windows'/> |
|
+ <value value='2' nick='switch-workspace'/> |
|
+ </enum> |
|
+ <!-- this is mean to Match StSide. LEFT and RIGHT actual position in reversed in |
|
+ rtl languages --> |
|
+ <enum id='org.gnome.shell.extensions.dash-to-dock.position'> |
|
+ <value value='0' nick='TOP'/> |
|
+ <value value='1' nick='RIGHT'/> |
|
+ <value value='2' nick='BOTTOM'/> |
|
+ <value value='3' nick='LEFT'/> |
|
+ </enum> |
|
+ <enum id='org.gnome.shell.extensions.dash-to-dock.intellihide-mode'> |
|
+ <value value='0' nick='ALL_WINDOWS'/> |
|
+ <value value='1' nick='FOCUS_APPLICATION_WINDOWS'/> |
|
+ <value value='2' nick='MAXIMIZED_WINDOWS'/> |
|
+ </enum> |
|
+ <enum id='org.gnome.shell.extensions.dash-to-dock.transparency-mode'> |
|
+ <value value='0' nick='DEFAULT'/> |
|
+ <value value='1' nick='FIXED'/> |
|
+ <value value='2' nick='ADAPTIVE'/> |
|
+ <value value='3' nick='DYNAMIC'/> |
|
+ </enum> |
|
+ <enum id='org.gnome.shell.extensions.dash-to-dock.running-indicator-style'> |
|
+ <value value='0' nick='DEFAULT'/> |
|
+ <value value='1' nick='DOTS'/> |
|
+ <value value='2' nick='SQUARES'/> |
|
+ <value value='3' nick='DASHES'/> |
|
+ <value value='4' nick='SEGMENTED'/> |
|
+ <value value='5' nick='SOLID'/> |
|
+ <value value='6' nick='CILIORA'/> |
|
+ <value value='7' nick='METRO'/> |
|
+ </enum> |
|
+ <schema path="/org/gnome/shell/extensions/dash-to-dock/" id="org.gnome.shell.extensions.dash-to-dock"> |
|
+ <key name="dock-position" enum="org.gnome.shell.extensions.dash-to-dock.position"> |
|
+ <default>'LEFT'</default> |
|
+ <summary>Dock position</summary> |
|
+ <description>Dock is shown on the Left, Right, Top or Bottom side of the screen.</description> |
|
+ </key> |
|
+ <key type="d" name="animation-time"> |
|
+ <default>0.2</default> |
|
+ <summary>Animation time</summary> |
|
+ <description>Sets the time duration of the autohide effect.</description> |
|
+ </key> |
|
+ <key type="d" name="show-delay"> |
|
+ <default>0.25</default> |
|
+ <summary>Show delay</summary> |
|
+ <description>Sets the delay after the mouse reaches the screen border before showing the dock.</description> |
|
+ </key> |
|
+ <key type="d" name="hide-delay"> |
|
+ <default>0.20</default> |
|
+ <summary>Show delay</summary> |
|
+ <description>Sets the delay after the mouse left the dock before hiding it.</description> |
|
+ </key> |
|
+ <key type="b" name="custom-background-color"> |
|
+ <default>false</default> |
|
+ <summary>Set a custom dash background background color</summary> |
|
+ <description>Sets the color for the dash background.</description> |
|
+ </key> |
|
+ <key type="s" name="background-color"> |
|
+ <default>"#ffffff"</default> |
|
+ <summary>Dash background color.</summary> |
|
+ <description>Customize the background color of the dash.</description> |
|
+ </key> |
|
+ <key name="transparency-mode" enum="org.gnome.shell.extensions.dash-to-dock.transparency-mode"> |
|
+ <default>'DEFAULT'</default> |
|
+ <summary>Transparency mode for the dock</summary> |
|
+ <description>FIXED: constant transparency. ADAPTIVE: lock state with the top panel when not hidden. DYNAMIC: dock takes the opaque style only when windows are close to it.</description> |
|
+ </key> |
|
+ <key name="running-indicator-style" enum="org.gnome.shell.extensions.dash-to-dock.running-indicator-style"> |
|
+ <default>'DEFAULT'</default> |
|
+ <summary>...</summary> |
|
+ <description>DEFAULT: .... DOTS: ....</description> |
|
+ </key> |
|
+ <key type="b" name="running-indicator-dominant-color"> |
|
+ <default>false</default> |
|
+ <summary>Use application icon dominant color for the indicator color</summary> |
|
+ <description></description> |
|
+ </key> |
|
+ <key type="b" name="customize-alphas"> |
|
+ <default>false</default> |
|
+ <summary>Manually set the min and max opacity</summary> |
|
+ <description>For Adaptive and Dynamic modes, the min/max opacity values will be given by 'min-alpha' and 'max-alpha'.</description> |
|
+ </key> |
|
+ <key type="d" name="min-alpha"> |
|
+ <default>0.2</default> |
|
+ <summary>Opacity of the dash background when free-floating</summary> |
|
+ <description>Sets the opacity of the dash background when no windows are close.</description> |
|
+ </key> |
|
+ <key type="d" name="max-alpha"> |
|
+ <default>0.8</default> |
|
+ <summary>Opacity of the dash background when windows are close.</summary> |
|
+ <description>Sets the opacity of the dash background when windows are close.</description> |
|
+ </key> |
|
+ <key type="d" name="background-opacity"> |
|
+ <default>0.8</default> |
|
+ <summary>Opacity of the dash background</summary> |
|
+ <description>Sets the opacity of the dash background when in autohide mode.</description> |
|
+ </key> |
|
+ <key type="b" name="intellihide"> |
|
+ <default>true</default> |
|
+ <summary>Dock dodges windows</summary> |
|
+ <description>Enable or disable intellihide mode</description> |
|
+ </key> |
|
+ <key name="intellihide-mode" enum="org.gnome.shell.extensions.dash-to-dock.intellihide-mode"> |
|
+ <default>'FOCUS_APPLICATION_WINDOWS'</default> |
|
+ <summary>Define which windows are considered for intellihide.</summary> |
|
+ <description></description> |
|
+ </key> |
|
+ <key type="b" name="autohide"> |
|
+ <default>true</default> |
|
+ <summary>Dock shown on mouse over</summary> |
|
+ <description>Enable or disable autohide mode</description> |
|
+ </key> |
|
+ <key type="b" name="require-pressure-to-show"> |
|
+ <default>true</default> |
|
+ <summary>Require pressure to show dash</summary> |
|
+ <description>Enable or disable requiring pressure to show the dash</description> |
|
+ </key> |
|
+ <key type="d" name="pressure-threshold"> |
|
+ <default>100</default> |
|
+ <summary>Pressure threshold</summary> |
|
+ <description>Sets how much pressure is needed to show the dash.</description> |
|
+ </key> |
|
+ <key type="b" name="autohide-in-fullscreen"> |
|
+ <default>false</default> |
|
+ <summary>Enable autohide in fullscreen mode.</summary> |
|
+ <description>Enable autohide in fullscreen mode.</description> |
|
+ </key> |
|
+ <key type="b" name="dock-fixed"> |
|
+ <default>false</default> |
|
+ <summary>Dock always visible</summary> |
|
+ <description>Dock is always visible</description> |
|
+ </key> |
|
+ <key type="b" name="scroll-switch-workspace"> |
|
+ <default>true</default> |
|
+ <summary>Switch workspace by scrolling over the dock</summary> |
|
+ <description>Add the possibility to switch workspace by mouse scrolling over the dock.</description> |
|
+ </key> |
|
+ <key type="i" name="dash-max-icon-size"> |
|
+ <default>48</default> |
|
+ <summary>Maximum dash icon size</summary> |
|
+ <description>Set the allowed maximum dash icon size. Allowed range: 16..64.</description> |
|
+ </key> |
|
+ <key type="b" name="icon-size-fixed"> |
|
+ <default>false</default> |
|
+ <summary>Fixed icon size</summary> |
|
+ <description>Keep the icon size fived by scrolling the dock.</description> |
|
+ </key> |
|
+ <key type="b" name="apply-custom-theme"> |
|
+ <default>false</default> |
|
+ <summary>Apply custom theme</summary> |
|
+ <description>Apply customization to the dash appearance</description> |
|
+ </key> |
|
+ <key type="b" name="custom-theme-shrink"> |
|
+ <default>false</default> |
|
+ <summary>TODO</summary> |
|
+ <description>TODO</description> |
|
+ </key> |
|
+ <key type="b" name="custom-theme-customize-running-dots"> |
|
+ <default>false</default> |
|
+ <summary>Customize the style of the running application indicators.</summary> |
|
+ <description>Customize the style of the running application indicators.</description> |
|
+ </key> |
|
+ <key type="s" name="custom-theme-running-dots-color"> |
|
+ <default>"#ffffff"</default> |
|
+ <summary>Running application indicators color</summary> |
|
+ <description>Customize the color of the running application indicators.</description> |
|
+ </key> |
|
+ <key type="s" name="custom-theme-running-dots-border-color"> |
|
+ <default>"#ffffff"</default> |
|
+ <summary>Running application indicators border color.</summary> |
|
+ <description>Customize the border color of the running application indicators.</description> |
|
+ </key> |
|
+ <key type="i" name="custom-theme-running-dots-border-width"> |
|
+ <default>0</default> |
|
+ <summary>Running application indicators border width.</summary> |
|
+ <description>Customize the border width of the running application indicators.</description> |
|
+ </key> |
|
+ <key type="b" name="show-running"> |
|
+ <default>true</default> |
|
+ <summary>Show running apps</summary> |
|
+ <description>Show or hide running appplications icons in the dash</description> |
|
+ </key> |
|
+ <key type="b" name="isolate-workspaces"> |
|
+ <default>false</default> |
|
+ <summary>Provide workspace isolation</summary> |
|
+ <description>Dash shows only windows from the currentworkspace</description> |
|
+ </key> |
|
+ <key type="b" name="isolate-monitors"> |
|
+ <default>false</default> |
|
+ <summary>Provide monitor isolation</summary> |
|
+ <description>Dash shows only windows from the monitor</description> |
|
+ </key> |
|
+ <key type="b" name="show-windows-preview"> |
|
+ <default>true</default> |
|
+ <summary>Show preview of the open windows</summary> |
|
+ <description>Replace open windows list with windows previews</description> |
|
+ </key> |
|
+ <key type="b" name="show-favorites"> |
|
+ <default>true</default> |
|
+ <summary>Show favorites apps</summary> |
|
+ <description>Show or hide favorite appplications icons in the dash</description> |
|
+ </key> |
|
+ <key type="b" name="show-show-apps-button"> |
|
+ <default>true</default> |
|
+ <summary>Show applications button</summary> |
|
+ <description>Show appplications button in the dash</description> |
|
+ </key> |
|
+ <key type="b" name="show-apps-at-top"> |
|
+ <default>false</default> |
|
+ <summary>Show application button at top</summary> |
|
+ <description>Show appplication button at top of the dash</description> |
|
+ </key> |
|
+ <key type="b" name="animate-show-apps"> |
|
+ <default>true</default> |
|
+ <summary>Animate Show Applications from the desktop</summary> |
|
+ <description>Animate Show Applications from the desktop</description> |
|
+ </key> |
|
+ <key type="b" name="bolt-support"> |
|
+ <default>true</default> |
|
+ <summary>Basic compatibility with bolt extensions</summary> |
|
+ <description>Make the extension work properly when bolt extensions is enabled</description> |
|
+ </key> |
|
+ <key type="d" name="height-fraction"> |
|
+ <default>0.90</default> |
|
+ <summary>Dock max height (fraction of available space)</summary> |
|
+ </key> |
|
+ <key type="b" name="extend-height"> |
|
+ <default>false</default> |
|
+ <summary>Extend the dock container to all the available height</summary> |
|
+ </key> |
|
+ <key type="i" name="preferred-monitor"> |
|
+ <default>-1</default> |
|
+ <summary>Monitor on which putting the dock</summary> |
|
+ <description>Set on which monitor to put the dock, use -1 for the primary one</description> |
|
+ </key> |
|
+ <key type="b" name="multi-monitor"> |
|
+ <default>false</default> |
|
+ <summary>Enable multi-monitor docks</summary> |
|
+ <description>Show a dock on every monitor</description> |
|
+ </key> |
|
+ <key type="b" name="minimize-shift"> |
|
+ <default>true</default> |
|
+ <summary>Minimize on shift+click</summary> |
|
+ </key> |
|
+ <key type="b" name="activate-single-window"> |
|
+ <default>true</default> |
|
+ <summary>Activate only one window</summary> |
|
+ </key> |
|
+ <key name="click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction"> |
|
+ <default>'cycle-windows'</default> |
|
+ <summary>Action when clicking on a running app</summary> |
|
+ <description>Set the action that is executed when clicking on the icon of a running application</description> |
|
+ </key> |
|
+ <key name="scroll-action" enum="org.gnome.shell.extensions.dash-to-dock.scrollAction"> |
|
+ <default>'do-nothing'</default> |
|
+ <summary>Action when scrolling app</summary> |
|
+ <description>Set the action that is executed when scrolling on the application icon</description> |
|
+ </key> |
|
+ <key name="shift-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction"> |
|
+ <default>'minimize'</default> |
|
+ <summary>Action when shit+clicking on a running app</summary> |
|
+ <description>Set the action that is executed when shift+clicking on the icon of a running application</description> |
|
+ </key> |
|
+ <key name="middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction"> |
|
+ <default>'launch'</default> |
|
+ <summary>Action when clicking on a running app</summary> |
|
+ <description>Set the action that is executed when middle-clicking on the icon of a running application</description> |
|
+ </key> |
|
+ <key name="shift-middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction"> |
|
+ <default>'launch'</default> |
|
+ <summary>Action when clicking on a running app</summary> |
|
+ <description>Set the action that is executed when shift+middle-clicking on the icon of a running application</description> |
|
+ </key> |
|
+ <key type="b" name="hot-keys"> |
|
+ <default>true</default> |
|
+ <summary>Super Hot-Keys</summary> |
|
+ <description>Launch and switch between dash items using Super+(0-9)</description> |
|
+ </key> |
|
+ <key type="b" name="hotkeys-show-dock"> |
|
+ <default>true</default> |
|
+ <summary>Show the dock when using the hotkeys</summary> |
|
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description> |
|
+ </key> |
|
+ <key type="s" name="shortcut-text"> |
|
+ <default>"<Super>q"</default> |
|
+ <summary>Keybinding to show the dock and the number overlay.</summary> |
|
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description> |
|
+ </key> |
|
+ <key type="as" name="shortcut"> |
|
+ <default><![CDATA[['<Super>q']]]></default> |
|
+ <summary>Keybinding to show the dock and the number overlay.</summary> |
|
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description> |
|
+ </key> |
|
+ <key type="d" name="shortcut-timeout"> |
|
+ <default>2</default> |
|
+ <summary>Timeout to hide the dock</summary> |
|
+ <description>Sets the time duration before the dock is hidden again.</description> |
|
+ </key> |
|
+ <key type="b" name="hotkeys-overlay"> |
|
+ <default>true</default> |
|
+ <summary>Show the dock when using the hotkeys</summary> |
|
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-1" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>1']]]></default> |
|
+ <summary>Keybinding to launch 1st dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 1st app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-2" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>2']]]></default> |
|
+ <summary>Keybinding to launch 2nd dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 2nd app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-3" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>3']]]></default> |
|
+ <summary>Keybinding to launch 3rd dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 3rd app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-4" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>4']]]></default> |
|
+ <summary>Keybinding to launch 4th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 4th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-5" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>5']]]></default> |
|
+ <summary>Keybinding to launch 5th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 5th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-6" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>6']]]></default> |
|
+ <summary>Keybinding to launch 6th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 6th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-7" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>7']]]></default> |
|
+ <summary>Keybinding to launch 7th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 7th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-8" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>8']]]></default> |
|
+ <summary>Keybinding to launch 8th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 8th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-9" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>9']]]></default> |
|
+ <summary>Keybinding to launch 9th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 9th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-ctrl-hotkey-10" type="as"> |
|
+ <default><![CDATA[['<Ctrl><Super>0']]]></default> |
|
+ <summary>Keybinding to launch 10th dash app</summary> |
|
+ <description> |
|
+ Keybinding to launch 10th app. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-1" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>1']]]></default> |
|
+ <summary>Keybinding to trigger 1st dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 1st app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-2" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>2']]]></default> |
|
+ <summary>Keybinding to trigger 2nd dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 2nd app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-3" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>3']]]></default> |
|
+ <summary>Keybinding to trigger 3rd dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 3rd app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-4" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>4']]]></default> |
|
+ <summary>Keybinding to trigger 4th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 4th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-5" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>5']]]></default> |
|
+ <summary>Keybinding to trigger 5th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 5th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-6" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>6']]]></default> |
|
+ <summary>Keybinding to trigger 6th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 6th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-7" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>7']]]></default> |
|
+ <summary>Keybinding to trigger 7th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 7th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-8" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>8']]]></default> |
|
+ <summary>Keybinding to trigger 8th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 8th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-9" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>9']]]></default> |
|
+ <summary>Keybinding to trigger 9th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 9th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-shift-hotkey-10" type="as"> |
|
+ <default><![CDATA[['<Shift><Super>0']]]></default> |
|
+ <summary>Keybinding to trigger 10th dash app with shift behavior</summary> |
|
+ <description> |
|
+ Keybinding to trigger 10th app with shift behavior. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-1" type="as"> |
|
+ <default><![CDATA[['<Super>1']]]></default> |
|
+ <summary>Keybinding to trigger 1st dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 1st application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-2" type="as"> |
|
+ <default><![CDATA[['<Super>2']]]></default> |
|
+ <summary>Keybinding to trigger 2nd dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 2nd application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-3" type="as"> |
|
+ <default><![CDATA[['<Super>3']]]></default> |
|
+ <summary>Keybinding to trigger 3rd dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 3rd application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-4" type="as"> |
|
+ <default><![CDATA[['<Super>4']]]></default> |
|
+ <summary>Keybinding to trigger 4th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 4th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-5" type="as"> |
|
+ <default><![CDATA[['<Super>5']]]></default> |
|
+ <summary>Keybinding to trigger 5th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 5th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-6" type="as"> |
|
+ <default><![CDATA[['<Super>6']]]></default> |
|
+ <summary>Keybinding to trigger 6th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 6th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-7" type="as"> |
|
+ <default><![CDATA[['<Super>7']]]></default> |
|
+ <summary>Keybinding to trigger 7th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 7th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-8" type="as"> |
|
+ <default><![CDATA[['<Super>8']]]></default> |
|
+ <summary>Keybinding to trigger 8th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 8th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-9" type="as"> |
|
+ <default><![CDATA[['<Super>9']]]></default> |
|
+ <summary>Keybinding to trigger 9th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 9th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="app-hotkey-10" type="as"> |
|
+ <default><![CDATA[['<Super>0']]]></default> |
|
+ <summary>Keybinding to trigger 10th dash app</summary> |
|
+ <description> |
|
+ Keybinding to either show or launch the 10th application in the dash. |
|
+ </description> |
|
+ </key> |
|
+ <key name="force-straight-corner" type="b"> |
|
+ <default>false</default> |
|
+ <summary>Force straight corners in dash</summary> |
|
+ <description>Make the borders in the dash non rounded</description> |
|
+ </key> |
|
+ <key name="unity-backlit-items" type="b"> |
|
+ <default>false</default> |
|
+ <summary>Enable unity7 like glossy backlit items</summary> |
|
+ <description>Emulate the unity7 backlit glossy items behaviour</description> |
|
+ </key> |
|
+ </schema> |
|
+</schemalist> |
|
diff --git a/extensions/dash-to-dock/prefs.js b/extensions/dash-to-dock/prefs.js |
|
new file mode 100644 |
|
index 0000000..d8d8b94 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/prefs.js |
|
@@ -0,0 +1,868 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Gio = imports.gi.Gio; |
|
+const GLib = imports.gi.GLib; |
|
+const GObject = imports.gi.GObject; |
|
+const Gtk = imports.gi.Gtk; |
|
+const Gdk = imports.gi.Gdk; |
|
+const Lang = imports.lang; |
|
+const Mainloop = imports.mainloop; |
|
+ |
|
+// Use __ () and N__() for the extension gettext domain, and reuse |
|
+// the shell domain with the default _() and N_() |
|
+const Gettext = imports.gettext.domain('dashtodock'); |
|
+const __ = Gettext.gettext; |
|
+const N__ = function(e) { return e }; |
|
+ |
|
+const ExtensionUtils = imports.misc.extensionUtils; |
|
+const Me = ExtensionUtils.getCurrentExtension(); |
|
+const Convenience = Me.imports.convenience; |
|
+ |
|
+const SCALE_UPDATE_TIMEOUT = 500; |
|
+const DEFAULT_ICONS_SIZES = [ 128, 96, 64, 48, 32, 24, 16 ]; |
|
+ |
|
+const TransparencyMode = { |
|
+ DEFAULT: 0, |
|
+ FIXED: 1, |
|
+ ADAPTIVE: 2, |
|
+ DYNAMIC: 3 |
|
+}; |
|
+ |
|
+const RunningIndicatorStyle = { |
|
+ DEFAULT: 0, |
|
+ DOTS: 1, |
|
+ SQUARES: 2, |
|
+ DASHES: 3, |
|
+ SEGMENTED: 4, |
|
+ SOLID: 5, |
|
+ CILIORA: 6, |
|
+ METRO: 7 |
|
+}; |
|
+ |
|
+/** |
|
+ * 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) { |
|
+ let shortcut_text = settings.get_string('shortcut-text'); |
|
+ let [key, mods] = Gtk.accelerator_parse(shortcut_text); |
|
+ |
|
+ if (Gtk.accelerator_valid(key, mods)) { |
|
+ let shortcut = Gtk.accelerator_name(key, mods); |
|
+ settings.set_strv('shortcut', [shortcut]); |
|
+ } |
|
+ else { |
|
+ settings.set_strv('shortcut', []); |
|
+ } |
|
+} |
|
+ |
|
+const Settings = new Lang.Class({ |
|
+ Name: 'DashToDock.Settings', |
|
+ |
|
+ _init: function() { |
|
+ this._settings = Convenience.getSettings('org.gnome.shell.extensions.dash-to-dock'); |
|
+ |
|
+ this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL); |
|
+ |
|
+ this._builder = new Gtk.Builder(); |
|
+ this._builder.set_translation_domain(Me.metadata['gettext-domain']); |
|
+ this._builder.add_from_file(Me.path + '/Settings.ui'); |
|
+ |
|
+ this.widget = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER }); |
|
+ this._notebook = this._builder.get_object('settings_notebook'); |
|
+ this.widget.add(this._notebook); |
|
+ |
|
+ // Set a reasonable initial window height |
|
+ this.widget.connect('realize', Lang.bind(this, function() { |
|
+ let window = this.widget.get_toplevel(); |
|
+ let [default_width, default_height] = window.get_default_size(); |
|
+ window.resize(default_width, 650); |
|
+ })); |
|
+ |
|
+ // Timeout to delay the update of the settings |
|
+ this._dock_size_timeout = 0; |
|
+ this._icon_size_timeout = 0; |
|
+ this._opacity_timeout = 0; |
|
+ |
|
+ this._bindSettings(); |
|
+ |
|
+ this._builder.connect_signals_full(Lang.bind(this, this._connector)); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Connect signals |
|
+ */ |
|
+ _connector: function(builder, object, signal, handler) { |
|
+ object.connect(signal, Lang.bind(this, this._SignalHandler[handler])); |
|
+ }, |
|
+ |
|
+ _bindSettings: function() { |
|
+ // Position and size panel |
|
+ |
|
+ // Monitor options |
|
+ |
|
+ this._monitors = []; |
|
+ // Build options based on the number of monitors and the current settings. |
|
+ let n_monitors = Gdk.Screen.get_default().get_n_monitors(); |
|
+ let primary_monitor = Gdk.Screen.get_default().get_primary_monitor(); |
|
+ |
|
+ let monitor = this._settings.get_int('preferred-monitor'); |
|
+ |
|
+ // Add primary monitor with index 0, because in GNOME Shell the primary monitor is always 0 |
|
+ this._builder.get_object('dock_monitor_combo').append_text(__('Primary monitor')); |
|
+ this._monitors.push(0); |
|
+ |
|
+ // Add connected monitors |
|
+ let ctr = 0; |
|
+ for (let i = 0; i < n_monitors; i++) { |
|
+ if (i !== primary_monitor) { |
|
+ ctr++; |
|
+ this._monitors.push(ctr); |
|
+ this._builder.get_object('dock_monitor_combo').append_text(__('Secondary monitor ') + ctr); |
|
+ } |
|
+ } |
|
+ |
|
+ // If one of the external monitor is set as preferred, show it even if not attached |
|
+ if ((monitor >= n_monitors) && (monitor !== primary_monitor)) { |
|
+ this._monitors.push(monitor) |
|
+ this._builder.get_object('dock_monitor_combo').append_text(__('Secondary monitor ') + ++ctr); |
|
+ } |
|
+ |
|
+ this._builder.get_object('dock_monitor_combo').set_active(this._monitors.indexOf(monitor)); |
|
+ |
|
+ // Position option |
|
+ let position = this._settings.get_enum('dock-position'); |
|
+ |
|
+ switch (position) { |
|
+ case 0: |
|
+ this._builder.get_object('position_top_button').set_active(true); |
|
+ break; |
|
+ case 1: |
|
+ this._builder.get_object('position_right_button').set_active(true); |
|
+ break; |
|
+ case 2: |
|
+ this._builder.get_object('position_bottom_button').set_active(true); |
|
+ break; |
|
+ case 3: |
|
+ this._builder.get_object('position_left_button').set_active(true); |
|
+ break; |
|
+ } |
|
+ |
|
+ if (this._rtl) { |
|
+ /* Left is Right in rtl as a setting */ |
|
+ this._builder.get_object('position_left_button').set_label(__('Right')); |
|
+ this._builder.get_object('position_right_button').set_label(__('Left')); |
|
+ } |
|
+ |
|
+ // Intelligent autohide options |
|
+ this._settings.bind('dock-fixed', |
|
+ this._builder.get_object('intelligent_autohide_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.INVERT_BOOLEAN); |
|
+ this._settings.bind('dock-fixed', |
|
+ this._builder.get_object('intelligent_autohide_button'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.INVERT_BOOLEAN); |
|
+ this._settings.bind('autohide', |
|
+ this._builder.get_object('autohide_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('autohide-in-fullscreen', |
|
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('require-pressure-to-show', |
|
+ this._builder.get_object('require_pressure_checkbutton'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('intellihide', |
|
+ this._builder.get_object('intellihide_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('animation-time', |
|
+ this._builder.get_object('animation_duration_spinbutton'), |
|
+ 'value', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('hide-delay', |
|
+ this._builder.get_object('hide_timeout_spinbutton'), |
|
+ 'value', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-delay', |
|
+ this._builder.get_object('show_timeout_spinbutton'), |
|
+ 'value', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('pressure-threshold', |
|
+ this._builder.get_object('pressure_threshold_spinbutton'), |
|
+ 'value', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ //this._builder.get_object('animation_duration_spinbutton').set_value(this._settings.get_double('animation-time')); |
|
+ |
|
+ // Create dialog for intelligent autohide advanced settings |
|
+ this._builder.get_object('intelligent_autohide_button').connect('clicked', Lang.bind(this, function() { |
|
+ |
|
+ let dialog = new Gtk.Dialog({ title: __('Intelligent autohide customization'), |
|
+ transient_for: this.widget.get_toplevel(), |
|
+ use_header_bar: true, |
|
+ modal: true }); |
|
+ |
|
+ // GTK+ leaves positive values for application-defined response ids. |
|
+ // Use +1 for the reset action |
|
+ dialog.add_button(__('Reset to defaults'), 1); |
|
+ |
|
+ let box = this._builder.get_object('intelligent_autohide_advanced_settings_box'); |
|
+ dialog.get_content_area().add(box); |
|
+ |
|
+ this._settings.bind('intellihide', |
|
+ this._builder.get_object('intellihide_mode_box'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.GET); |
|
+ |
|
+ // intellihide mode |
|
+ |
|
+ let intellihideModeRadioButtons = [ |
|
+ this._builder.get_object('all_windows_radio_button'), |
|
+ this._builder.get_object('focus_application_windows_radio_button'), |
|
+ this._builder.get_object('maximized_windows_radio_button') |
|
+ ]; |
|
+ |
|
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true); |
|
+ |
|
+ this._settings.bind('autohide', |
|
+ this._builder.get_object('require_pressure_checkbutton'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.GET); |
|
+ |
|
+ this._settings.bind('autohide', |
|
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.GET); |
|
+ |
|
+ this._settings.bind('require-pressure-to-show', |
|
+ this._builder.get_object('show_timeout_spinbutton'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.INVERT_BOOLEAN); |
|
+ this._settings.bind('require-pressure-to-show', |
|
+ this._builder.get_object('show_timeout_label'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.INVERT_BOOLEAN); |
|
+ this._settings.bind('require-pressure-to-show', |
|
+ this._builder.get_object('pressure_threshold_spinbutton'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('require-pressure-to-show', |
|
+ this._builder.get_object('pressure_threshold_label'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ dialog.connect('response', Lang.bind(this, function(dialog, id) { |
|
+ if (id == 1) { |
|
+ // restore default settings for the relevant keys |
|
+ let keys = ['intellihide', 'autohide', 'intellihide-mode', 'autohide-in-fullscreen', 'require-pressure-to-show', |
|
+ 'animation-time', 'show-delay', 'hide-delay', 'pressure-threshold']; |
|
+ keys.forEach(function(val) { |
|
+ this._settings.set_value(val, this._settings.get_default_value(val)); |
|
+ }, this); |
|
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true); |
|
+ } else { |
|
+ // remove the settings box so it doesn't get destroyed; |
|
+ dialog.get_content_area().remove(box); |
|
+ dialog.destroy(); |
|
+ } |
|
+ return; |
|
+ })); |
|
+ |
|
+ dialog.show_all(); |
|
+ |
|
+ })); |
|
+ |
|
+ // size options |
|
+ this._builder.get_object('dock_size_scale').set_value(this._settings.get_double('height-fraction')); |
|
+ this._builder.get_object('dock_size_scale').add_mark(0.9, Gtk.PositionType.TOP, null); |
|
+ let icon_size_scale = this._builder.get_object('icon_size_scale'); |
|
+ icon_size_scale.set_range(8, DEFAULT_ICONS_SIZES[0]); |
|
+ icon_size_scale.set_value(this._settings.get_int('dash-max-icon-size')); |
|
+ DEFAULT_ICONS_SIZES.forEach(function(val) { |
|
+ icon_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 |
|
+ this._builder.get_object('dock_size_scale').set_value_pos(Gtk.PositionType.LEFT); |
|
+ icon_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 |
|
+ icon_size_scale.set_flippable(false); |
|
+ icon_size_scale.set_inverted(true); |
|
+ } |
|
+ |
|
+ this._settings.bind('icon-size-fixed', this._builder.get_object('icon_size_fixed_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('extend-height', this._builder.get_object('dock_size_extend_checkbutton'), 'active', Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('extend-height', this._builder.get_object('dock_size_scale'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN); |
|
+ |
|
+ |
|
+ // Apps panel |
|
+ |
|
+ this._settings.bind('show-running', |
|
+ this._builder.get_object('show_running_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('isolate-workspaces', |
|
+ this._builder.get_object('application_button_isolation_button'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('isolate-monitors', |
|
+ this._builder.get_object('application_button_monitor_isolation_button'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-windows-preview', |
|
+ this._builder.get_object('windows_preview_button'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('multi-monitor', |
|
+ this._builder.get_object('multi_monitor_button'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-favorites', |
|
+ this._builder.get_object('show_favorite_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-show-apps-button', |
|
+ this._builder.get_object('show_applications_button_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-apps-at-top', |
|
+ this._builder.get_object('application_button_first_button'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-show-apps-button', |
|
+ this._builder.get_object('application_button_first_button'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('animate-show-apps', |
|
+ this._builder.get_object('application_button_animation_button'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('show-show-apps-button', |
|
+ this._builder.get_object('application_button_animation_button'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ |
|
+ // Behavior panel |
|
+ |
|
+ 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('click_action_combo').set_active(this._settings.get_enum('click-action')); |
|
+ this._builder.get_object('click_action_combo').connect('changed', Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('click-action', widget.get_active()); |
|
+ })); |
|
+ |
|
+ this._builder.get_object('scroll_action_combo').set_active(this._settings.get_enum('scroll-action')); |
|
+ this._builder.get_object('scroll_action_combo').connect('changed', Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('scroll-action', widget.get_active()); |
|
+ })); |
|
+ |
|
+ this._builder.get_object('shift_click_action_combo').connect('changed', Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('shift-click-action', widget.get_active()); |
|
+ })); |
|
+ |
|
+ this._builder.get_object('middle_click_action_combo').connect('changed', Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('middle-click-action', widget.get_active()); |
|
+ })); |
|
+ this._builder.get_object('shift_middle_click_action_combo').connect('changed', Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('shift-middle-click-action', widget.get_active()); |
|
+ })); |
|
+ |
|
+ // Create dialog for number overlay options |
|
+ this._builder.get_object('overlay_button').connect('clicked', Lang.bind(this, function() { |
|
+ |
|
+ let dialog = new Gtk.Dialog({ title: __('Show dock and application numbers'), |
|
+ transient_for: this.widget.get_toplevel(), |
|
+ use_header_bar: true, |
|
+ modal: true }); |
|
+ |
|
+ // GTK+ leaves positive values for application-defined response ids. |
|
+ // Use +1 for the reset action |
|
+ dialog.add_button(__('Reset to defaults'), 1); |
|
+ |
|
+ let box = this._builder.get_object('box_overlay_shortcut'); |
|
+ dialog.get_content_area().add(box); |
|
+ |
|
+ this._builder.get_object('overlay_switch').set_active(this._settings.get_boolean('hotkeys-overlay')); |
|
+ this._builder.get_object('show_dock_switch').set_active(this._settings.get_boolean('hotkeys-show-dock')); |
|
+ |
|
+ // We need to update the shortcut 'strv' when the text is modified |
|
+ this._settings.connect('changed::shortcut-text', Lang.bind(this, function() {setShortcut(this._settings);})); |
|
+ this._settings.bind('shortcut-text', |
|
+ this._builder.get_object('shortcut_entry'), |
|
+ 'text', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ this._settings.bind('hotkeys-overlay', |
|
+ this._builder.get_object('overlay_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('hotkeys-show-dock', |
|
+ this._builder.get_object('show_dock_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('shortcut-timeout', |
|
+ this._builder.get_object('timeout_spinbutton'), |
|
+ 'value', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ dialog.connect('response', Lang.bind(this, function(dialog, id) { |
|
+ if (id == 1) { |
|
+ // restore default settings for the relevant keys |
|
+ let keys = ['shortcut-text', 'hotkeys-overlay', 'hotkeys-show-dock', 'shortcut-timeout']; |
|
+ keys.forEach(function(val) { |
|
+ this._settings.set_value(val, this._settings.get_default_value(val)); |
|
+ }, this); |
|
+ } else { |
|
+ // remove the settings box so it doesn't get destroyed; |
|
+ dialog.get_content_area().remove(box); |
|
+ dialog.destroy(); |
|
+ } |
|
+ return; |
|
+ })); |
|
+ |
|
+ dialog.show_all(); |
|
+ |
|
+ })); |
|
+ |
|
+ // Create dialog for middle-click options |
|
+ this._builder.get_object('middle_click_options_button').connect('clicked', Lang.bind(this, function() { |
|
+ |
|
+ let dialog = new Gtk.Dialog({ title: __('Customize middle-click behavior'), |
|
+ transient_for: this.widget.get_toplevel(), |
|
+ use_header_bar: true, |
|
+ modal: true }); |
|
+ |
|
+ // GTK+ leaves positive values for application-defined response ids. |
|
+ // Use +1 for the reset action |
|
+ dialog.add_button(__('Reset to defaults'), 1); |
|
+ |
|
+ let box = this._builder.get_object('box_middle_click_options'); |
|
+ dialog.get_content_area().add(box); |
|
+ |
|
+ this._builder.get_object('shift_click_action_combo').set_active(this._settings.get_enum('shift-click-action')); |
|
+ |
|
+ this._builder.get_object('middle_click_action_combo').set_active(this._settings.get_enum('middle-click-action')); |
|
+ |
|
+ this._builder.get_object('shift_middle_click_action_combo').set_active(this._settings.get_enum('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.connect('response', Lang.bind(this, function(dialog, id) { |
|
+ if (id == 1) { |
|
+ // 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(this._settings.get_enum('shift-click-action')); |
|
+ this._builder.get_object('middle_click_action_combo').set_active(this._settings.get_enum('middle-click-action')); |
|
+ this._builder.get_object('shift_middle_click_action_combo').set_active(this._settings.get_enum('shift-middle-click-action')); |
|
+ } else { |
|
+ // remove the settings box so it doesn't get destroyed; |
|
+ dialog.get_content_area().remove(box); |
|
+ dialog.destroy(); |
|
+ } |
|
+ return; |
|
+ })); |
|
+ |
|
+ dialog.show_all(); |
|
+ |
|
+ })); |
|
+ |
|
+ // Appearance Panel |
|
+ |
|
+ this._settings.bind('apply-custom-theme', this._builder.get_object('customize_theme'), 'sensitive', Gio.SettingsBindFlags.INVERT_BOOLEAN | Gio.SettingsBindFlags.GET); |
|
+ this._settings.bind('apply-custom-theme', this._builder.get_object('builtin_theme_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('custom-theme-shrink', this._builder.get_object('shrink_dash_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ // Running indicators |
|
+ this._builder.get_object('running_indicators_combo').set_active( |
|
+ this._settings.get_enum('running-indicator-style') |
|
+ ); |
|
+ this._builder.get_object('running_indicators_combo').connect( |
|
+ 'changed', |
|
+ Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('running-indicator-style', widget.get_active()); |
|
+ }) |
|
+ ); |
|
+ |
|
+ if (this._settings.get_enum('running-indicator-style') == RunningIndicatorStyle.DEFAULT) |
|
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false); |
|
+ |
|
+ this._settings.connect('changed::running-indicator-style', Lang.bind(this, function() { |
|
+ if (this._settings.get_enum('running-indicator-style') == RunningIndicatorStyle.DEFAULT) |
|
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false); |
|
+ else |
|
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(true); |
|
+ })); |
|
+ |
|
+ // Create dialog for running indicators advanced settings |
|
+ this._builder.get_object('running_indicators_advance_settings_button').connect('clicked', Lang.bind(this, function() { |
|
+ |
|
+ let dialog = new Gtk.Dialog({ title: __('Customize running indicators'), |
|
+ transient_for: this.widget.get_toplevel(), |
|
+ use_header_bar: true, |
|
+ modal: true }); |
|
+ |
|
+ let box = this._builder.get_object('running_dots_advance_settings_box'); |
|
+ dialog.get_content_area().add(box); |
|
+ |
|
+ this._settings.bind('running-indicator-dominant-color', |
|
+ this._builder.get_object('dominant_color_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ this._settings.bind('custom-theme-customize-running-dots', |
|
+ this._builder.get_object('dot_style_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('custom-theme-customize-running-dots', |
|
+ this._builder.get_object('dot_style_settings_box'), |
|
+ 'sensitive', Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ let rgba = new Gdk.RGBA(); |
|
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-color')); |
|
+ this._builder.get_object('dot_color_colorbutton').set_rgba(rgba); |
|
+ |
|
+ this._builder.get_object('dot_color_colorbutton').connect('notify::color', Lang.bind(this, function(button) { |
|
+ let rgba = button.get_rgba(); |
|
+ let css = rgba.to_string(); |
|
+ let hexString = cssHexString(css); |
|
+ this._settings.set_string('custom-theme-running-dots-color', hexString); |
|
+ })); |
|
+ |
|
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-border-color')); |
|
+ this._builder.get_object('dot_border_color_colorbutton').set_rgba(rgba); |
|
+ |
|
+ this._builder.get_object('dot_border_color_colorbutton').connect('notify::color', Lang.bind(this, function(button) { |
|
+ let rgba = button.get_rgba(); |
|
+ let css = rgba.to_string(); |
|
+ let hexString = cssHexString(css); |
|
+ this._settings.set_string('custom-theme-running-dots-border-color', hexString); |
|
+ })); |
|
+ |
|
+ this._settings.bind('custom-theme-running-dots-border-width', |
|
+ this._builder.get_object('dot_border_width_spin_button'), |
|
+ 'value', |
|
+ Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ |
|
+ dialog.connect('response', Lang.bind(this, function(dialog, id) { |
|
+ // remove the settings box so it doesn't get destroyed; |
|
+ dialog.get_content_area().remove(box); |
|
+ dialog.destroy(); |
|
+ return; |
|
+ })); |
|
+ |
|
+ dialog.show_all(); |
|
+ |
|
+ })); |
|
+ |
|
+ this._settings.bind('custom-background-color', this._builder.get_object('custom_background_color_switch'), 'active', Gio.SettingsBindFlags.DEFAULT); |
|
+ this._settings.bind('custom-background-color', this._builder.get_object('custom_background_color'), 'sensitive', Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ let rgba = new Gdk.RGBA(); |
|
+ rgba.parse(this._settings.get_string('background-color')); |
|
+ this._builder.get_object('custom_background_color').set_rgba(rgba); |
|
+ |
|
+ this._builder.get_object('custom_background_color').connect('notify::color', Lang.bind(this, function(button) { |
|
+ let rgba = button.get_rgba(); |
|
+ let css = rgba.to_string(); |
|
+ let hexString = cssHexString(css); |
|
+ this._settings.set_string('background-color', hexString); |
|
+ })); |
|
+ |
|
+ // Opacity |
|
+ this._builder.get_object('customize_opacity_combo').set_active( |
|
+ this._settings.get_enum('transparency-mode') |
|
+ ); |
|
+ this._builder.get_object('customize_opacity_combo').connect( |
|
+ 'changed', |
|
+ Lang.bind (this, function(widget) { |
|
+ this._settings.set_enum('transparency-mode', widget.get_active()); |
|
+ }) |
|
+ ); |
|
+ |
|
+ this._builder.get_object('custom_opacity_scale').set_value(this._settings.get_double('background-opacity')); |
|
+ |
|
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED) |
|
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false); |
|
+ |
|
+ this._settings.connect('changed::transparency-mode', Lang.bind(this, function() { |
|
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED) |
|
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false); |
|
+ else |
|
+ this._builder.get_object('custom_opacity_scale').set_sensitive(true); |
|
+ })); |
|
+ |
|
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.ADAPTIVE && |
|
+ this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC) { |
|
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false); |
|
+ } |
|
+ |
|
+ this._settings.connect('changed::transparency-mode', Lang.bind(this, function() { |
|
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.ADAPTIVE && |
|
+ this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC) { |
|
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false); |
|
+ } |
|
+ else { |
|
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(true); |
|
+ } |
|
+ })); |
|
+ |
|
+ // Create dialog for transparency advanced settings |
|
+ this._builder.get_object('dynamic_opacity_button').connect('clicked', Lang.bind(this, function() { |
|
+ |
|
+ let dialog = new Gtk.Dialog({ title: __('Cutomize opacity'), |
|
+ transient_for: this.widget.get_toplevel(), |
|
+ use_header_bar: true, |
|
+ modal: true }); |
|
+ |
|
+ let box = this._builder.get_object('advanced_transparency_dialog'); |
|
+ dialog.get_content_area().add(box); |
|
+ |
|
+ this._settings.bind( |
|
+ 'customize-alphas', |
|
+ this._builder.get_object('customize_alphas_switch'), |
|
+ 'active', |
|
+ Gio.SettingsBindFlags.DEFAULT |
|
+ ); |
|
+ this._settings.bind( |
|
+ 'customize-alphas', |
|
+ this._builder.get_object('min_alpha_scale'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.DEFAULT |
|
+ ); |
|
+ this._settings.bind( |
|
+ 'customize-alphas', |
|
+ this._builder.get_object('max_alpha_scale'), |
|
+ 'sensitive', |
|
+ Gio.SettingsBindFlags.DEFAULT |
|
+ ); |
|
+ |
|
+ this._builder.get_object('min_alpha_scale').set_value( |
|
+ this._settings.get_double('min-alpha') |
|
+ ); |
|
+ this._builder.get_object('max_alpha_scale').set_value( |
|
+ this._settings.get_double('max-alpha') |
|
+ ); |
|
+ |
|
+ dialog.connect('response', Lang.bind(this, function(dialog, id) { |
|
+ // remove the settings box so it doesn't get destroyed; |
|
+ dialog.get_content_area().remove(box); |
|
+ dialog.destroy(); |
|
+ return; |
|
+ })); |
|
+ |
|
+ dialog.show_all(); |
|
+ })); |
|
+ |
|
+ |
|
+ this._settings.bind('unity-backlit-items', |
|
+ this._builder.get_object('unity_backlit_items_switch'), |
|
+ 'active', Gio.SettingsBindFlags.DEFAULT |
|
+ ); |
|
+ |
|
+ this._settings.bind('force-straight-corner', |
|
+ this._builder.get_object('force_straight_corner_switch'), |
|
+ 'active', Gio.SettingsBindFlags.DEFAULT); |
|
+ |
|
+ // About Panel |
|
+ |
|
+ this._builder.get_object('extension_version').set_label(Me.metadata.version.toString()); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Object containing all signals defined in the glade file |
|
+ */ |
|
+ _SignalHandler: { |
|
+ dock_display_combo_changed_cb: function(combo) { |
|
+ this._settings.set_int('preferred-monitor', this._monitors[combo.get_active()]); |
|
+ }, |
|
+ |
|
+ position_top_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('dock-position', 0); |
|
+ }, |
|
+ |
|
+ position_right_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('dock-position', 1); |
|
+ }, |
|
+ |
|
+ position_bottom_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('dock-position', 2); |
|
+ }, |
|
+ |
|
+ position_left_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('dock-position', 3); |
|
+ }, |
|
+ |
|
+ icon_size_combo_changed_cb: function(combo) { |
|
+ this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]); |
|
+ }, |
|
+ |
|
+ dock_size_scale_format_value_cb: function(scale, value) { |
|
+ return Math.round(value*100)+ ' %'; |
|
+ }, |
|
+ |
|
+ dock_size_scale_value_changed_cb: function(scale) { |
|
+ // Avoid settings the size consinuosly |
|
+ if (this._dock_size_timeout > 0) |
|
+ Mainloop.source_remove(this._dock_size_timeout); |
|
+ |
|
+ this._dock_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { |
|
+ this._settings.set_double('height-fraction', scale.get_value()); |
|
+ this._dock_size_timeout = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ }, |
|
+ |
|
+ icon_size_scale_format_value_cb: function(scale, value) { |
|
+ return value+ ' px'; |
|
+ }, |
|
+ |
|
+ icon_size_scale_value_changed_cb: function(scale) { |
|
+ // Avoid settings the size consinuosly |
|
+ if (this._icon_size_timeout > 0) |
|
+ Mainloop.source_remove(this._icon_size_timeout); |
|
+ |
|
+ this._icon_size_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { |
|
+ this._settings.set_int('dash-max-icon-size', scale.get_value()); |
|
+ this._icon_size_timeout = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ }, |
|
+ |
|
+ custom_opacity_scale_value_changed_cb: function(scale) { |
|
+ // Avoid settings the opacity consinuosly as it's change is animated |
|
+ if (this._opacity_timeout > 0) |
|
+ Mainloop.source_remove(this._opacity_timeout); |
|
+ |
|
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { |
|
+ this._settings.set_double('background-opacity', scale.get_value()); |
|
+ this._opacity_timeout = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ }, |
|
+ |
|
+ min_opacity_scale_value_changed_cb: function(scale) { |
|
+ // Avoid settings the opacity consinuosly as it's change is animated |
|
+ if (this._opacity_timeout > 0) |
|
+ Mainloop.source_remove(this._opacity_timeout); |
|
+ |
|
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { |
|
+ this._settings.set_double('min-alpha', scale.get_value()); |
|
+ this._opacity_timeout = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ }, |
|
+ |
|
+ max_opacity_scale_value_changed_cb: function(scale) { |
|
+ // Avoid settings the opacity consinuosly as it's change is animated |
|
+ if (this._opacity_timeout > 0) |
|
+ Mainloop.source_remove(this._opacity_timeout); |
|
+ |
|
+ this._opacity_timeout = Mainloop.timeout_add(SCALE_UPDATE_TIMEOUT, Lang.bind(this, function() { |
|
+ this._settings.set_double('max-alpha', scale.get_value()); |
|
+ this._opacity_timeout = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ }, |
|
+ |
|
+ custom_opacity_scale_format_value_cb: function(scale, value) { |
|
+ return Math.round(value*100) + ' %'; |
|
+ }, |
|
+ |
|
+ min_opacity_scale_format_value_cb: function(scale, value) { |
|
+ return Math.round(value*100) + ' %'; |
|
+ }, |
|
+ |
|
+ max_opacity_scale_format_value_cb: function(scale, value) { |
|
+ return Math.round(value*100) + ' %'; |
|
+ }, |
|
+ |
|
+ all_windows_radio_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('intellihide-mode', 0); |
|
+ }, |
|
+ |
|
+ focus_application_windows_radio_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('intellihide-mode', 1); |
|
+ }, |
|
+ |
|
+ maximized_windows_radio_button_toggled_cb: function(button) { |
|
+ if (button.get_active()) |
|
+ this._settings.set_enum('intellihide-mode', 2); |
|
+ } |
|
+ } |
|
+}); |
|
+ |
|
+function init() { |
|
+ Convenience.initTranslations(); |
|
+} |
|
+ |
|
+function buildPrefsWidget() { |
|
+ let settings = new Settings(); |
|
+ let widget = settings.widget; |
|
+ widget.show_all(); |
|
+ return widget; |
|
+} |
|
diff --git a/extensions/dash-to-dock/stylesheet.css b/extensions/dash-to-dock/stylesheet.css |
|
new file mode 100644 |
|
index 0000000..6e9bf38 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/stylesheet.css |
|
@@ -0,0 +1,109 @@ |
|
+/* Shrink the dash by reducing padding and border radius */ |
|
+#dashtodockContainer.shrink #dash, |
|
+#dashtodockContainer.dashtodock #dash { |
|
+ border:1px; |
|
+ padding:0px; |
|
+} |
|
+ |
|
+#dashtodockContainer.shrink.left #dash, |
|
+#dashtodockContainer.dashtodock.left #dash { |
|
+ border-left: 0px; |
|
+ border-radius: 0px 9px 9px 0px; |
|
+} |
|
+ |
|
+ |
|
+#dashtodockContainer.shrink.right #dash, |
|
+#dashtodockContainer.dashtodock.right #dash { |
|
+ border-right: 0px; |
|
+ border-radius: 9px 0px 0px 9px; |
|
+} |
|
+ |
|
+ |
|
+#dashtodockContainer.shrink.top #dash, |
|
+#dashtodockContainer.dashtodock.top #dash { |
|
+ border-top: 0px; |
|
+ border-radius: 0px 0px 9px 9px; |
|
+} |
|
+ |
|
+#dashtodockContainer.shrink.bottom #dash, |
|
+#dashtodockContainer.dashtodock.bottom #dash { |
|
+ border-bottom: 0px; |
|
+ border-radius: 9px 9px 0px 0px; |
|
+} |
|
+ |
|
+#dashtodockContainer.straight-corner #dash, |
|
+#dashtodockContainer.shrink.straight-corner #dash { |
|
+ border-radius: 0px; |
|
+} |
|
+ |
|
+/* Scrollview style */ |
|
+.bottom #dashtodockDashScrollview, |
|
+.top #dashtodockDashScrollview { |
|
+ -st-hfade-offset: 24px; |
|
+} |
|
+ |
|
+.left #dashtodockDashScrollview, |
|
+.right #dashtodockDashScrollview { |
|
+ -st-vfade-offset: 24px; |
|
+} |
|
+ |
|
+#dashtodockContainer.running-dots .dash-item-container > StButton, |
|
+#dashtodockContainer.dashtodock .dash-item-container > StButton { |
|
+ transition-duration: 250; |
|
+ background-size: contain; |
|
+} |
|
+ |
|
+#dashtodockContainer.shrink .dash-item-container > StButton, |
|
+#dashtodockContainer.dashtodock .dash-item-container > StButton { |
|
+ padding: 1px 2px; |
|
+} |
|
+ |
|
+/* Dash height extended to the whole available vertical space */ |
|
+#dashtodockContainer.extended.top #dash, |
|
+#dashtodockContainer.extended.right #dash, |
|
+#dashtodockContainer.extended.bottom #dash, |
|
+#dashtodockContainer.extended.left #dash { |
|
+ border-radius: 0; |
|
+} |
|
+ |
|
+#dashtodockContainer.extended.top #dash, |
|
+#dashtodockContainer.extended.bottom #dash { |
|
+ border-left:0px; |
|
+ border-right:0px; |
|
+} |
|
+ |
|
+#dashtodockContainer.extended.right #dash, |
|
+#dashtodockContainer.extended.left #dash { |
|
+ border-top:0px; |
|
+ border-bottom:0px; |
|
+} |
|
+ |
|
+/* Running and focused application style */ |
|
+ |
|
+#dashtodockContainer.running-dots .app-well-app.running > .overview-icon, |
|
+#dashtodockContainer.dashtodock .app-well-app.running > .overview-icon { |
|
+ background-image:none; |
|
+} |
|
+ |
|
+ |
|
+#dashtodockContainer.running-dots .app-well-app.focused .overview-icon, |
|
+#dashtodockContainer.dashtodock .app-well-app.focused .overview-icon { |
|
+ background-color: rgba(238, 238, 236, 0.1); |
|
+} |
|
+ |
|
+#dashtodockContainer.dashtodock #dash { |
|
+ background: #2e3436; |
|
+} |
|
+ |
|
+#dashtodockContainer .number-overlay { |
|
+ color: rgba(255,255,255,1); |
|
+ background-color: rgba(0,0,0,0.8); |
|
+ text-align: center; |
|
+} |
|
+ |
|
+#dashtodockPreviewSeparator.popup-separator-menu-item-horizontal { |
|
+ width: 1px; |
|
+ height: auto; |
|
+ border-right-width: 1px; |
|
+ margin: 32px 0px; |
|
+} |
|
diff --git a/extensions/dash-to-dock/theming.js b/extensions/dash-to-dock/theming.js |
|
new file mode 100644 |
|
index 0000000..4b18d1a |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/theming.js |
|
@@ -0,0 +1,672 @@ |
|
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const Gio = imports.gi.Gio; |
|
+const GLib = imports.gi.GLib; |
|
+const Gtk = imports.gi.Gtk; |
|
+const Signals = imports.signals; |
|
+const Lang = imports.lang; |
|
+const Meta = imports.gi.Meta; |
|
+const Shell = imports.gi.Shell; |
|
+const St = imports.gi.St; |
|
+const Mainloop = imports.mainloop; |
|
+ |
|
+const AppDisplay = imports.ui.appDisplay; |
|
+const AppFavorites = imports.ui.appFavorites; |
|
+const Dash = imports.ui.dash; |
|
+const DND = imports.ui.dnd; |
|
+const IconGrid = imports.ui.iconGrid; |
|
+const Main = imports.ui.main; |
|
+const PopupMenu = imports.ui.popupMenu; |
|
+const Tweener = imports.ui.tweener; |
|
+const Util = imports.misc.util; |
|
+const Workspace = imports.ui.workspace; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Dock = Me.imports.docking; |
|
+const Utils = Me.imports.utils; |
|
+ |
|
+/* |
|
+ * DEFAULT: transparency given by theme |
|
+ * FIXED: constant transparency chosen by user |
|
+ * ADAPTIVE: apply 'transparent' style to dock AND panel when |
|
+ * no windows are close to the dock OR panel. |
|
+ * When dock is hidden, the dock 'transparent' style only |
|
+ * apply to itself. |
|
+ * DYNAMIC: apply 'transparent' style when no windows are close to the dock |
|
+ * */ |
|
+const TransparencyMode = { |
|
+ DEFAULT: 0, |
|
+ FIXED: 1, |
|
+ ADAPTIVE: 2, |
|
+ DYNAMIC: 3 |
|
+}; |
|
+ |
|
+/** |
|
+ * Manage theme customization and custom theme support |
|
+ */ |
|
+var ThemeManager = new Lang.Class({ |
|
+ Name: 'DashToDock.ThemeManager', |
|
+ |
|
+ _init: function(settings, dock) { |
|
+ this._settings = settings; |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ this._bindSettingsChanges(); |
|
+ this._actor = dock.actor; |
|
+ this._dash = dock.dash; |
|
+ |
|
+ // initialize colors with generic values |
|
+ this._customizedBackground = {red: 0, green: 0, blue: 0, alpha: 0}; |
|
+ this._customizedBorder = {red: 0, green: 0, blue: 0, alpha: 0}; |
|
+ this._transparency = new Transparency(this._settings, dock); |
|
+ |
|
+ this._signalsHandler.add([ |
|
+ // When theme changes re-obtain default background color |
|
+ St.ThemeContext.get_for_stage (global.stage), |
|
+ 'changed', |
|
+ Lang.bind(this, this.updateCustomTheme) |
|
+ ], [ |
|
+ // update :overview pseudoclass |
|
+ Main.overview, |
|
+ 'showing', |
|
+ Lang.bind(this, this._onOverviewShowing) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'hiding', |
|
+ Lang.bind(this, this._onOverviewHiding) |
|
+ ]); |
|
+ |
|
+ this._updateCustomStyleClasses(); |
|
+ |
|
+ // destroy themeManager when the managed actor is destroyed (e.g. extension unload) |
|
+ // in order to disconnect signals |
|
+ this._actor.connect('destroy', Lang.bind(this, this.destroy)); |
|
+ |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this._signalsHandler.destroy(); |
|
+ this._transparency.destroy(); |
|
+ }, |
|
+ |
|
+ _onOverviewShowing: function() { |
|
+ this._actor.add_style_pseudo_class('overview'); |
|
+ }, |
|
+ |
|
+ _onOverviewHiding: function() { |
|
+ this._actor.remove_style_pseudo_class('overview'); |
|
+ }, |
|
+ |
|
+ _updateDashOpacity: function() { |
|
+ let newAlpha = this._settings.get_double('background-opacity'); |
|
+ |
|
+ let [backgroundColor, borderColor] = this._getDefaultColors(); |
|
+ |
|
+ if (backgroundColor==null) |
|
+ return; |
|
+ |
|
+ // Get the background and border alphas. We check the background alpha |
|
+ // for a minimum of .001 to prevent division by 0 errors |
|
+ let backgroundAlpha = Math.max(Math.round(backgroundColor.alpha/2.55)/100, .001); |
|
+ let borderAlpha = Math.round(borderColor.alpha/2.55)/100; |
|
+ |
|
+ // The border and background alphas should remain in sync |
|
+ // We also limit the borderAlpha to a maximum of 1 (full opacity) |
|
+ borderAlpha = Math.min((borderAlpha/backgroundAlpha)*newAlpha, 1); |
|
+ |
|
+ this._customizedBackground = 'rgba(' + |
|
+ backgroundColor.red + ',' + |
|
+ backgroundColor.green + ',' + |
|
+ backgroundColor.blue + ',' + |
|
+ newAlpha + ')'; |
|
+ |
|
+ this._customizedBorder = 'rgba(' + |
|
+ borderColor.red + ',' + |
|
+ borderColor.green + ',' + |
|
+ borderColor.blue + ',' + |
|
+ borderAlpha + ')'; |
|
+ |
|
+ }, |
|
+ |
|
+ _getDefaultColors: function() { |
|
+ // Prevent shell crash if the actor is not on the stage. |
|
+ // It happens enabling/disabling repeatedly the extension |
|
+ if (!this._dash._container.get_stage()) |
|
+ return [null, null]; |
|
+ |
|
+ // Remove custom style |
|
+ let oldStyle = this._dash._container.get_style(); |
|
+ this._dash._container.set_style(null); |
|
+ |
|
+ let themeNode = this._dash._container.get_theme_node(); |
|
+ this._dash._container.set_style(oldStyle); |
|
+ |
|
+ let backgroundColor = themeNode.get_background_color(); |
|
+ |
|
+ // Just in case the theme has different border colors .. |
|
+ // We want to find the inside border-color of the dock because it is |
|
+ // the side most visible to the user. We do this by finding the side |
|
+ // opposite the position |
|
+ let position = Utils.getPosition(this._settings); |
|
+ let side = position + 2; |
|
+ if (side > 3) |
|
+ side = Math.abs(side - 4); |
|
+ |
|
+ let borderColor = themeNode.get_border_color(side); |
|
+ |
|
+ return [backgroundColor, borderColor]; |
|
+ }, |
|
+ |
|
+ _updateDashColor: function() { |
|
+ // Retrieve the color. If needed we will adjust it before passing it to |
|
+ // this._transparency. |
|
+ let [backgroundColor, borderColor] = this._getDefaultColors(); |
|
+ |
|
+ if (backgroundColor==null) |
|
+ return; |
|
+ |
|
+ if (this._settings.get_boolean('custom-background-color')) { |
|
+ // When applying a custom color, we need to check the alpha value, |
|
+ // if not the opacity will always be overridden by the color below. |
|
+ // Note that if using 'adaptive' or 'dynamic' transparency modes, |
|
+ // the opacity will be set by the opaque/transparent styles anyway. |
|
+ let newAlpha = Math.round(backgroundColor.alpha/2.55)/100; |
|
+ if (this._settings.get_enum('transparency-mode') == TransparencyMode.FIXED) |
|
+ newAlpha = this._settings.get_double('background-opacity'); |
|
+ |
|
+ backgroundColor = Clutter.color_from_string(this._settings.get_string('background-color'))[1]; |
|
+ this._customizedBackground = 'rgba(' + |
|
+ backgroundColor.red + ',' + |
|
+ backgroundColor.green + ',' + |
|
+ backgroundColor.blue + ',' + |
|
+ newAlpha + ')'; |
|
+ |
|
+ this._customizedBorder = this._customizedBackground; |
|
+ } |
|
+ this._transparency.setColor(backgroundColor); |
|
+ }, |
|
+ |
|
+ _updateCustomStyleClasses: function() { |
|
+ if (this._settings.get_boolean('apply-custom-theme')) |
|
+ this._actor.add_style_class_name('dashtodock'); |
|
+ else |
|
+ this._actor.remove_style_class_name('dashtodock'); |
|
+ |
|
+ if (this._settings.get_boolean('custom-theme-shrink')) |
|
+ this._actor.add_style_class_name('shrink'); |
|
+ else |
|
+ this._actor.remove_style_class_name('shrink'); |
|
+ |
|
+ if (this._settings.get_enum('running-indicator-style') !== 0) |
|
+ this._actor.add_style_class_name('running-dots'); |
|
+ else |
|
+ this._actor.remove_style_class_name('running-dots'); |
|
+ |
|
+ // If not the built-in theme option is not selected |
|
+ if (!this._settings.get_boolean('apply-custom-theme')) { |
|
+ if (this._settings.get_boolean('force-straight-corner')) |
|
+ this._actor.add_style_class_name('straight-corner'); |
|
+ else |
|
+ this._actor.remove_style_class_name('straight-corner'); |
|
+ } else { |
|
+ this._actor.remove_style_class_name('straight-corner'); |
|
+ } |
|
+ }, |
|
+ |
|
+ updateCustomTheme: function() { |
|
+ this._updateCustomStyleClasses(); |
|
+ this._updateDashOpacity(); |
|
+ this._updateDashColor(); |
|
+ this._adjustTheme(); |
|
+ this._dash._redisplay(); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Reimported back and adapted from atomdock |
|
+ */ |
|
+ _adjustTheme: function() { |
|
+ // Prevent shell crash if the actor is not on the stage. |
|
+ // It happens enabling/disabling repeatedly the extension |
|
+ if (!this._dash._container.get_stage()) |
|
+ return; |
|
+ |
|
+ // Remove prior style edits |
|
+ this._dash._container.set_style(null); |
|
+ this._transparency.disable(); |
|
+ |
|
+ // If built-in theme is enabled do nothing else |
|
+ if (this._settings.get_boolean('apply-custom-theme')) |
|
+ return; |
|
+ |
|
+ let newStyle = ''; |
|
+ let position = Utils.getPosition(this._settings); |
|
+ |
|
+ if (!this._settings.get_boolean('custom-theme-shrink')) { |
|
+ // obtain theme border settings |
|
+ let themeNode = this._dash._container.get_theme_node(); |
|
+ let borderColor = themeNode.get_border_color(St.Side.TOP); |
|
+ let borderWidth = themeNode.get_border_width(St.Side.TOP); |
|
+ let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT); |
|
+ |
|
+ // We're copying border and corner styles to left border and top-left |
|
+ // corner, also removing bottom border and bottom-right corner styles |
|
+ let borderInner = ''; |
|
+ let borderRadiusValue = ''; |
|
+ let borderMissingStyle = ''; |
|
+ |
|
+ if (this._rtl && (position != St.Side.RIGHT)) |
|
+ borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' + |
|
+ borderColor.to_string() + ';'; |
|
+ else if (!this._rtl && (position != St.Side.LEFT)) |
|
+ borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' + |
|
+ borderColor.to_string() + ';'; |
|
+ |
|
+ switch (position) { |
|
+ case St.Side.LEFT: |
|
+ borderInner = 'border-left'; |
|
+ borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;'; |
|
+ break; |
|
+ case St.Side.RIGHT: |
|
+ borderInner = 'border-right'; |
|
+ borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;'; |
|
+ break; |
|
+ case St.Side.TOP: |
|
+ borderInner = 'border-top'; |
|
+ borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;'; |
|
+ break; |
|
+ case St.Side.BOTTOM: |
|
+ borderInner = 'border-bottom'; |
|
+ borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;'; |
|
+ break; |
|
+ } |
|
+ |
|
+ newStyle = borderInner + ': none;' + |
|
+ 'border-radius: ' + borderRadiusValue + |
|
+ borderMissingStyle; |
|
+ |
|
+ // I do call set_style possibly twice so that only the background gets the transition. |
|
+ // The transition-property css rules seems to be unsupported |
|
+ this._dash._container.set_style(newStyle); |
|
+ } |
|
+ |
|
+ // Customize background |
|
+ let fixedTransparency = this._settings.get_enum('transparency-mode') == TransparencyMode.FIXED; |
|
+ let defaultTransparency = this._settings.get_enum('transparency-mode') == TransparencyMode.DEFAULT; |
|
+ if (!defaultTransparency && !fixedTransparency) { |
|
+ this._transparency.enable(); |
|
+ } |
|
+ else if (!defaultTransparency || this._settings.get_boolean('custom-background-color')) { |
|
+ newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' + |
|
+ 'border-color:'+ this._customizedBorder + '; ' + |
|
+ 'transition-delay: 0s; transition-duration: 0.250s;'; |
|
+ this._dash._container.set_style(newStyle); |
|
+ } |
|
+ }, |
|
+ |
|
+ _bindSettingsChanges: function() { |
|
+ let keys = ['transparency-mode', |
|
+ 'customize-alphas', |
|
+ 'min-alpha', |
|
+ 'max-alpha', |
|
+ 'background-opacity', |
|
+ 'custom-background-color', |
|
+ 'background-color', |
|
+ 'apply-custom-theme', |
|
+ 'custom-theme-shrink', |
|
+ 'custom-theme-running-dots', |
|
+ 'extend-height', |
|
+ 'force-straight-corner']; |
|
+ |
|
+ keys.forEach(function(key) { |
|
+ this._signalsHandler.add([ |
|
+ this._settings, |
|
+ 'changed::' + key, |
|
+ Lang.bind(this, this.updateCustomTheme) |
|
+ ]); |
|
+ }, this); |
|
+ } |
|
+}); |
|
+ |
|
+/** |
|
+ * The following class is based on the following upstream commit: |
|
+ * https://git.gnome.org/browse/gnome-shell/commit/?id=447bf55e45b00426ed908b1b1035f472c2466956 |
|
+ * Transparency when free-floating |
|
+ */ |
|
+const Transparency = new Lang.Class({ |
|
+ Name: 'DashToDock.Transparency', |
|
+ |
|
+ _init: function(settings, dock) { |
|
+ this._settings = settings; |
|
+ this._dash = dock.dash; |
|
+ this._actor = this._dash._container; |
|
+ this._dockActor = dock.actor; |
|
+ this._dock = dock; |
|
+ this._panel = Main.panel; |
|
+ this._position = Utils.getPosition(this._settings); |
|
+ |
|
+ this._backgroundColor = '0,0,0'; |
|
+ this._transparentAlpha = '0.2'; |
|
+ this._opaqueAlpha = '1'; |
|
+ this._transparentAlphaBorder = '0.1'; |
|
+ this._opaqueAlphaBorder = '0.5'; |
|
+ this._transparentTransition = '0ms'; |
|
+ this._opaqueTransition = '0ms'; |
|
+ |
|
+ this._updateStyles(); |
|
+ |
|
+ this._signalsHandler = new Utils.GlobalSignalsHandler(); |
|
+ this._injectionsHandler = new Utils.InjectionsHandler(); |
|
+ this._trackedWindows = new Map(); |
|
+ }, |
|
+ |
|
+ enable: function() { |
|
+ // ensure I never double-register/inject |
|
+ // although it should never happen |
|
+ this.disable(); |
|
+ |
|
+ this._signalsHandler.addWithLabel('transparency', [ |
|
+ global.window_group, |
|
+ 'actor-added', |
|
+ Lang.bind(this, this._onWindowActorAdded) |
|
+ ], [ |
|
+ global.window_group, |
|
+ 'actor-removed', |
|
+ Lang.bind(this, this._onWindowActorRemoved) |
|
+ ], [ |
|
+ global.window_manager, |
|
+ 'switch-workspace', |
|
+ Lang.bind(this, this._updateSolidStyle) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'hiding', |
|
+ Lang.bind(this, this._updateSolidStyle) |
|
+ ], [ |
|
+ Main.overview, |
|
+ 'showing', |
|
+ Lang.bind(this, this._updateSolidStyle) |
|
+ ]); |
|
+ |
|
+ // Window signals |
|
+ global.get_window_actors().forEach(function(win) { |
|
+ // An irrelevant window actor ('Gnome-shell') produces an error when the signals are |
|
+ // disconnected, therefore do not add signals to it. |
|
+ if (win.get_meta_window().get_wm_class() !== 'Gnome-shell') |
|
+ this._onWindowActorAdded(null, win); |
|
+ }, this); |
|
+ |
|
+ if (this._settings.get_enum('transparency-mode') === TransparencyMode.ADAPTIVE) |
|
+ this._enableAdaptive(); |
|
+ |
|
+ if (this._actor.get_stage()) |
|
+ this._updateSolidStyle(); |
|
+ |
|
+ this.emit('transparency-enabled'); |
|
+ }, |
|
+ |
|
+ disable: function() { |
|
+ this._disableAdaptive(); |
|
+ |
|
+ // ensure I never double-register/inject |
|
+ // although it should never happen |
|
+ this._signalsHandler.removeWithLabel('transparency'); |
|
+ |
|
+ for (let key of this._trackedWindows.keys()) |
|
+ this._trackedWindows.get(key).forEach(id => { |
|
+ key.disconnect(id); |
|
+ }); |
|
+ this._trackedWindows.clear(); |
|
+ |
|
+ this.emit('transparency-disabled'); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this.disable(); |
|
+ this._signalsHandler.destroy(); |
|
+ this._injectionsHandler.destroy(); |
|
+ }, |
|
+ |
|
+ _onWindowActorAdded: function(container, metaWindowActor) { |
|
+ let signalIds = []; |
|
+ ['allocation-changed', 'notify::visible'].forEach(s => { |
|
+ signalIds.push(metaWindowActor.connect(s, Lang.bind(this, this._updateSolidStyle))); |
|
+ }); |
|
+ this._trackedWindows.set(metaWindowActor, signalIds); |
|
+ }, |
|
+ |
|
+ _onWindowActorRemoved: function(container, metaWindowActor) { |
|
+ if (!this._trackedWindows.get(metaWindowActor)) |
|
+ return; |
|
+ |
|
+ this._trackedWindows.get(metaWindowActor).forEach(id => { |
|
+ metaWindowActor.disconnect(id); |
|
+ }); |
|
+ this._trackedWindows.delete(metaWindowActor); |
|
+ this._updateSolidStyle(); |
|
+ }, |
|
+ |
|
+ _updateSolidStyle: function() { |
|
+ let isNear = this._dockIsNear() || this._panelIsNear(); |
|
+ if (isNear) { |
|
+ this._actor.set_style(this._opaque_style); |
|
+ if (this._panel._updateSolidStyle && this._adaptiveEnabled) { |
|
+ if (this._settings.get_boolean('dock-fixed') || this._panelIsNear()) |
|
+ this._panel._addStyleClassName('solid'); |
|
+ else |
|
+ this._panel._removeStyleClassName('solid'); |
|
+ } |
|
+ } |
|
+ else { |
|
+ this._actor.set_style(this._transparent_style); |
|
+ if (this._panel._updateSolidStyle && this._adaptiveEnabled) |
|
+ this._panel._removeStyleClassName('solid'); |
|
+ } |
|
+ |
|
+ this.emit('solid-style-updated', isNear); |
|
+ }, |
|
+ |
|
+ _dockIsNear: function() { |
|
+ if (this._dockActor.has_style_pseudo_class('overview')) |
|
+ return false; |
|
+ /* Get all the windows in the active workspace that are in the primary monitor and visible */ |
|
+ let activeWorkspace = global.screen.get_active_workspace(); |
|
+ let dash = this._dash; |
|
+ let windows = activeWorkspace.list_windows().filter(function(metaWindow) { |
|
+ return metaWindow.get_monitor() === dash._monitorIndex && |
|
+ metaWindow.showing_on_its_workspace() && |
|
+ metaWindow.get_window_type() != Meta.WindowType.DESKTOP; |
|
+ }); |
|
+ |
|
+ /* Check if at least one window is near enough to the panel. |
|
+ * If the dock is hidden, we need to account for the space it would take |
|
+ * up when it slides out. This is avoid an ugly transition. |
|
+ * */ |
|
+ let factor = 0; |
|
+ if (!this._settings.get_boolean('dock-fixed') && |
|
+ this._dock.getDockState() == Dock.State.HIDDEN) |
|
+ factor = 1; |
|
+ let [leftCoord, topCoord] = this._actor.get_transformed_position(); |
|
+ let threshold; |
|
+ if (this._position === St.Side.LEFT) |
|
+ threshold = leftCoord + this._actor.get_width() * (factor + 1); |
|
+ else if (this._position === St.Side.RIGHT) |
|
+ threshold = leftCoord - this._actor.get_width() * factor; |
|
+ else if (this._position === St.Side.TOP) |
|
+ threshold = topCoord + this._actor.get_height() * (factor + 1); |
|
+ else |
|
+ threshold = topCoord - this._actor.get_height() * factor; |
|
+ |
|
+ let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ let isNearEnough = windows.some(Lang.bind(this, function(metaWindow) { |
|
+ let coord; |
|
+ if (this._position === St.Side.LEFT) { |
|
+ coord = metaWindow.get_frame_rect().x; |
|
+ return coord < threshold + 5 * scale; |
|
+ } |
|
+ else if (this._position === St.Side.RIGHT) { |
|
+ coord = metaWindow.get_frame_rect().x + metaWindow.get_frame_rect().width; |
|
+ return coord > threshold - 5 * scale; |
|
+ } |
|
+ else if (this._position === St.Side.TOP) { |
|
+ coord = metaWindow.get_frame_rect().y; |
|
+ return coord < threshold + 5 * scale; |
|
+ } |
|
+ else { |
|
+ coord = metaWindow.get_frame_rect().y + metaWindow.get_frame_rect().height; |
|
+ return coord > threshold - 5 * scale; |
|
+ } |
|
+ })); |
|
+ |
|
+ return isNearEnough; |
|
+ }, |
|
+ |
|
+ _panelIsNear: function() { |
|
+ if (!this._panel._updateSolidStyle || |
|
+ this._settings.get_enum('transparency-mode') !== TransparencyMode.ADAPTIVE) |
|
+ return false; |
|
+ |
|
+ if (this._panel.actor.has_style_pseudo_class('overview') || !Main.sessionMode.hasWindows) { |
|
+ this._panel._removeStyleClassName('solid'); |
|
+ return false; |
|
+ } |
|
+ |
|
+ /* Get all the windows in the active workspace that are in the |
|
+ * primary monitor and visible */ |
|
+ let activeWorkspace = global.screen.get_active_workspace(); |
|
+ let windows = activeWorkspace.list_windows().filter(function(metaWindow) { |
|
+ return metaWindow.is_on_primary_monitor() && |
|
+ metaWindow.showing_on_its_workspace() && |
|
+ metaWindow.get_window_type() != Meta.WindowType.DESKTOP; |
|
+ }); |
|
+ |
|
+ /* Check if at least one window is near enough to the panel */ |
|
+ let [, panelTop] = this._panel.actor.get_transformed_position(); |
|
+ let panelBottom = panelTop + this._panel.actor.get_height(); |
|
+ let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor; |
|
+ let isNearEnough = windows.some(Lang.bind(this._panel, function(metaWindow) { |
|
+ let verticalPosition = metaWindow.get_frame_rect().y; |
|
+ return verticalPosition < panelBottom + 5 * scale; |
|
+ })); |
|
+ |
|
+ return isNearEnough; |
|
+ }, |
|
+ |
|
+ _updateStyles: function() { |
|
+ this._getAlphas(); |
|
+ |
|
+ this._transparent_style = |
|
+ 'background-color: rgba(' + |
|
+ this._backgroundColor + ', ' + this._transparentAlpha + ');' + |
|
+ 'border-color: rgba(' + |
|
+ this._backgroundColor + ', ' + this._transparentAlphaBorder + ');' + |
|
+ 'transition-duration: ' + this._transparentTransition + 'ms;'; |
|
+ |
|
+ this._opaque_style = |
|
+ 'background-color: rgba(' + |
|
+ this._backgroundColor + ', ' + this._opaqueAlpha + ');' + |
|
+ 'border-color: rgba(' + |
|
+ this._backgroundColor + ',' + this._opaqueAlphaBorder + ');' + |
|
+ 'transition-duration: ' + this._opaqueTransition + 'ms;'; |
|
+ |
|
+ this.emit('styles-updated'); |
|
+ }, |
|
+ |
|
+ setColor: function(color) { |
|
+ this._backgroundColor = color.red + ',' + color.green + ',' + color.blue; |
|
+ this._updateStyles(); |
|
+ }, |
|
+ |
|
+ _getAlphas: function() { |
|
+ // Create dummy object and add to the uiGroup to get it to the stage |
|
+ let dummyObject = new St.Bin({ |
|
+ name: 'dashtodockContainer', |
|
+ }); |
|
+ Main.uiGroup.add_child(dummyObject); |
|
+ |
|
+ dummyObject.add_style_class_name('opaque'); |
|
+ let themeNode = dummyObject.get_theme_node(); |
|
+ this._opaqueAlpha = themeNode.get_background_color().alpha / 255; |
|
+ this._opaqueAlphaBorder = themeNode.get_border_color(0).alpha / 255; |
|
+ this._opaqueTransition = themeNode.get_transition_duration(); |
|
+ |
|
+ dummyObject.add_style_class_name('transparent'); |
|
+ themeNode = dummyObject.get_theme_node(); |
|
+ this._transparentAlpha = themeNode.get_background_color().alpha / 255; |
|
+ this._transparentAlphaBorder = themeNode.get_border_color(0).alpha / 255; |
|
+ this._transparentTransition = themeNode.get_transition_duration(); |
|
+ |
|
+ Main.uiGroup.remove_child(dummyObject); |
|
+ |
|
+ if (this._settings.get_boolean('customize-alphas')) { |
|
+ this._opaqueAlpha = this._settings.get_double('max-alpha'); |
|
+ this._opaqueAlphaBorder = this._opaqueAlpha / 2; |
|
+ this._transparentAlpha = this._settings.get_double('min-alpha'); |
|
+ this._transparentAlphaBorder = this._transparentAlpha / 2; |
|
+ } |
|
+ |
|
+ if (this._settings.get_enum('transparency-mode') === TransparencyMode.ADAPTIVE && |
|
+ this._panel._updateSolidStyle) { |
|
+ themeNode = this._panel.actor.get_theme_node(); |
|
+ if (this._panel.actor.has_style_class_name('solid')) { |
|
+ this._opaqueTransition = themeNode.get_transition_duration(); |
|
+ this._panel._removeStyleClassName('solid'); |
|
+ themeNode = this._panel.actor.get_theme_node(); |
|
+ this._transparentTransition = themeNode.get_transition_duration(); |
|
+ this._panel._addStyleClassName('solid'); |
|
+ } |
|
+ else { |
|
+ this._transparentTransition = themeNode.get_transition_duration(); |
|
+ this._panel._addStyleClassName('solid'); |
|
+ themeNode = this._panel.actor.get_theme_node(); |
|
+ this._opaqueTransition = themeNode.get_transition_duration(); |
|
+ this._panel._removeStyleClassName('solid'); |
|
+ } |
|
+ } |
|
+ }, |
|
+ |
|
+ _enableAdaptive: function() { |
|
+ if (!this._panel._updateSolidStyle || |
|
+ this._dash._monitorIndex !== Main.layoutManager.primaryIndex) |
|
+ return; |
|
+ |
|
+ this._adaptiveEnabled = true; |
|
+ |
|
+ function UpdateSolidStyle() { |
|
+ return; |
|
+ } |
|
+ |
|
+ this._injectionsHandler.addWithLabel('adaptive', [ |
|
+ this._panel, |
|
+ '_updateSolidStyle', |
|
+ UpdateSolidStyle |
|
+ ]); |
|
+ |
|
+ // Once we injected the new function, we need to disconnect and |
|
+ // reconnect all window signals. |
|
+ for (let key of this._panel._trackedWindows.keys()) |
|
+ this._panel._trackedWindows.get(key).forEach(id => { |
|
+ key.disconnect(id); |
|
+ }); |
|
+ |
|
+ for (let win of this._panel._trackedWindows.keys()) |
|
+ this._panel._onWindowActorAdded(null, win); |
|
+ }, |
|
+ |
|
+ _disableAdaptive: function() { |
|
+ if (!this._adaptiveEnabled) |
|
+ return; |
|
+ |
|
+ this._injectionsHandler.removeWithLabel('adaptive'); |
|
+ this._adaptiveEnabled = false; |
|
+ |
|
+ // Once we removed the injection, we need to disconnect and |
|
+ // reconnect all window signals. |
|
+ for (let key of this._panel._trackedWindows.keys()) |
|
+ this._panel._trackedWindows.get(key).forEach(id => { |
|
+ key.disconnect(id); |
|
+ }); |
|
+ |
|
+ for (let win of this._panel._trackedWindows.keys()) |
|
+ this._panel._onWindowActorAdded(null, win); |
|
+ } |
|
+}); |
|
+Signals.addSignalMethods(Transparency.prototype); |
|
diff --git a/extensions/dash-to-dock/utils.js b/extensions/dash-to-dock/utils.js |
|
new file mode 100644 |
|
index 0000000..6514649 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/utils.js |
|
@@ -0,0 +1,255 @@ |
|
+const Clutter = imports.gi.Clutter; |
|
+const Lang = imports.lang; |
|
+const St = imports.gi.St; |
|
+ |
|
+/** |
|
+ * Simplify global signals and function injections handling |
|
+ * abstract class |
|
+ */ |
|
+const BasicHandler = new Lang.Class({ |
|
+ Name: 'DashToDock.BasicHandler', |
|
+ |
|
+ _init: function() { |
|
+ this._storage = new Object(); |
|
+ }, |
|
+ |
|
+ add: function(/* unlimited 3-long array arguments */) { |
|
+ // Convert arguments object to array, concatenate with generic |
|
+ let args = Array.concat('generic', Array.slice(arguments)); |
|
+ // Call addWithLabel with ags as if they were passed arguments |
|
+ this.addWithLabel.apply(this, args); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ for( let label in this._storage ) |
|
+ this.removeWithLabel(label); |
|
+ }, |
|
+ |
|
+ addWithLabel: function(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]; |
|
+ item.push(this._create(arguments[i])); |
|
+ } |
|
+ }, |
|
+ |
|
+ removeWithLabel: function(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]; |
|
+ } |
|
+ }, |
|
+ |
|
+ // Virtual methods to be implemented by subclass |
|
+ |
|
+ /** |
|
+ * Create single element to be stored in the storage structure |
|
+ */ |
|
+ _create: function(item) { |
|
+ throw new Error('no implementation of _create in ' + this); |
|
+ }, |
|
+ |
|
+ /** |
|
+ * Correctly delete single element |
|
+ */ |
|
+ _remove: function(item) { |
|
+ throw new Error('no implementation of _remove in ' + this); |
|
+ } |
|
+}); |
|
+ |
|
+/** |
|
+ * Manage global signals |
|
+ */ |
|
+var GlobalSignalsHandler = new Lang.Class({ |
|
+ Name: 'DashToDock.GlobalSignalHandler', |
|
+ Extends: BasicHandler, |
|
+ |
|
+ _create: function(item) { |
|
+ let object = item[0]; |
|
+ let event = item[1]; |
|
+ let callback = item[2] |
|
+ let id = object.connect(event, callback); |
|
+ |
|
+ return [object, id]; |
|
+ }, |
|
+ |
|
+ _remove: function(item) { |
|
+ item[0].disconnect(item[1]); |
|
+ } |
|
+}); |
|
+ |
|
+/** |
|
+ * Color manipulation utilities |
|
+ */ |
|
+var ColorUtils = { |
|
+ |
|
+ // Darken or brigthen color by a fraction dlum |
|
+ // Each rgb value is modified by the same fraction. |
|
+ // Return "#rrggbb" string |
|
+ ColorLuminance: function(r, g, b, dlum) { |
|
+ let rgbString = '#'; |
|
+ |
|
+ rgbString += Math.round(Math.min(Math.max(r*(1+dlum), 0), 255)).toString(16); |
|
+ rgbString += Math.round(Math.min(Math.max(g*(1+dlum), 0), 255)).toString(16); |
|
+ rgbString += Math.round(Math.min(Math.max(b*(1+dlum), 0), 255)).toString(16); |
|
+ |
|
+ return rgbString; |
|
+ }, |
|
+ |
|
+ // 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. |
|
+ HSVtoRGB: function(h, s, v) { |
|
+ 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) |
|
+ }; |
|
+ }, |
|
+ |
|
+ // 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. |
|
+ RGBtoHSV: function (r, g, b) { |
|
+ 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 |
|
+ }; |
|
+ } |
|
+}; |
|
+ |
|
+/** |
|
+ * Manage function injection: both instances and prototype can be overridden |
|
+ * and restored |
|
+ */ |
|
+var InjectionsHandler = new Lang.Class({ |
|
+ Name: 'DashToDock.InjectionsHandler', |
|
+ Extends: BasicHandler, |
|
+ |
|
+ _create: function(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: function(item) { |
|
+ let object = item[0]; |
|
+ let name = item[1]; |
|
+ let original = item[3]; |
|
+ object[name] = original; |
|
+ } |
|
+}); |
|
+ |
|
+/** |
|
+ * Return the actual position reverseing left and right in rtl |
|
+ */ |
|
+function getPosition(settings) { |
|
+ let position = settings.get_enum('dock-position'); |
|
+ if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) { |
|
+ if (position == St.Side.LEFT) |
|
+ position = St.Side.RIGHT; |
|
+ else if (position == St.Side.RIGHT) |
|
+ position = St.Side.LEFT; |
|
+ } |
|
+ return position; |
|
+} |
|
+ |
|
+function drawRoundedLine(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); |
|
+ |
|
+ var leftRadius = isRoundLeft ? height / 2.0 : 0.0; |
|
+ var 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/extensions/dash-to-dock/windowPreview.js b/extensions/dash-to-dock/windowPreview.js |
|
new file mode 100644 |
|
index 0000000..4b99aa8 |
|
--- /dev/null |
|
+++ b/extensions/dash-to-dock/windowPreview.js |
|
@@ -0,0 +1,630 @@ |
|
+/* |
|
+ * Credits: |
|
+ * This file is based on code from the Dash to Panel extension by Jason DeRose |
|
+ * and code from the Taskbar extension by Zorin OS |
|
+ * Some code was also adapted from the upstream Gnome Shell source code. |
|
+ */ |
|
+const Clutter = imports.gi.Clutter; |
|
+const GLib = imports.gi.GLib; |
|
+const Lang = imports.lang; |
|
+const St = imports.gi.St; |
|
+const Mainloop = imports.mainloop; |
|
+const Main = imports.ui.main; |
|
+const Gtk = imports.gi.Gtk; |
|
+ |
|
+const Params = imports.misc.params; |
|
+const PopupMenu = imports.ui.popupMenu; |
|
+const Tweener = imports.ui.tweener; |
|
+const Workspace = imports.ui.workspace; |
|
+ |
|
+const Me = imports.misc.extensionUtils.getCurrentExtension(); |
|
+const Utils = Me.imports.utils; |
|
+ |
|
+const PREVIEW_MAX_WIDTH = 250; |
|
+const PREVIEW_MAX_HEIGHT = 150; |
|
+ |
|
+const WindowPreviewMenu = new Lang.Class({ |
|
+ Name: 'WindowPreviewMenu', |
|
+ Extends: PopupMenu.PopupMenu, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ this._dtdSettings = settings; |
|
+ |
|
+ let side = Utils.getPosition(settings); |
|
+ |
|
+ this.parent(source.actor, 0.5, side); |
|
+ |
|
+ // We want to keep the item hovered while the menu is up |
|
+ this.blockSourceEvents = true; |
|
+ |
|
+ this._source = source; |
|
+ this._app = this._source.app; |
|
+ let monitorIndex = this._source.monitorIndex; |
|
+ |
|
+ this.actor.add_style_class_name('app-well-menu'); |
|
+ this.actor.set_style('max-width: ' + (Main.layoutManager.monitors[monitorIndex].width - 22) + 'px; ' + |
|
+ 'max-height: ' + (Main.layoutManager.monitors[monitorIndex].height - 22) + 'px;'); |
|
+ this.actor.hide(); |
|
+ |
|
+ // Chain our visibility and lifecycle to that of the source |
|
+ this._mappedId = this._source.actor.connect('notify::mapped', Lang.bind(this, function () { |
|
+ if (!this._source.actor.mapped) |
|
+ this.close(); |
|
+ })); |
|
+ this._destroyId = this._source.actor.connect('destroy', Lang.bind(this, this.destroy)); |
|
+ |
|
+ Main.uiGroup.add_actor(this.actor); |
|
+ |
|
+ // Change the initialized side where required. |
|
+ this._arrowSide = side; |
|
+ this._boxPointer._arrowSide = side; |
|
+ this._boxPointer._userArrowSide = side; |
|
+ |
|
+ this._previewBox = new WindowPreviewList(this._source, this._dtdSettings); |
|
+ this.addMenuItem(this._previewBox); |
|
+ }, |
|
+ |
|
+ _redisplay: function() { |
|
+ this._previewBox._shownInitially = false; |
|
+ this._previewBox._redisplay(); |
|
+ }, |
|
+ |
|
+ popup: function() { |
|
+ let windows = this._source.getInterestingWindows(); |
|
+ if (windows.length > 0) { |
|
+ this._redisplay(); |
|
+ this.open(); |
|
+ this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); |
|
+ this._source.emit('sync-tooltip'); |
|
+ } |
|
+ }, |
|
+ |
|
+ destroy: function () { |
|
+ if (this._mappedId) |
|
+ this._source.actor.disconnect(this._mappedId); |
|
+ |
|
+ if (this._destroyId) |
|
+ this._source.actor.disconnect(this._destroyId); |
|
+ |
|
+ this.parent(); |
|
+ } |
|
+ |
|
+}); |
|
+ |
|
+const WindowPreviewList = new Lang.Class({ |
|
+ Name: 'WindowPreviewMenuSection', |
|
+ Extends: PopupMenu.PopupMenuSection, |
|
+ |
|
+ _init: function(source, settings) { |
|
+ this._dtdSettings = settings; |
|
+ |
|
+ this.parent(); |
|
+ |
|
+ this.actor = new St.ScrollView({ name: 'dashtodockWindowScrollview', |
|
+ hscrollbar_policy: Gtk.PolicyType.NEVER, |
|
+ vscrollbar_policy: Gtk.PolicyType.NEVER, |
|
+ enable_mouse_scrolling: true }); |
|
+ |
|
+ this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent )); |
|
+ |
|
+ let position = Utils.getPosition(this._dtdSettings); |
|
+ this.isHorizontal = position == St.Side.BOTTOM || position == St.Side.TOP; |
|
+ this.box.set_vertical(!this.isHorizontal); |
|
+ this.box.set_name('dashtodockWindowList'); |
|
+ this.actor.add_actor(this.box); |
|
+ this.actor._delegate = this; |
|
+ |
|
+ this._shownInitially = false; |
|
+ |
|
+ this._source = source; |
|
+ this.app = source.app; |
|
+ |
|
+ this._redisplayId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay)); |
|
+ |
|
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); |
|
+ this._stateChangedId = this.app.connect('windows-changed', |
|
+ Lang.bind(this, |
|
+ this._queueRedisplay)); |
|
+ }, |
|
+ |
|
+ _queueRedisplay: function () { |
|
+ Main.queueDeferredWork(this._redisplayId); |
|
+ }, |
|
+ |
|
+ _onScrollEvent: function(actor, event) { |
|
+ // Event coordinates are relative to the stage but can be transformed |
|
+ // as the actor will only receive events within his bounds. |
|
+ let stage_x, stage_y, ok, event_x, event_y, actor_w, actor_h; |
|
+ [stage_x, stage_y] = event.get_coords(); |
|
+ [ok, event_x, event_y] = actor.transform_stage_point(stage_x, stage_y); |
|
+ [actor_w, actor_h] = actor.get_size(); |
|
+ |
|
+ // If the scroll event is within a 1px margin from |
|
+ // the relevant edge of the actor, let the event propagate. |
|
+ if (event_y >= actor_h - 2) |
|
+ return Clutter.EVENT_PROPAGATE; |
|
+ |
|
+ // Skip to avoid double events mouse |
|
+ if (event.is_pointer_emulated()) |
|
+ return Clutter.EVENT_STOP; |
|
+ |
|
+ let adjustment, delta; |
|
+ |
|
+ if (this.isHorizontal) |
|
+ adjustment = this.actor.get_hscroll_bar().get_adjustment(); |
|
+ else |
|
+ adjustment = this.actor.get_vscroll_bar().get_adjustment(); |
|
+ |
|
+ let increment = adjustment.step_increment; |
|
+ |
|
+ switch ( event.get_scroll_direction() ) { |
|
+ case Clutter.ScrollDirection.UP: |
|
+ delta = -increment; |
|
+ break; |
|
+ case Clutter.ScrollDirection.DOWN: |
|
+ 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; |
|
+ }, |
|
+ |
|
+ _onDestroy: function() { |
|
+ this.app.disconnect(this._stateChangedId); |
|
+ this._stateChangedId = 0; |
|
+ }, |
|
+ |
|
+ _createPreviewItem: function(window) { |
|
+ let preview = new WindowPreviewMenuItem(window); |
|
+ return preview; |
|
+ }, |
|
+ |
|
+ _redisplay: function () { |
|
+ // Remove separator |
|
+ let nonWinItem = this._getMenuItems().filter(function(actor) { |
|
+ return !actor._window; |
|
+ }); |
|
+ for (let i = 0; i < nonWinItem.length; i++) { |
|
+ let item = nonWinItem[i]; |
|
+ item.destroy(); |
|
+ } |
|
+ |
|
+ let children = this._getMenuItems().filter(function(actor) { |
|
+ return actor._window; |
|
+ }); |
|
+ |
|
+ // Windows currently on the menu |
|
+ let oldWin = children.map(function(actor) { |
|
+ return actor._window; |
|
+ }); |
|
+ |
|
+ // All app windows |
|
+ let newWin = this._source.getInterestingWindows().sort(this.sortWindowsCompareFunction); |
|
+ |
|
+ let addedItems = []; |
|
+ let removedActors = []; |
|
+ |
|
+ let newIndex = 0; |
|
+ let oldIndex = 0; |
|
+ |
|
+ while (newIndex < newWin.length || oldIndex < oldWin.length) { |
|
+ // No change at oldIndex/newIndex |
|
+ if (oldWin[oldIndex] && |
|
+ oldWin[oldIndex] == newWin[newIndex]) { |
|
+ oldIndex++; |
|
+ newIndex++; |
|
+ continue; |
|
+ } |
|
+ |
|
+ // Window removed at oldIndex |
|
+ if (oldWin[oldIndex] && |
|
+ newWin.indexOf(oldWin[oldIndex]) == -1) { |
|
+ removedActors.push(children[oldIndex]); |
|
+ oldIndex++; |
|
+ continue; |
|
+ } |
|
+ |
|
+ // Window added at newIndex |
|
+ if (newWin[newIndex] && |
|
+ oldWin.indexOf(newWin[newIndex]) == -1) { |
|
+ addedItems.push({ item: this._createPreviewItem(newWin[newIndex]), |
|
+ pos: newIndex }); |
|
+ newIndex++; |
|
+ continue; |
|
+ } |
|
+ |
|
+ // Window moved |
|
+ let insertHere = newWin[newIndex + 1] && |
|
+ newWin[newIndex + 1] == oldWin[oldIndex]; |
|
+ let alreadyRemoved = removedActors.reduce(function(result, actor) { |
|
+ let removedWin = actor._window; |
|
+ return result || removedWin == newWin[newIndex]; |
|
+ }, false); |
|
+ |
|
+ if (insertHere || alreadyRemoved) { |
|
+ addedItems.push({ item: this._createPreviewItem(newWin[newIndex]), |
|
+ pos: newIndex + removedActors.length }); |
|
+ newIndex++; |
|
+ } else { |
|
+ removedActors.push(children[oldIndex]); |
|
+ oldIndex++; |
|
+ } |
|
+ } |
|
+ |
|
+ for (let i = 0; i < addedItems.length; i++) |
|
+ this.addMenuItem(addedItems[i].item, |
|
+ addedItems[i].pos); |
|
+ |
|
+ for (let i = 0; i < removedActors.length; i++) { |
|
+ let item = removedActors[i]; |
|
+ if (this._shownInitially) |
|
+ item._animateOutAndDestroy(); |
|
+ else |
|
+ item.actor.destroy(); |
|
+ } |
|
+ |
|
+ // Separate windows from other workspaces |
|
+ let ws_index = global.screen.get_active_workspace_index(); |
|
+ let separator_index = 0; |
|
+ for (let i = 0; i < newWin.length; i++) |
|
+ if (newWin[i].get_workspace().index() == ws_index) |
|
+ separator_index++; |
|
+ |
|
+ if (separator_index > 0 && separator_index !== newWin.length) { |
|
+ let separatorItem = new PopupMenu.PopupSeparatorMenuItem(); |
|
+ if (this.isHorizontal) { |
|
+ separatorItem._separator.set_x_expand(false); |
|
+ separatorItem._separator.set_y_expand(true); |
|
+ separatorItem._separator.set_name('dashtodockPreviewSeparator'); |
|
+ separatorItem._separator.add_style_class_name('popup-separator-menu-item-horizontal'); |
|
+ separatorItem._separator.set_x_align(Clutter.ActorAlign.CENTER); |
|
+ separatorItem._separator.set_y_align(Clutter.ActorAlign.FILL); |
|
+ } |
|
+ this.addMenuItem(separatorItem, separator_index); |
|
+ } |
|
+ |
|
+ // Skip animations on first run when adding the initial set |
|
+ // of items, to avoid all items zooming in at once |
|
+ let animate = this._shownInitially; |
|
+ |
|
+ if (!this._shownInitially) |
|
+ this._shownInitially = true; |
|
+ |
|
+ for (let i = 0; i < addedItems.length; i++) |
|
+ addedItems[i].item.show(animate); |
|
+ |
|
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 |
|
+ // Without it, StBoxLayout may use a stale size cache |
|
+ this.box.queue_relayout(); |
|
+ |
|
+ if (newWin.length < 1) |
|
+ this._getTopMenu().close(~0); |
|
+ |
|
+ // As for upstream: |
|
+ // St.ScrollView always requests space horizontally for a possible vertical |
|
+ // scrollbar if in AUTOMATIC mode. Doing better would require implementation |
|
+ // of width-for-height in St.BoxLayout and St.ScrollView. This looks bad |
|
+ // when we *don't* need it, so turn off the scrollbar when that's true. |
|
+ // Dynamic changes in whether we need it aren't handled properly. |
|
+ let needsScrollbar = this._needsScrollbar(); |
|
+ let scrollbar_policy = needsScrollbar ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER; |
|
+ if (this.isHorizontal) |
|
+ this.actor.hscrollbar_policy = scrollbar_policy; |
|
+ else |
|
+ this.actor.vscrollbar_policy = scrollbar_policy; |
|
+ |
|
+ if (needsScrollbar) |
|
+ this.actor.add_style_pseudo_class('scrolled'); |
|
+ else |
|
+ this.actor.remove_style_pseudo_class('scrolled'); |
|
+ }, |
|
+ |
|
+ _needsScrollbar: function() { |
|
+ let topMenu = this._getTopMenu(); |
|
+ let topThemeNode = topMenu.actor.get_theme_node(); |
|
+ if (this.isHorizontal) { |
|
+ let [topMinWidth, topNaturalWidth] = topMenu.actor.get_preferred_width(-1); |
|
+ let topMaxWidth = topThemeNode.get_max_width(); |
|
+ return topMaxWidth >= 0 && topNaturalWidth >= topMaxWidth; |
|
+ } else { |
|
+ let [topMinHeight, topNaturalHeight] = topMenu.actor.get_preferred_height(-1); |
|
+ let topMaxHeight = topThemeNode.get_max_height(); |
|
+ return topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight; |
|
+ } |
|
+ |
|
+ }, |
|
+ |
|
+ isAnimatingOut: function() { |
|
+ return this.actor.get_children().reduce(function(result, actor) { |
|
+ return result || actor.animatingOut; |
|
+ }, false); |
|
+ }, |
|
+ |
|
+ sortWindowsCompareFunction: function(windowA, windowB) { |
|
+ let ws_index = global.screen.get_active_workspace_index(); |
|
+ let winA_inActiveWS = windowA.get_workspace().index() == ws_index; |
|
+ let winB_inActiveWS = windowB.get_workspace().index() == ws_index; |
|
+ |
|
+ // Only change the order if winA is not in the current WS, while winB is |
|
+ if (!winA_inActiveWS && winB_inActiveWS) |
|
+ return 1; |
|
+ |
|
+ return 0; |
|
+ } |
|
+}); |
|
+ |
|
+const WindowPreviewMenuItem = new Lang.Class({ |
|
+ Name: 'WindowPreviewMenuItem', |
|
+ Extends: PopupMenu.PopupBaseMenuItem, |
|
+ |
|
+ _init: function(window, params) { |
|
+ this._window = window; |
|
+ this._destroyId = 0; |
|
+ this._windowAddedId = 0; |
|
+ this.parent(params); |
|
+ |
|
+ // We don't want this: it adds spacing on the left of the item. |
|
+ this.actor.remove_child(this._ornamentLabel); |
|
+ this.actor.add_style_class_name('dashtodock-app-well-preview-menu-item'); |
|
+ |
|
+ this._cloneBin = new St.Bin(); |
|
+ this._cloneBin.set_size(PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT); |
|
+ |
|
+ // TODO: improve the way the closebutton is layout. Just use some padding |
|
+ // for the moment. |
|
+ this._cloneBin.set_style('padding-bottom: 0.5em'); |
|
+ |
|
+ this.closeButton = new St.Button({ style_class: 'window-close', |
|
+ x_expand: true, |
|
+ y_expand: true}); |
|
+ this.closeButton.set_x_align(Clutter.ActorAlign.END); |
|
+ this.closeButton.set_y_align(Clutter.ActorAlign.START); |
|
+ |
|
+ |
|
+ this.closeButton.opacity = 0; |
|
+ this.closeButton.connect('clicked', Lang.bind(this, this._closeWindow)); |
|
+ |
|
+ let overlayGroup = new Clutter.Actor({layout_manager: new Clutter.BinLayout() }); |
|
+ |
|
+ overlayGroup.add_actor(this._cloneBin); |
|
+ overlayGroup.add_actor(this.closeButton); |
|
+ |
|
+ let label = new St.Label({ text: window.get_title()}); |
|
+ label.set_style('max-width: '+PREVIEW_MAX_WIDTH +'px'); |
|
+ let labelBin = new St.Bin({ child: label, |
|
+ x_align: St.Align.MIDDLE}); |
|
+ |
|
+ this._windowTitleId = this._window.connect('notify::title', Lang.bind(this, function() { |
|
+ label.set_text(this._window.get_title()); |
|
+ })); |
|
+ |
|
+ let box = new St.BoxLayout({ vertical: true, |
|
+ reactive:true, |
|
+ x_expand:true }); |
|
+ box.add(overlayGroup); |
|
+ box.add(labelBin); |
|
+ this.actor.add_actor(box); |
|
+ |
|
+ this.actor.connect('enter-event', |
|
+ Lang.bind(this, this._onEnter)); |
|
+ this.actor.connect('leave-event', |
|
+ Lang.bind(this, this._onLeave)); |
|
+ this.actor.connect('key-focus-in', |
|
+ Lang.bind(this, this._onEnter)); |
|
+ this.actor.connect('key-focus-out', |
|
+ Lang.bind(this, this._onLeave)); |
|
+ |
|
+ this._cloneTexture(window); |
|
+ |
|
+ }, |
|
+ |
|
+ _cloneTexture: function(metaWin){ |
|
+ |
|
+ let mutterWindow = metaWin.get_compositor_private(); |
|
+ |
|
+ // Newly-created windows are added to a workspace before |
|
+ // the compositor finds out about them... |
|
+ // Moreover sometimes they return an empty texture, thus as a workarounf also check for it size |
|
+ if (!mutterWindow || !mutterWindow.get_texture() || !mutterWindow.get_texture().get_size()[0]) { |
|
+ let id = Mainloop.idle_add(Lang.bind(this, |
|
+ function () { |
|
+ // Check if there's still a point in getting the texture, |
|
+ // otherwise this could go on indefinitely |
|
+ if (this.actor && metaWin.get_workspace()) |
|
+ this._cloneTexture(metaWin); |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ GLib.Source.set_name_by_id(id, '[dash-to-dock] this._cloneTexture'); |
|
+ return; |
|
+ } |
|
+ |
|
+ let windowTexture = mutterWindow.get_texture(); |
|
+ let [width, height] = windowTexture.get_size(); |
|
+ |
|
+ let scale = Math.min(1.0, PREVIEW_MAX_WIDTH/width, PREVIEW_MAX_HEIGHT/height); |
|
+ |
|
+ let clone = new Clutter.Clone ({ source: windowTexture, |
|
+ reactive: true, |
|
+ width: width * scale, |
|
+ height: height * scale }); |
|
+ |
|
+ // when the source actor is destroyed, i.e. the window closed, first destroy the clone |
|
+ // and then destroy the menu item (do this animating out) |
|
+ this._destroyId = mutterWindow.connect('destroy', Lang.bind(this, function() { |
|
+ clone.destroy(); |
|
+ this._destroyId = 0; // avoid to try to disconnect this signal from mutterWindow in _onDestroy(), |
|
+ // as the object was just destroyed |
|
+ this._animateOutAndDestroy(); |
|
+ })); |
|
+ |
|
+ this._clone = clone; |
|
+ this._mutterWindow = mutterWindow; |
|
+ this._cloneBin.set_child(this._clone); |
|
+ }, |
|
+ |
|
+ _windowCanClose: function() { |
|
+ return this._window.can_close() && |
|
+ !this._hasAttachedDialogs(); |
|
+ }, |
|
+ |
|
+ _closeWindow: function(actor) { |
|
+ this._workspace = this._window.get_workspace(); |
|
+ |
|
+ // This mechanism is copied from the workspace.js upstream code |
|
+ // It forces window activation if the windows don't get closed, |
|
+ // for instance because asking user confirmation, by monitoring the opening of |
|
+ // such additional confirmation window |
|
+ this._windowAddedId = this._workspace.connect('window-added', |
|
+ Lang.bind(this, |
|
+ this._onWindowAdded)); |
|
+ |
|
+ this.deleteAllWindows(); |
|
+ }, |
|
+ |
|
+ deleteAllWindows: function() { |
|
+ // Delete all windows, starting from the bottom-most (most-modal) one |
|
+ //let windows = this._window.get_compositor_private().get_children(); |
|
+ let windows = this._clone.get_children(); |
|
+ for (let i = windows.length - 1; i >= 1; i--) { |
|
+ let realWindow = windows[i].source; |
|
+ let metaWindow = realWindow.meta_window; |
|
+ |
|
+ metaWindow.delete(global.get_current_time()); |
|
+ } |
|
+ |
|
+ this._window.delete(global.get_current_time()); |
|
+ }, |
|
+ |
|
+ _onWindowAdded: function(workspace, win) { |
|
+ let metaWindow = this._window; |
|
+ |
|
+ if (win.get_transient_for() == metaWindow) { |
|
+ workspace.disconnect(this._windowAddedId); |
|
+ this._windowAddedId = 0; |
|
+ |
|
+ // use an idle handler to avoid mapping problems - |
|
+ // see comment in Workspace._windowAdded |
|
+ let id = Mainloop.idle_add(Lang.bind(this, |
|
+ function() { |
|
+ this.emit('activate'); |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ GLib.Source.set_name_by_id(id, '[dash-to-dock] this.emit'); |
|
+ } |
|
+ }, |
|
+ |
|
+ _hasAttachedDialogs: function() { |
|
+ // count trasient windows |
|
+ let n=0; |
|
+ this._window.foreach_transient(function(){n++;}); |
|
+ return n>0; |
|
+ }, |
|
+ |
|
+ _onEnter: function() { |
|
+ this._showCloseButton(); |
|
+ return Clutter.EVENT_PROPAGATE; |
|
+ }, |
|
+ |
|
+ _onLeave: function() { |
|
+ if (!this._cloneBin.has_pointer && |
|
+ !this.closeButton.has_pointer) |
|
+ this._hideCloseButton(); |
|
+ |
|
+ return Clutter.EVENT_PROPAGATE; |
|
+ }, |
|
+ |
|
+ _idleToggleCloseButton: function() { |
|
+ this._idleToggleCloseId = 0; |
|
+ |
|
+ if (!this._cloneBin.has_pointer && |
|
+ !this.closeButton.has_pointer) |
|
+ this._hideCloseButton(); |
|
+ |
|
+ return GLib.SOURCE_REMOVE; |
|
+ }, |
|
+ |
|
+ _showCloseButton: function() { |
|
+ |
|
+ if (this._windowCanClose()) { |
|
+ this.closeButton.show(); |
|
+ Tweener.addTween(this.closeButton, |
|
+ { opacity: 255, |
|
+ time: Workspace.CLOSE_BUTTON_FADE_TIME, |
|
+ transition: 'easeOutQuad' }); |
|
+ } |
|
+ }, |
|
+ |
|
+ _hideCloseButton: function() { |
|
+ Tweener.addTween(this.closeButton, |
|
+ { opacity: 0, |
|
+ time: Workspace.CLOSE_BUTTON_FADE_TIME, |
|
+ transition: 'easeInQuad' }); |
|
+ }, |
|
+ |
|
+ show: function(animate) { |
|
+ let fullWidth = this.actor.get_width(); |
|
+ |
|
+ this.actor.opacity = 0; |
|
+ this.actor.set_width(0); |
|
+ |
|
+ let time = animate ? 0.25 : 0; |
|
+ Tweener.addTween(this.actor, |
|
+ { opacity: 255, |
|
+ width: fullWidth, |
|
+ time: time, |
|
+ transition: 'easeInOutQuad' |
|
+ }); |
|
+ }, |
|
+ |
|
+ _animateOutAndDestroy: function() { |
|
+ Tweener.addTween(this.actor, |
|
+ { opacity: 0, |
|
+ time: 0.25, |
|
+ }); |
|
+ |
|
+ Tweener.addTween(this.actor, |
|
+ { height: 0, |
|
+ width: 0, |
|
+ time: 0.25, |
|
+ delay: 0.25, |
|
+ onCompleteScope: this, |
|
+ onComplete: function() { |
|
+ this.actor.destroy(); |
|
+ } |
|
+ }); |
|
+ }, |
|
+ |
|
+ activate: function() { |
|
+ this._getTopMenu().close(); |
|
+ Main.activateWindow(this._window); |
|
+ }, |
|
+ |
|
+ _onDestroy: function() { |
|
+ |
|
+ this.parent(); |
|
+ |
|
+ if (this._windowAddedId > 0) { |
|
+ this._workspace.disconnect(this._windowAddedId); |
|
+ this._windowAddedId = 0; |
|
+ } |
|
+ |
|
+ if (this._destroyId > 0) { |
|
+ this._mutterWindow.disconnect(this._destroyId); |
|
+ this._destroyId = 0; |
|
+ } |
|
+ |
|
+ if (this._windowTitleId > 0) { |
|
+ this._window.disconnect(this._windowTitleId); |
|
+ this._windowTitleId = 0; |
|
+ } |
|
+ } |
|
+ |
|
+}); |
|
diff --git a/meson.build b/meson.build |
|
index c16bde1..f9b56cf 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -52,6 +52,7 @@ default_extensions += [ |
|
all_extensions = default_extensions |
|
all_extensions += [ |
|
'auto-move-windows', |
|
+ 'dash-to-dock', |
|
'example', |
|
'native-window-placement', |
|
'top-icons', |
|
-- |
|
2.17.1 |
|
|
|
|
|
From 671a9cb9728d7de3bd617fec40045b0397bf0cab Mon Sep 17 00:00:00 2001 |
|
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org> |
|
Date: Wed, 20 May 2015 18:55:47 +0200 |
|
Subject: [PATCH 3/5] Add panel-favorites extension |
|
|
|
--- |
|
extensions/panel-favorites/extension.js | 267 ++++++++++++++++++++ |
|
extensions/panel-favorites/meson.build | 5 + |
|
extensions/panel-favorites/metadata.json.in | 10 + |
|
extensions/panel-favorites/stylesheet.css | 14 + |
|
meson.build | 1 + |
|
5 files changed, 297 insertions(+) |
|
create mode 100644 extensions/panel-favorites/extension.js |
|
create mode 100644 extensions/panel-favorites/meson.build |
|
create mode 100644 extensions/panel-favorites/metadata.json.in |
|
create mode 100644 extensions/panel-favorites/stylesheet.css |
|
|
|
diff --git a/extensions/panel-favorites/extension.js b/extensions/panel-favorites/extension.js |
|
new file mode 100644 |
|
index 0000000..b817dbb |
|
--- /dev/null |
|
+++ b/extensions/panel-favorites/extension.js |
|
@@ -0,0 +1,267 @@ |
|
+// Copyright (C) 2011-2013 R M Yorston |
|
+// Licence: GPLv2+ |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const Gio = imports.gi.Gio; |
|
+const GLib = imports.gi.GLib; |
|
+const Lang = imports.lang; |
|
+const Shell = imports.gi.Shell; |
|
+const Signals = imports.signals; |
|
+const St = imports.gi.St; |
|
+const Mainloop = imports.mainloop; |
|
+ |
|
+const AppFavorites = imports.ui.appFavorites; |
|
+const Main = imports.ui.main; |
|
+const Panel = imports.ui.panel; |
|
+const Tweener = imports.ui.tweener; |
|
+ |
|
+const PANEL_LAUNCHER_LABEL_SHOW_TIME = 0.15; |
|
+const PANEL_LAUNCHER_LABEL_HIDE_TIME = 0.1; |
|
+const PANEL_LAUNCHER_HOVER_TIMEOUT = 300; |
|
+ |
|
+const PanelLauncher = new Lang.Class({ |
|
+ Name: 'PanelLauncher', |
|
+ |
|
+ _init: function(app) { |
|
+ this.actor = new St.Button({ style_class: 'panel-button', |
|
+ reactive: true }); |
|
+ this.iconSize = 24; |
|
+ let icon = app.create_icon_texture(this.iconSize); |
|
+ this.actor.set_child(icon); |
|
+ this.actor._delegate = this; |
|
+ let text = app.get_name(); |
|
+ if ( app.get_description() ) { |
|
+ text += '\n' + app.get_description(); |
|
+ } |
|
+ |
|
+ this.label = new St.Label({ style_class: 'panel-launcher-label'}); |
|
+ this.label.set_text(text); |
|
+ Main.layoutManager.addChrome(this.label); |
|
+ this.label.hide(); |
|
+ this.actor.label_actor = this.label; |
|
+ |
|
+ this._app = app; |
|
+ this.actor.connect('clicked', Lang.bind(this, function() { |
|
+ this._app.open_new_window(-1); |
|
+ })); |
|
+ this.actor.connect('notify::hover', |
|
+ Lang.bind(this, this._onHoverChanged)); |
|
+ this.actor.opacity = 207; |
|
+ |
|
+ this.actor.connect('notify::allocation', Lang.bind(this, this._alloc)); |
|
+ }, |
|
+ |
|
+ _onHoverChanged: function(actor) { |
|
+ actor.opacity = actor.hover ? 255 : 207; |
|
+ }, |
|
+ |
|
+ _alloc: function() { |
|
+ let size = this.actor.allocation.y2 - this.actor.allocation.y1 - 3; |
|
+ if ( size >= 24 && size != this.iconSize ) { |
|
+ this.actor.get_child().destroy(); |
|
+ this.iconSize = size; |
|
+ let icon = this._app.create_icon_texture(this.iconSize); |
|
+ this.actor.set_child(icon); |
|
+ } |
|
+ }, |
|
+ |
|
+ showLabel: function() { |
|
+ this.label.opacity = 0; |
|
+ this.label.show(); |
|
+ |
|
+ let [stageX, stageY] = this.actor.get_transformed_position(); |
|
+ |
|
+ let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1; |
|
+ let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1; |
|
+ let labelWidth = this.label.get_width(); |
|
+ |
|
+ let node = this.label.get_theme_node(); |
|
+ let yOffset = node.get_length('-y-offset'); |
|
+ |
|
+ let y = stageY + itemHeight + yOffset; |
|
+ let x = Math.floor(stageX + itemWidth/2 - labelWidth/2); |
|
+ |
|
+ let parent = this.label.get_parent(); |
|
+ let parentWidth = parent.allocation.x2 - parent.allocation.x1; |
|
+ |
|
+ if ( Clutter.get_default_text_direction() == Clutter.TextDirection.LTR ) { |
|
+ // stop long tooltips falling off the right of the screen |
|
+ x = Math.min(x, parentWidth-labelWidth-6); |
|
+ // but whatever happens don't let them fall of the left |
|
+ x = Math.max(x, 6); |
|
+ } |
|
+ else { |
|
+ x = Math.max(x, 6); |
|
+ x = Math.min(x, parentWidth-labelWidth-6); |
|
+ } |
|
+ |
|
+ this.label.set_position(x, y); |
|
+ Tweener.addTween(this.label, |
|
+ { opacity: 255, |
|
+ time: PANEL_LAUNCHER_LABEL_SHOW_TIME, |
|
+ transition: 'easeOutQuad', |
|
+ }); |
|
+ }, |
|
+ |
|
+ hideLabel: function() { |
|
+ this.label.opacity = 255; |
|
+ Tweener.addTween(this.label, |
|
+ { opacity: 0, |
|
+ time: PANEL_LAUNCHER_LABEL_HIDE_TIME, |
|
+ transition: 'easeOutQuad', |
|
+ onComplete: Lang.bind(this, function() { |
|
+ this.label.hide(); |
|
+ }) |
|
+ }); |
|
+ }, |
|
+ |
|
+ destroy: function() { |
|
+ this.label.destroy(); |
|
+ this.actor.destroy(); |
|
+ } |
|
+}); |
|
+ |
|
+const PanelFavorites = new Lang.Class({ |
|
+ Name: 'PanelFavorites', |
|
+ |
|
+ _init: function() { |
|
+ this._showLabelTimeoutId = 0; |
|
+ this._resetHoverTimeoutId = 0; |
|
+ this._labelShowing = false; |
|
+ |
|
+ this.actor = new St.BoxLayout({ name: 'panelFavorites', |
|
+ x_expand: true, y_expand: true, |
|
+ style_class: 'panel-favorites' }); |
|
+ this._display(); |
|
+ |
|
+ this.container = new St.Bin({ y_fill: true, |
|
+ x_fill: true, |
|
+ child: this.actor }); |
|
+ |
|
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); |
|
+ this._installChangedId = Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, this._redisplay)); |
|
+ this._changedId = AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._redisplay)); |
|
+ }, |
|
+ |
|
+ _redisplay: function() { |
|
+ for ( let i=0; i<this._buttons.length; ++i ) { |
|
+ this._buttons[i].destroy(); |
|
+ } |
|
+ |
|
+ this._display(); |
|
+ }, |
|
+ |
|
+ _display: function() { |
|
+ let launchers = global.settings.get_strv(AppFavorites.getAppFavorites().FAVORITE_APPS_KEY); |
|
+ |
|
+ this._buttons = []; |
|
+ let j = 0; |
|
+ for ( let i=0; i<launchers.length; ++i ) { |
|
+ let app = Shell.AppSystem.get_default().lookup_app(launchers[i]); |
|
+ |
|
+ if ( app == null ) { |
|
+ continue; |
|
+ } |
|
+ |
|
+ let launcher = new PanelLauncher(app); |
|
+ this.actor.add(launcher.actor); |
|
+ launcher.actor.connect('notify::hover', |
|
+ Lang.bind(this, function() { |
|
+ this._onHover(launcher); |
|
+ })); |
|
+ this._buttons[j] = launcher; |
|
+ ++j; |
|
+ } |
|
+ }, |
|
+ |
|
+ // this routine stolen from dash.js |
|
+ _onHover: function(launcher) { |
|
+ if ( launcher.actor.hover ) { |
|
+ if (this._showLabelTimeoutId == 0) { |
|
+ let timeout = this._labelShowing ? |
|
+ 0 : PANEL_LAUNCHER_HOVER_TIMEOUT; |
|
+ this._showLabelTimeoutId = Mainloop.timeout_add(timeout, |
|
+ Lang.bind(this, function() { |
|
+ this._labelShowing = true; |
|
+ launcher.showLabel(); |
|
+ this._showLabelTimeoutId = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ if (this._resetHoverTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._resetHoverTimeoutId); |
|
+ this._resetHoverTimeoutId = 0; |
|
+ } |
|
+ } |
|
+ } else { |
|
+ if (this._showLabelTimeoutId > 0) { |
|
+ Mainloop.source_remove(this._showLabelTimeoutId); |
|
+ this._showLabelTimeoutId = 0; |
|
+ } |
|
+ launcher.hideLabel(); |
|
+ if (this._labelShowing) { |
|
+ this._resetHoverTimeoutId = Mainloop.timeout_add( |
|
+ PANEL_LAUNCHER_HOVER_TIMEOUT, |
|
+ Lang.bind(this, function() { |
|
+ this._labelShowing = false; |
|
+ this._resetHoverTimeoutId = 0; |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ } |
|
+ } |
|
+ }, |
|
+ |
|
+ _onDestroy: function() { |
|
+ if ( this._installChangedId != 0 ) { |
|
+ Shell.AppSystem.get_default().disconnect(this._installChangedId); |
|
+ this._installChangedId = 0; |
|
+ } |
|
+ |
|
+ if ( this._changedId != 0 ) { |
|
+ AppFavorites.getAppFavorites().disconnect(this._changedId); |
|
+ this._changedId = 0; |
|
+ } |
|
+ } |
|
+}); |
|
+Signals.addSignalMethods(PanelFavorites.prototype); |
|
+ |
|
+let myAddToStatusArea; |
|
+let panelFavorites; |
|
+ |
|
+function enable() { |
|
+ Panel.Panel.prototype.myAddToStatusArea = myAddToStatusArea; |
|
+ |
|
+ // place panel to left of app menu, or failing that at right end of box |
|
+ let siblings = Main.panel._leftBox.get_children(); |
|
+ let appMenu = Main.panel.statusArea['appMenu']; |
|
+ let pos = appMenu ? siblings.indexOf(appMenu.container) : siblings.length; |
|
+ |
|
+ panelFavorites = new PanelFavorites(); |
|
+ Main.panel.myAddToStatusArea('panel-favorites', panelFavorites, |
|
+ pos, 'left'); |
|
+} |
|
+ |
|
+function disable() { |
|
+ delete Panel.Panel.prototype.myAddToStatusArea; |
|
+ |
|
+ panelFavorites.actor.destroy(); |
|
+ panelFavorites.emit('destroy'); |
|
+ panelFavorites = null; |
|
+} |
|
+ |
|
+function init() { |
|
+ myAddToStatusArea = function(role, indicator, position, box) { |
|
+ if (this.statusArea[role]) |
|
+ throw new Error('Extension point conflict: there is already a status indicator for role ' + role); |
|
+ |
|
+ position = position || 0; |
|
+ let boxes = { |
|
+ left: this._leftBox, |
|
+ center: this._centerBox, |
|
+ right: this._rightBox |
|
+ }; |
|
+ let boxContainer = boxes[box] || this._rightBox; |
|
+ this.statusArea[role] = indicator; |
|
+ this._addToPanelBox(role, indicator, position, boxContainer); |
|
+ return indicator; |
|
+ }; |
|
+} |
|
diff --git a/extensions/panel-favorites/meson.build b/extensions/panel-favorites/meson.build |
|
new file mode 100644 |
|
index 0000000..48504f6 |
|
--- /dev/null |
|
+++ b/extensions/panel-favorites/meson.build |
|
@@ -0,0 +1,5 @@ |
|
+extension_data += configure_file( |
|
+ input: metadata_name + '.in', |
|
+ output: metadata_name, |
|
+ configuration: metadata_conf |
|
+) |
|
diff --git a/extensions/panel-favorites/metadata.json.in b/extensions/panel-favorites/metadata.json.in |
|
new file mode 100644 |
|
index 0000000..037f281 |
|
--- /dev/null |
|
+++ b/extensions/panel-favorites/metadata.json.in |
|
@@ -0,0 +1,10 @@ |
|
+{ |
|
+"extension-id": "@extension_id@", |
|
+"uuid": "@uuid@", |
|
+"settings-schema": "@gschemaname@", |
|
+"gettext-domain": "@gettext_domain@", |
|
+"name": "Frippery Panel Favorites", |
|
+"description": "Add launchers for Favorites to the panel", |
|
+"shell-version": [ "@shell_current@" ], |
|
+"url": "http://intgat.tigress.co.uk/rmy/extensions/index.html" |
|
+} |
|
diff --git a/extensions/panel-favorites/stylesheet.css b/extensions/panel-favorites/stylesheet.css |
|
new file mode 100644 |
|
index 0000000..120adac |
|
--- /dev/null |
|
+++ b/extensions/panel-favorites/stylesheet.css |
|
@@ -0,0 +1,14 @@ |
|
+.panel-favorites { |
|
+ spacing: 6px; |
|
+} |
|
+ |
|
+.panel-launcher-label { |
|
+ border-radius: 7px; |
|
+ padding: 4px 12px; |
|
+ background-color: rgba(0,0,0,0.9); |
|
+ color: white; |
|
+ text-align: center; |
|
+ font-size: 9pt; |
|
+ font-weight: bold; |
|
+ -y-offset: 6px; |
|
+} |
|
diff --git a/meson.build b/meson.build |
|
index f9b56cf..3451585 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -55,6 +55,7 @@ all_extensions += [ |
|
'dash-to-dock', |
|
'example', |
|
'native-window-placement', |
|
+ 'panel-favorites', |
|
'top-icons', |
|
'user-theme' |
|
] |
|
-- |
|
2.17.1 |
|
|
|
|
|
From d518dca768e895a00e7eae89b72cab8474826314 Mon Sep 17 00:00:00 2001 |
|
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org> |
|
Date: Fri, 4 Mar 2016 17:07:21 +0100 |
|
Subject: [PATCH 4/5] Add updates-dialog extension |
|
|
|
--- |
|
extensions/updates-dialog/extension.js | 490 ++++++++++++++++++ |
|
extensions/updates-dialog/meson.build | 7 + |
|
extensions/updates-dialog/metadata.json.in | 10 + |
|
...hell.extensions.updates-dialog.gschema.xml | 30 ++ |
|
extensions/updates-dialog/stylesheet.css | 1 + |
|
meson.build | 1 + |
|
po/POTFILES.in | 2 + |
|
7 files changed, 541 insertions(+) |
|
create mode 100644 extensions/updates-dialog/extension.js |
|
create mode 100644 extensions/updates-dialog/meson.build |
|
create mode 100644 extensions/updates-dialog/metadata.json.in |
|
create mode 100644 extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml |
|
create mode 100644 extensions/updates-dialog/stylesheet.css |
|
|
|
diff --git a/extensions/updates-dialog/extension.js b/extensions/updates-dialog/extension.js |
|
new file mode 100644 |
|
index 0000000..2fa62a5 |
|
--- /dev/null |
|
+++ b/extensions/updates-dialog/extension.js |
|
@@ -0,0 +1,490 @@ |
|
+/* |
|
+ * Copyright (c) 2015 Red Hat, Inc. |
|
+ * |
|
+ * 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 <http://www.gnu.org/licenses/>. |
|
+ */ |
|
+ |
|
+const Clutter = imports.gi.Clutter; |
|
+const Gio = imports.gi.Gio; |
|
+const GLib = imports.gi.GLib; |
|
+const Lang = imports.lang; |
|
+const Pango = imports.gi.Pango; |
|
+const PkgKit = imports.gi.PackageKitGlib; |
|
+const Polkit = imports.gi.Polkit; |
|
+const Signals = imports.signals; |
|
+const St = imports.gi.St; |
|
+ |
|
+const EndSessionDialog = imports.ui.endSessionDialog; |
|
+const Main = imports.ui.main; |
|
+const ModalDialog = imports.ui.modalDialog; |
|
+ |
|
+const ExtensionUtils = imports.misc.extensionUtils; |
|
+const Me = ExtensionUtils.getCurrentExtension(); |
|
+const Convenience = Me.imports.convenience; |
|
+ |
|
+const PkIface = '<node> \ |
|
+<interface name="org.freedesktop.PackageKit"> \ |
|
+ <method name="CreateTransaction"> \ |
|
+ <arg type="o" name="object_path" direction="out"/> \ |
|
+ </method> \ |
|
+ <signal name="UpdatesChanged"/> \ |
|
+</interface> \ |
|
+</node>'; |
|
+ |
|
+const PkOfflineIface = '<node> \ |
|
+<interface name="org.freedesktop.PackageKit.Offline"> \ |
|
+ <property name="UpdatePrepared" type="b" access="read"/> \ |
|
+ <property name="TriggerAction" type="s" access="read"/> \ |
|
+ <method name="Trigger"> \ |
|
+ <arg type="s" name="action" direction="in"/> \ |
|
+ </method> \ |
|
+ <method name="Cancel"/> \ |
|
+</interface> \ |
|
+</node>'; |
|
+ |
|
+const PkTransactionIface = '<node> \ |
|
+<interface name="org.freedesktop.PackageKit.Transaction"> \ |
|
+ <method name="SetHints"> \ |
|
+ <arg type="as" name="hints" direction="in"/> \ |
|
+ </method> \ |
|
+ <method name="GetUpdates"> \ |
|
+ <arg type="t" name="filter" direction="in"/> \ |
|
+ </method> \ |
|
+ <method name="UpdatePackages"> \ |
|
+ <arg type="t" name="transaction_flags" direction="in"/> \ |
|
+ <arg type="as" name="package_ids" direction="in"/> \ |
|
+ </method> \ |
|
+ <signal name="Package"> \ |
|
+ <arg type="u" name="info" direction="out"/> \ |
|
+ <arg type="s" name="package_id" direction="out"/> \ |
|
+ <arg type="s" name="summary" direction="out"/> \ |
|
+ </signal> \ |
|
+ <signal name="Finished"> \ |
|
+ <arg type="u" name="exit" direction="out"/> \ |
|
+ <arg type="u" name="runtime" direction="out"/> \ |
|
+ </signal> \ |
|
+</interface> \ |
|
+</node>'; |
|
+ |
|
+const LoginManagerIface = '<node> \ |
|
+<interface name="org.freedesktop.login1.Manager"> \ |
|
+<method name="Reboot"> \ |
|
+ <arg type="b" direction="in"/> \ |
|
+</method> \ |
|
+<method name="CanReboot"> \ |
|
+ <arg type="s" direction="out"/> \ |
|
+</method> \ |
|
+</interface> \ |
|
+</node>'; |
|
+ |
|
+const PkProxy = Gio.DBusProxy.makeProxyWrapper(PkIface); |
|
+const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface); |
|
+const PkTransactionProxy = Gio.DBusProxy.makeProxyWrapper(PkTransactionIface); |
|
+const LoginManagerProxy = Gio.DBusProxy.makeProxyWrapper(LoginManagerIface); |
|
+ |
|
+let pkProxy = null; |
|
+let pkOfflineProxy = null; |
|
+let loginManagerProxy = null; |
|
+let updatesDialog = null; |
|
+let extensionSettings = null; |
|
+let cancellable = null; |
|
+ |
|
+let updatesCheckInProgress = false; |
|
+let updatesCheckRequested = false; |
|
+let securityUpdates = []; |
|
+ |
|
+function getDetailText(period) { |
|
+ let text = _("Important security updates need to be installed.\n"); |
|
+ if (period < 60) |
|
+ text += ngettext("You can close this dialog and get %d minute to finish your work.", |
|
+ "You can close this dialog and get %d minutes to finish your work.", |
|
+ period).format(period); |
|
+ else |
|
+ text += ngettext("You can close this dialog and get %d hour to finish your work.", |
|
+ "You can close this dialog and get %d hours to finish your work.", |
|
+ Math.floor(period / 60)).format(Math.floor(period / 60)); |
|
+ return text; |
|
+} |
|
+ |
|
+const UpdatesDialog = new Lang.Class({ |
|
+ Name: 'UpdatesDialog', |
|
+ Extends: ModalDialog.ModalDialog, |
|
+ |
|
+ _init: function(settings) { |
|
+ this.parent({ styleClass: 'end-session-dialog', |
|
+ destroyOnClose: false }); |
|
+ |
|
+ this._gracePeriod = settings.get_uint('grace-period'); |
|
+ this._gracePeriod = Math.min(Math.max(10, this._gracePeriod), 24*60); |
|
+ this._lastWarningPeriod = settings.get_uint('last-warning-period'); |
|
+ this._lastWarningPeriod = Math.min(Math.max(1, this._lastWarningPeriod), this._gracePeriod - 1); |
|
+ this._lastWarnings = settings.get_uint('last-warnings'); |
|
+ this._lastWarnings = Math.min(Math.max(1, this._lastWarnings), |
|
+ Math.floor((this._gracePeriod - 1) / this._lastWarningPeriod)); |
|
+ |
|
+ let messageLayout = new St.BoxLayout({ vertical: true, |
|
+ style_class: 'end-session-dialog-layout' }); |
|
+ this.contentLayout.add(messageLayout, |
|
+ { x_fill: true, |
|
+ y_fill: true, |
|
+ y_expand: true }); |
|
+ |
|
+ let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject', |
|
+ style: 'padding-bottom: 1em;', |
|
+ text: _("Important security updates") }); |
|
+ messageLayout.add(subjectLabel, |
|
+ { x_fill: false, |
|
+ y_fill: false, |
|
+ x_align: St.Align.START, |
|
+ y_align: St.Align.START }); |
|
+ |
|
+ this._detailLabel = new St.Label({ style_class: 'end-session-dialog-description', |
|
+ style: 'padding-bottom: 0em;', |
|
+ text: getDetailText(this._gracePeriod) }); |
|
+ this._detailLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; |
|
+ this._detailLabel.clutter_text.line_wrap = true; |
|
+ |
|
+ messageLayout.add(this._detailLabel, |
|
+ { y_fill: true, |
|
+ y_align: St.Align.START }); |
|
+ |
|
+ let buttons = [{ action: Lang.bind(this, this.close), |
|
+ label: _("Close"), |
|
+ key: Clutter.Escape }, |
|
+ { action: Lang.bind(this, this._done), |
|
+ label: _("Restart & Install") }]; |
|
+ |
|
+ this.setButtons(buttons); |
|
+ |
|
+ this._openTimeoutId = 0; |
|
+ this.connect('destroy', Lang.bind(this, this._clearOpenTimeout)); |
|
+ |
|
+ this._startTimer(); |
|
+ }, |
|
+ |
|
+ _clearOpenTimeout: function() { |
|
+ if (this._openTimeoutId > 0) { |
|
+ GLib.source_remove(this._openTimeoutId); |
|
+ this._openTimeoutId = 0; |
|
+ } |
|
+ }, |
|
+ |
|
+ tryOpen: function() { |
|
+ if (this._openTimeoutId > 0 || this.open()) |
|
+ return; |
|
+ |
|
+ this._openTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, |
|
+ Lang.bind(this, function() { |
|
+ if (!this.open()) |
|
+ return GLib.SOURCE_CONTINUE; |
|
+ |
|
+ this._clearOpenTimeout(); |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ }, |
|
+ |
|
+ _startTimer: function() { |
|
+ this._secondsLeft = this._gracePeriod*60; |
|
+ |
|
+ this._timerId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, Lang.bind(this, |
|
+ function() { |
|
+ this._secondsLeft -= 1; |
|
+ let minutesLeft = this._secondsLeft / 60; |
|
+ let periodLeft = Math.floor(minutesLeft); |
|
+ |
|
+ if (this._secondsLeft == 60 || |
|
+ (periodLeft > 0 && periodLeft <= this._lastWarningPeriod * this._lastWarnings && |
|
+ minutesLeft % this._lastWarningPeriod == 0)) { |
|
+ this.tryOpen(); |
|
+ this._detailLabel.text = getDetailText(periodLeft); |
|
+ } |
|
+ |
|
+ if (this._secondsLeft > 0) { |
|
+ if (this._secondsLeft < 60) { |
|
+ let seconds = EndSessionDialog._roundSecondsToInterval(this._gracePeriod*60, this._secondsLeft, 10); |
|
+ this._detailLabel.text = |
|
+ _("Important security updates need to be installed now.\n") + |
|
+ ngettext("This computer will restart in %d second.", |
|
+ "This computer will restart in %d seconds.", |
|
+ seconds).format(seconds); |
|
+ } |
|
+ return GLib.SOURCE_CONTINUE; |
|
+ } |
|
+ |
|
+ this._done(); |
|
+ return GLib.SOURCE_REMOVE; |
|
+ })); |
|
+ this.connect('destroy', Lang.bind(this, function() { |
|
+ if (this._timerId > 0) { |
|
+ GLib.source_remove(this._timerId); |
|
+ this._timerId = 0; |
|
+ } |
|
+ })); |
|
+ }, |
|
+ |
|
+ _done: function() { |
|
+ this.emit('done'); |
|
+ this.destroy(); |
|
+ }, |
|
+ |
|
+ getState: function() { |
|
+ return [this._gracePeriod, this._lastWarningPeriod, this._lastWarnings, this._secondsLeft]; |
|
+ }, |
|
+ |
|
+ setState: function(state) { |
|
+ [this._gracePeriod, this._lastWarningPeriod, this._lastWarnings, this._secondsLeft] = state; |
|
+ }, |
|
+}); |
|
+Signals.addSignalMethods(UpdatesDialog.prototype); |
|
+ |
|
+function showDialog() { |
|
+ if (updatesDialog) |
|
+ return; |
|
+ |
|
+ updatesDialog = new UpdatesDialog(extensionSettings); |
|
+ updatesDialog.tryOpen(); |
|
+ updatesDialog.connect('destroy', function() { updatesDialog = null; }); |
|
+ updatesDialog.connect('done', function() { |
|
+ if (pkOfflineProxy.TriggerAction == 'power-off' || |
|
+ pkOfflineProxy.TriggerAction == 'reboot') { |
|
+ loginManagerProxy.RebootRemote(false); |
|
+ } else { |
|
+ pkOfflineProxy.TriggerRemote('reboot', function(result, error) { |
|
+ if (!error) |
|
+ loginManagerProxy.RebootRemote(false); |
|
+ else |
|
+ log('Failed to trigger offline update: %s'.format(error.message)); |
|
+ }); |
|
+ } |
|
+ }); |
|
+} |
|
+ |
|
+function cancelDialog(save) { |
|
+ if (!updatesDialog) |
|
+ return; |
|
+ |
|
+ if (save) { |
|
+ let state = GLib.Variant.new('(uuuu)', updatesDialog.getState()); |
|
+ global.set_runtime_state(Me.uuid, state); |
|
+ } |
|
+ updatesDialog.destroy(); |
|
+} |
|
+ |
|
+function restoreExistingState() { |
|
+ let state = global.get_runtime_state('(uuuu)', Me.uuid); |
|
+ if (state === null) |
|
+ return false; |
|
+ |
|
+ global.set_runtime_state(Me.uuid, null); |
|
+ showDialog(); |
|
+ updatesDialog.setState(state.deep_unpack()); |
|
+ return true; |
|
+} |
|
+ |
|
+function syncState() { |
|
+ if (!pkOfflineProxy || !loginManagerProxy) |
|
+ return; |
|
+ |
|
+ if (restoreExistingState()) |
|
+ return; |
|
+ |
|
+ if (!updatesCheckInProgress && |
|
+ securityUpdates.length > 0 && |
|
+ pkOfflineProxy.UpdatePrepared) |
|
+ showDialog(); |
|
+ else |
|
+ cancelDialog(); |
|
+} |
|
+ |
|
+function doPkTransaction(callback) { |
|
+ if (!pkProxy) |
|
+ return; |
|
+ |
|
+ pkProxy.CreateTransactionRemote(function(result, error) { |
|
+ if (error) { |
|
+ log('Error creating PackageKit transaction: %s'.format(error.message)); |
|
+ checkUpdatesDone(); |
|
+ return; |
|
+ } |
|
+ |
|
+ new PkTransactionProxy(Gio.DBus.system, |
|
+ 'org.freedesktop.PackageKit', |
|
+ String(result), |
|
+ function(proxy, error) { |
|
+ if (!error) { |
|
+ proxy.SetHintsRemote( |
|
+ ['background=true', 'interactive=false'], |
|
+ function(result, error) { |
|
+ if (error) { |
|
+ log('Error connecting to PackageKit: %s'.format(error.message)); |
|
+ checkUpdatesDone(); |
|
+ return; |
|
+ } |
|
+ callback(proxy); |
|
+ }); |
|
+ } else { |
|
+ log('Error connecting to PackageKit: %s'.format(error.message)); |
|
+ } |
|
+ }); |
|
+ }); |
|
+} |
|
+ |
|
+function pkUpdatePackages(proxy) { |
|
+ proxy.connectSignal('Finished', function(p, e, params) { |
|
+ let [exit, runtime] = params; |
|
+ |
|
+ if (exit == PkgKit.ExitEnum.CANCELLED_PRIORITY) { |
|
+ // try again |
|
+ checkUpdates(); |
|
+ } else if (exit != PkgKit.ExitEnum.SUCCESS) { |
|
+ log('UpdatePackages failed: %s'.format(PkgKit.ExitEnum.to_string(exit))); |
|
+ } |
|
+ |
|
+ checkUpdatesDone(); |
|
+ }); |
|
+ proxy.UpdatePackagesRemote(1 << PkgKit.TransactionFlagEnum.ONLY_DOWNLOAD, securityUpdates); |
|
+} |
|
+ |
|
+function pkGetUpdates(proxy) { |
|
+ proxy.connectSignal('Package', function(p, e, params) { |
|
+ let [info, packageId, summary] = params; |
|
+ |
|
+ if (info == PkgKit.InfoEnum.SECURITY) |
|
+ securityUpdates.push(packageId); |
|
+ }); |
|
+ proxy.connectSignal('Finished', function(p, e, params) { |
|
+ let [exit, runtime] = params; |
|
+ |
|
+ if (exit == PkgKit.ExitEnum.SUCCESS) { |
|
+ if (securityUpdates.length > 0) { |
|
+ doPkTransaction(pkUpdatePackages); |
|
+ return; |
|
+ } |
|
+ } else if (exit == PkgKit.ExitEnum.CANCELLED_PRIORITY) { |
|
+ // try again |
|
+ checkUpdates(); |
|
+ } else { |
|
+ log('GetUpdates failed: %s'.format(PkgKit.ExitEnum.to_string(exit))); |
|
+ } |
|
+ |
|
+ checkUpdatesDone(); |
|
+ }); |
|
+ proxy.GetUpdatesRemote(0); |
|
+} |
|
+ |
|
+function checkUpdatesDone() { |
|
+ updatesCheckInProgress = false; |
|
+ if (updatesCheckRequested) { |
|
+ updatesCheckRequested = false; |
|
+ checkUpdates(); |
|
+ } else { |
|
+ syncState(); |
|
+ } |
|
+} |
|
+ |
|
+function checkUpdates() { |
|
+ if (updatesCheckInProgress) { |
|
+ updatesCheckRequested = true; |
|
+ return; |
|
+ } |
|
+ updatesCheckInProgress = true; |
|
+ securityUpdates = []; |
|
+ doPkTransaction(pkGetUpdates); |
|
+} |
|
+ |
|
+function initSystemProxies() { |
|
+ new PkProxy(Gio.DBus.system, |
|
+ 'org.freedesktop.PackageKit', |
|
+ '/org/freedesktop/PackageKit', |
|
+ function(proxy, error) { |
|
+ if (!error) { |
|
+ pkProxy = proxy; |
|
+ let id = pkProxy.connectSignal('UpdatesChanged', checkUpdates); |
|
+ pkProxy._signalId = id; |
|
+ checkUpdates(); |
|
+ } else { |
|
+ log('Error connecting to PackageKit: %s'.format(error.message)); |
|
+ } |
|
+ }, |
|
+ cancellable); |
|
+ new PkOfflineProxy(Gio.DBus.system, |
|
+ 'org.freedesktop.PackageKit', |
|
+ '/org/freedesktop/PackageKit', |
|
+ function(proxy, error) { |
|
+ if (!error) { |
|
+ pkOfflineProxy = proxy; |
|
+ let id = pkOfflineProxy.connect('g-properties-changed', syncState); |
|
+ pkOfflineProxy._signalId = id; |
|
+ syncState(); |
|
+ } else { |
|
+ log('Error connecting to PackageKit: %s'.format(error.message)); |
|
+ } |
|
+ }, |
|
+ cancellable); |
|
+ new LoginManagerProxy(Gio.DBus.system, |
|
+ 'org.freedesktop.login1', |
|
+ '/org/freedesktop/login1', |
|
+ function(proxy, error) { |
|
+ if (!error) { |
|
+ proxy.CanRebootRemote(cancellable, function(result, error) { |
|
+ if (!error && result == 'yes') { |
|
+ loginManagerProxy = proxy; |
|
+ syncState(); |
|
+ } else { |
|
+ log('Reboot is not available'); |
|
+ } |
|
+ }); |
|
+ } else { |
|
+ log('Error connecting to Login manager: %s'.format(error.message)); |
|
+ } |
|
+ }, |
|
+ cancellable); |
|
+} |
|
+ |
|
+function init(metadata) { |
|
+} |
|
+ |
|
+function enable() { |
|
+ cancellable = new Gio.Cancellable(); |
|
+ extensionSettings = Convenience.getSettings(); |
|
+ Polkit.Permission.new("org.freedesktop.packagekit.trigger-offline-update", |
|
+ null, cancellable, function(p, result) { |
|
+ try { |
|
+ let permission = Polkit.Permission.new_finish(result); |
|
+ if (permission && permission.allowed) |
|
+ initSystemProxies(); |
|
+ else |
|
+ throw(new Error('not allowed')); |
|
+ } catch(e) { |
|
+ log('No permission to trigger offline updates: %s'.format(e.toString())); |
|
+ } |
|
+ }); |
|
+} |
|
+ |
|
+function disable() { |
|
+ cancelDialog(true); |
|
+ cancellable.cancel(); |
|
+ cancellable = null; |
|
+ extensionSettings = null; |
|
+ updatesDialog = null; |
|
+ loginManagerProxy = null; |
|
+ if (pkOfflineProxy) { |
|
+ pkOfflineProxy.disconnect(pkOfflineProxy._signalId); |
|
+ pkOfflineProxy = null; |
|
+ } |
|
+ if (pkProxy) { |
|
+ pkProxy.disconnectSignal(pkProxy._signalId); |
|
+ pkProxy = null; |
|
+ } |
|
+} |
|
diff --git a/extensions/updates-dialog/meson.build b/extensions/updates-dialog/meson.build |
|
new file mode 100644 |
|
index 0000000..585c02d |
|
--- /dev/null |
|
+++ b/extensions/updates-dialog/meson.build |
|
@@ -0,0 +1,7 @@ |
|
+extension_data += configure_file( |
|
+ input: metadata_name + '.in', |
|
+ output: metadata_name, |
|
+ configuration: metadata_conf |
|
+) |
|
+ |
|
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml') |
|
diff --git a/extensions/updates-dialog/metadata.json.in b/extensions/updates-dialog/metadata.json.in |
|
new file mode 100644 |
|
index 0000000..9946abb |
|
--- /dev/null |
|
+++ b/extensions/updates-dialog/metadata.json.in |
|
@@ -0,0 +1,10 @@ |
|
+{ |
|
+"extension-id": "@extension_id@", |
|
+"uuid": "@uuid@", |
|
+"settings-schema": "@gschemaname@", |
|
+"gettext-domain": "@gettext_domain@", |
|
+"name": "Updates Dialog", |
|
+"description": "Shows a modal dialog when there are software updates.", |
|
+"shell-version": [ "@shell_current@" ], |
|
+"url": "http://rtcm.fedorapeople.org/updates-dialog" |
|
+} |
|
diff --git a/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml |
|
new file mode 100644 |
|
index 0000000..c08d33c |
|
--- /dev/null |
|
+++ b/extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml |
|
@@ -0,0 +1,30 @@ |
|
+<?xml version="1.0" encoding="UTF-8"?> |
|
+<schemalist> |
|
+ <schema path="/org/gnome/shell/extensions/updates-dialog/" |
|
+ id="org.gnome.shell.extensions.updates-dialog"> |
|
+ <key name="grace-period" type="u"> |
|
+ <default>300</default> |
|
+ <summary>Grace period in minutes</summary> |
|
+ <description> |
|
+ When the grace period is over, the computer will automatically |
|
+ reboot and install security updates. |
|
+ </description> |
|
+ </key> |
|
+ <key name="last-warning-period" type="u"> |
|
+ <default>10</default> |
|
+ <summary>Last warning dialog period</summary> |
|
+ <description> |
|
+ A last warning dialog is displayed this many minutes before |
|
+ the automatic reboot. |
|
+ </description> |
|
+ </key> |
|
+ <key name="last-warnings" type="u"> |
|
+ <default>1</default> |
|
+ <summary>Number of last warning dialogs</summary> |
|
+ <description> |
|
+ How many warning dialogs are displayed. Each is displayed at |
|
+ 'last-warning-period' minute intervals. |
|
+ </description> |
|
+ </key> |
|
+ </schema> |
|
+</schemalist> |
|
diff --git a/extensions/updates-dialog/stylesheet.css b/extensions/updates-dialog/stylesheet.css |
|
new file mode 100644 |
|
index 0000000..25134b6 |
|
--- /dev/null |
|
+++ b/extensions/updates-dialog/stylesheet.css |
|
@@ -0,0 +1 @@ |
|
+/* This extensions requires no special styling */ |
|
diff --git a/meson.build b/meson.build |
|
index 3451585..08a243e 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -57,6 +57,7 @@ all_extensions += [ |
|
'native-window-placement', |
|
'panel-favorites', |
|
'top-icons', |
|
+ 'updates-dialog', |
|
'user-theme' |
|
] |
|
|
|
diff --git a/po/POTFILES.in b/po/POTFILES.in |
|
index d98ca1b..43a817f 100644 |
|
--- a/po/POTFILES.in |
|
+++ b/po/POTFILES.in |
|
@@ -15,6 +15,8 @@ extensions/native-window-placement/org.gnome.shell.extensions.native-window-plac |
|
extensions/places-menu/extension.js |
|
extensions/places-menu/placeDisplay.js |
|
extensions/screenshot-window-sizer/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml |
|
+extensions/updates-dialog/extension.js |
|
+extensions/updates-dialog/org.gnome.shell.extensions.updates-dialog.gschema.xml |
|
extensions/user-theme/extension.js |
|
extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml |
|
extensions/window-list/extension.js |
|
-- |
|
2.17.1 |
|
|
|
|
|
From e5f70e34dc8bb90fe1df024c5964bb0b206ea6b5 Mon Sep 17 00:00:00 2001 |
|
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org> |
|
Date: Thu, 1 Jun 2017 23:57:14 +0200 |
|
Subject: [PATCH 5/5] Add no-hot-corner extension |
|
|
|
--- |
|
extensions/no-hot-corner/extension.js | 31 +++++++++++++++++++++++ |
|
extensions/no-hot-corner/meson.build | 5 ++++ |
|
extensions/no-hot-corner/metadata.json.in | 9 +++++++ |
|
extensions/no-hot-corner/stylesheet.css | 1 + |
|
meson.build | 1 + |
|
5 files changed, 47 insertions(+) |
|
create mode 100644 extensions/no-hot-corner/extension.js |
|
create mode 100644 extensions/no-hot-corner/meson.build |
|
create mode 100644 extensions/no-hot-corner/metadata.json.in |
|
create mode 100644 extensions/no-hot-corner/stylesheet.css |
|
|
|
diff --git a/extensions/no-hot-corner/extension.js b/extensions/no-hot-corner/extension.js |
|
new file mode 100644 |
|
index 0000000..e7a0d63 |
|
--- /dev/null |
|
+++ b/extensions/no-hot-corner/extension.js |
|
@@ -0,0 +1,31 @@ |
|
+const Main = imports.ui.main; |
|
+ |
|
+let _id; |
|
+ |
|
+function _disableHotCorners() { |
|
+ // Disables all hot corners |
|
+ Main.layoutManager.hotCorners.forEach(function(hotCorner) { |
|
+ if (!hotCorner) { |
|
+ return; |
|
+ } |
|
+ |
|
+ hotCorner._toggleOverview = function() {}; |
|
+ hotCorner._pressureBarrier._trigger = function() {}; |
|
+ }); |
|
+} |
|
+ |
|
+function init() { |
|
+} |
|
+ |
|
+function enable() { |
|
+ _disableHotCorners(); |
|
+ // Hot corners may be re-created afterwards (for example, If there's a monitor change). |
|
+ // So we catch all changes. |
|
+ _id = Main.layoutManager.connect('hot-corners-changed', _disableHotCorners); |
|
+} |
|
+ |
|
+function disable() { |
|
+ // Disconnects the callback and re-creates the hot corners |
|
+ Main.layoutManager.disconnect(_id); |
|
+ Main.layoutManager._updateHotCorners(); |
|
+} |
|
diff --git a/extensions/no-hot-corner/meson.build b/extensions/no-hot-corner/meson.build |
|
new file mode 100644 |
|
index 0000000..48504f6 |
|
--- /dev/null |
|
+++ b/extensions/no-hot-corner/meson.build |
|
@@ -0,0 +1,5 @@ |
|
+extension_data += configure_file( |
|
+ input: metadata_name + '.in', |
|
+ output: metadata_name, |
|
+ configuration: metadata_conf |
|
+) |
|
diff --git a/extensions/no-hot-corner/metadata.json.in b/extensions/no-hot-corner/metadata.json.in |
|
new file mode 100644 |
|
index 0000000..406d83b |
|
--- /dev/null |
|
+++ b/extensions/no-hot-corner/metadata.json.in |
|
@@ -0,0 +1,9 @@ |
|
+{ |
|
+"extension-id": "@extension_id@", |
|
+"uuid": "@uuid@", |
|
+"name": "No Topleft Hot Corner", |
|
+"description": "Disable the hot corner in the top left; you can still reach the overview by clicking the Activities button or pressing the dedicated key.", |
|
+"shell-version": [ "@shell_current@" ], |
|
+"url": "https://github.com/HROMANO/nohotcorner/", |
|
+"version": 15 |
|
+} |
|
diff --git a/extensions/no-hot-corner/stylesheet.css b/extensions/no-hot-corner/stylesheet.css |
|
new file mode 100644 |
|
index 0000000..25134b6 |
|
--- /dev/null |
|
+++ b/extensions/no-hot-corner/stylesheet.css |
|
@@ -0,0 +1 @@ |
|
+/* This extensions requires no special styling */ |
|
diff --git a/meson.build b/meson.build |
|
index 08a243e..201c484 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -55,6 +55,7 @@ all_extensions += [ |
|
'dash-to-dock', |
|
'example', |
|
'native-window-placement', |
|
+ 'no-hot-corner', |
|
'panel-favorites', |
|
'top-icons', |
|
'updates-dialog', |
|
-- |
|
2.17.1 |
|
|
|
|