summaryrefslogtreecommitdiffstats
path: root/extensions/vertical-workspaces/dash.js
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/vertical-workspaces/dash.js')
-rw-r--r--extensions/vertical-workspaces/dash.js829
1 files changed, 829 insertions, 0 deletions
diff --git a/extensions/vertical-workspaces/dash.js b/extensions/vertical-workspaces/dash.js
new file mode 100644
index 0000000..c4e3101
--- /dev/null
+++ b/extensions/vertical-workspaces/dash.js
@@ -0,0 +1,829 @@
+/**
+ * Vertical Workspaces
+ * dash.js
+ *
+ * @author GdH <G-dH@github.com>
+ * @copyright 2022-2023
+ * @license GPL-3.0
+ * modified dash module of https://github.com/RensAlthuis/vertical-overview extension
+ */
+
+const { Clutter, GLib, GObject, Graphene, Meta, Shell, St } = imports.gi;
+const AppDisplay = imports.ui.appDisplay;
+const AppFavorites = imports.ui.appFavorites;
+const DND = imports.ui.dnd;
+const IconGrid = imports.ui.iconGrid;
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+const Dash = imports.ui.dash;
+const { DashIcon, DashItemContainer, getAppFromSource, DragPlaceholderItem } = imports.ui.dash;
+
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const Util = Me.imports.util;
+const _ = Me.imports.settings._;
+
+let verticalOverrides = {};
+let _origWorkId;
+let _newWorkId;
+let _showAppsIconBtnPressId;
+
+// added values to achieve better ability to scale down according the available space
+var BaseIconSizes = [16, 24, 32, 40, 44, 48, 56, 64, 72, 80, 96, 112, 128];
+
+const RecentFilesSearchProviderPrefix = Me.imports.recentFilesSearchProvider.prefix;
+const WindowSearchProviderPrefix = Me.imports.windowSearchProvider.prefix;
+
+let _overrides;
+
+const DASH_ITEM_LABEL_SHOW_TIME = 150;
+
+let opt;
+
+function update(reset = false) {
+ if (_overrides) {
+ _overrides.removeAll();
+ }
+
+ opt = Me.imports.settings.opt;
+ const dash = Main.overview._overview._controls.layoutManager._dash;
+
+ setToHorizontal();
+
+ dash.remove_style_class_name("vertical-overview");
+ dash.remove_style_class_name("vertical-overview-left");
+ dash.remove_style_class_name("vertical-overview-right");
+
+ if (reset) {
+ _moveDashAppGridIcon(reset);
+ _connectShowAppsIcon(reset);
+ _updateSearchWindowsIcon(false);
+ _updateRecentFilesIcon(false);
+ dash.visible = true;
+ dash._background.opacity = 255;
+ _overrides = null;
+ opt = null;
+ return;
+ }
+
+ _overrides = new Util.Overrides();
+
+ _overrides.addOverride('DashItemContainer', Dash.DashItemContainer.prototype, DashItemContainerOverride);
+ _overrides.addOverride('DashCommon', Dash.Dash.prototype, DashCommonOverride);
+
+ if (opt.DASH_VERTICAL) {
+ _overrides.addOverride('Dash', Dash.Dash.prototype, DashOverride);
+ setToVertical();
+ dash.add_style_class_name("vertical-overview");
+
+ if (!_newWorkId) {
+ _origWorkId = dash._workId;
+ dash._workId = Main.initializeDeferredWork(dash._box, dash._redisplay.bind(dash));
+ _newWorkId = dash._workId;
+ } else {
+ dash._workId = _newWorkId;
+ }
+ } else {
+ setToHorizontal();
+ if (_origWorkId)
+ dash._workId = _origWorkId;
+ }
+
+ _updateSearchWindowsIcon();
+ _updateRecentFilesIcon();
+ _moveDashAppGridIcon();
+ _connectShowAppsIcon();
+
+ if (dash._showWindowsIcon && !dash._showWindowsIconClickedId) {
+ dash._showWindowsIconClickedId = dash._showWindowsIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(WindowSearchProviderPrefix));
+ }
+ if (dash._recentFilesIcon && !dash._recentFilesIconClickedId) {
+ dash._recentFilesIconClickedId = dash._recentFilesIcon.toggleButton.connect('clicked', (a, c) => c && Util.activateSearchProvider(RecentFilesSearchProviderPrefix));
+ }
+ Main.overview.dash._redisplay();
+ Main.overview._overview._controls.layoutManager._dash.visible = opt.DASH_VISIBLE;
+}
+
+function setToVertical() {
+ let dash = Main.overview._overview._controls.layoutManager._dash;
+
+ dash._box.layout_manager.orientation = Clutter.Orientation.VERTICAL;
+ dash._dashContainer.layout_manager.orientation = Clutter.Orientation.VERTICAL;
+ dash._dashContainer.y_expand = false;
+ dash._dashContainer.x_expand = true;
+ dash.x_align = Clutter.ActorAlign.START;
+ dash.y_align = Clutter.ActorAlign.CENTER;
+
+ let sizerBox = dash._background.get_children()[0];
+ sizerBox.clear_constraints();
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: dash._showAppsIcon.icon,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: dash._dashContainer,
+ coordinate: Clutter.BindCoordinate.HEIGHT,
+ }));
+ dash._box.remove_all_children();
+ dash._separator = null;
+ dash._queueRedisplay();
+ dash._adjustIconSize();
+
+ dash.add_style_class_name(opt.DASH_LEFT ? 'vertical-overview-left' : 'vertical-overview-right');
+}
+
+function setToHorizontal() {
+ let dash = Main.overview._overview._controls.layoutManager._dash;
+ if (_origWorkId)
+ dash._workId = _origWorkId; //pretty sure this is a leak, but there no provided way to disconnect these...
+ dash._box.layout_manager.orientation = Clutter.Orientation.HORIZONTAL;
+ dash._dashContainer.layout_manager.orientation = Clutter.Orientation.HORIZONTAL;
+ dash._dashContainer.y_expand = true;
+ dash._dashContainer.x_expand = false;
+ dash.x_align = Clutter.ActorAlign.CENTER;
+ dash.y_align = 0;
+
+ let sizerBox = dash._background.get_children()[0];
+ sizerBox.clear_constraints();
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: dash._showAppsIcon.icon,
+ coordinate: Clutter.BindCoordinate.HEIGHT,
+ }));
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: dash._dashContainer,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+
+ dash._box.remove_all_children();
+ dash._separator = null;
+ dash._queueRedisplay();
+ dash._adjustIconSize();
+}
+
+function _moveDashAppGridIcon(reset = false) {
+ // move dash app grid icon to the front
+ const dash = Main.overview._overview._controls.layoutManager._dash;
+
+ const appIconPosition = opt.get('showAppsIconPosition', true);
+ dash._showAppsIcon.remove_style_class_name('show-apps-icon-vertical-hide');
+ dash._showAppsIcon.remove_style_class_name('show-apps-icon-horizontal-hide');
+ dash._showAppsIcon.opacity = 255;
+ if (!reset && appIconPosition === 0) // 0 - start
+ dash._dashContainer.set_child_at_index(dash._showAppsIcon, 0);
+ if (reset || appIconPosition === 1) { // 1 - end
+ const index = dash._dashContainer.get_children().length - 1;
+ dash._dashContainer.set_child_at_index(dash._showAppsIcon, index);
+ }
+ if (!reset && appIconPosition === 2) {// 2 - hide
+ const style = opt.DASH_VERTICAL ? 'show-apps-icon-vertical-hide' : 'show-apps-icon-horizontal-hide';
+ dash._showAppsIcon.add_style_class_name(style);
+ // for some reason even if the icon height in vertical mode should be set to 0 by the style, it stays visible in full size returning height 1px
+ dash._showAppsIcon.opacity = 0;
+ }
+}
+
+function _connectShowAppsIcon(reset = false) {
+ if (!reset) {
+ if (_showAppsIconBtnPressId || Util.dashIsDashToDock()) {
+ // button is already connected || dash is Dash to Dock
+ return;
+ }
+
+ Main.overview.dash._showAppsIcon.reactive = true;
+ _showAppsIconBtnPressId = Main.overview.dash._showAppsIcon.connect('button-press-event', (actor, event) => {
+ const button = event.get_button();
+ if (button === Clutter.BUTTON_MIDDLE) {
+ Util.openPreferences();
+ } else if (button === Clutter.BUTTON_SECONDARY) {
+ Util.activateSearchProvider(WindowSearchProviderPrefix);
+ } else {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ });
+ } else {
+ if (_showAppsIconBtnPressId) {
+ Main.overview.dash._showAppsIcon.disconnect(_showAppsIconBtnPressId);
+ _showAppsIconBtnPressId = 0;
+ Main.overview.dash._showAppsIcon.reactive = false;
+ }
+ }
+}
+
+var DashOverride = {
+ handleDragOver: function (source, actor, _x, y, _time) {
+ let app = getAppFromSource(source);
+
+ // Don't allow favoriting of transient apps
+ if (app == null || app.is_window_backed())
+ return DND.DragMotionResult.NO_DROP;
+
+ if (!global.settings.is_writable('favorite-apps'))
+ 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 = this._box.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._dragPlaceholder.height;
+ numChildren--;
+ }
+
+ // Same with the separator
+ if (this._separator) {
+ boxHeight -= this._separator.height;
+ numChildren--;
+ }
+
+ let pos;
+ if (!this._emptyDropTarget)
+ pos = Math.floor(y * numChildren / boxHeight);
+ else
+ pos = 0; // always insert at the top when dash is empty
+
+ // Put the placeholder after the last favorite if we are not
+ // in the favorites zone
+ if (pos > numFavorites)
+ pos = numFavorites;
+
+ if (pos !== this._dragPlaceholderPos && 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 DragPlaceholderItem();
+ this._dragPlaceholder.child.set_width(this.iconSize / 2);
+ this._dragPlaceholder.child.set_height(this.iconSize);
+ this._box.insert_child_at_index(this._dragPlaceholder,
+ this._dragPlaceholderPos);
+ this._dragPlaceholder.show(fadeIn);
+ }
+
+ if (!this._dragPlaceholder)
+ return DND.DragMotionResult.NO_DROP;
+
+ let srcIsFavorite = favPos != -1;
+
+ if (srcIsFavorite)
+ return DND.DragMotionResult.MOVE_DROP;
+
+ return DND.DragMotionResult.COPY_DROP;
+ },
+
+ _redisplay: function () {
+ let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let running = this._appSystem.get_running();
+
+ let children = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.app;
+ });
+ // Apps currently in the dash
+ let oldApps = children.map(actor => actor.child._delegate.app);
+ // Apps supposed to be in the dash
+ let newApps = [];
+
+ for (let id in favorites)
+ newApps.push(favorites[id]);
+
+ for (let i = 0; i < running.length; i++) {
+ let app = running[i];
+ if (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) {
+ let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null;
+ let newApp = newApps.length > newIndex ? newApps[newIndex] : null;
+
+ // No change at oldIndex/newIndex
+ if (oldApp == newApp) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // App removed at oldIndex
+ if (oldApp && !newApps.includes(oldApp)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // App added at newIndex
+ if (newApp && !oldApps.includes(newApp)) {
+ addedItems.push({
+ app: newApp,
+ item: this._createAppItem(newApp),
+ pos: newIndex
+ });
+ newIndex++;
+ continue;
+ }
+
+ // App moved
+ let nextApp = newApps.length > newIndex + 1
+ ? newApps[newIndex + 1] : null;
+ let insertHere = nextApp && nextApp == oldApp;
+ let alreadyRemoved = removedActors.reduce((result, actor) => {
+ let removedApp = actor.child._delegate.app;
+ return result || removedApp == newApp;
+ }, false);
+
+ if (insertHere || alreadyRemoved) {
+ let newItem = this._createAppItem(newApp);
+ addedItems.push({
+ app: newApp,
+ 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
+ // or hidden
+ if (Main.overview.visible && !Main.overview.animationInProgress)
+ item.animateOutAndDestroy();
+ else
+ item.destroy();
+ }
+
+ this._adjustIconSize();
+
+ // 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.visible &&
+ !Main.overview.animationInProgress;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ for (let i = 0; i < addedItems.length; i++)
+ addedItems[i].item.show(animate);
+
+ // Update separator
+ const nFavorites = Object.keys(favorites).length;
+ const nIcons = children.length + addedItems.length - removedActors.length;
+ if (nFavorites > 0 && nFavorites < nIcons) {
+ // destroy the horizontal separator if it exists.
+ // this is incredibly janky, but I can't think of a better way atm.
+ if (this._separator && this._separator.height !== 1) {
+ this._separator.destroy();
+ this._separator = null;
+ }
+
+ if (!this._separator) {
+ this._separator = new St.Widget({
+ style_class: 'dash-separator',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ width: this.iconSize,
+ height: 1,
+ });
+ this._box.add_child(this._separator)
+ }
+
+ //FIXME: separator placement is broken (also in original dash)
+ let pos = nFavorites;
+ if (this._dragPlaceholder)
+ pos++;
+ this._box.set_child_at_index(this._separator, pos);
+ } else if (this._separator) {
+ this._separator.destroy();
+ this._separator = null;
+ }
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this._box.queue_relayout();
+ },
+
+ _createAppItem: function (app) {
+ let appIcon = new DashIcon(app);
+
+ let indicator = appIcon._dot;
+ indicator.x_align = opt.DASH_LEFT ? Clutter.ActorAlign.START : Clutter.ActorAlign.END;
+ indicator.y_align = Clutter.ActorAlign.CENTER;
+
+ appIcon.connect('menu-state-changed',
+ (o, opened) => {
+ this._itemMenuStateChanged(item, opened);
+ });
+
+ let item = new DashItemContainer();
+ item.setChild(appIcon);
+
+ // Override default AppIcon label_actor, now the
+ // accessible_name is set at DashItemContainer.setLabelText
+ appIcon.label_actor = null;
+ item.setLabelText(app.get_name());
+
+ appIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(item, appIcon);
+
+ return item;
+ }
+}
+
+var DashItemContainerOverride = {
+ // move labels according dash position
+ showLabel: function() {
+ if (!this._labelText)
+ return;
+
+ this.label.set_text(this._labelText);
+ this.label.opacity = 0;
+ this.label.show();
+
+ let [stageX, stageY] = this.get_transformed_position();
+
+ const itemWidth = this.allocation.get_width();
+ const itemHeight = this.allocation.get_height();
+
+ const labelWidth = this.label.get_width();
+ const labelHeight = this.label.get_height();
+ const xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ let x = Math.clamp(stageX + xOffset, 0, global.stage.width - labelWidth);
+
+ let node = this.label.get_theme_node();
+ let y;
+
+ if (opt.DASH_TOP) {
+ const yOffset = itemHeight - labelHeight + 3 * node.get_length('-y-offset');
+ y = stageY + yOffset;
+
+ } else if (opt.DASH_BOTTOM) {
+ const yOffset = node.get_length('-y-offset');
+ y = stageY - this.label.height - yOffset;
+
+ } else if (opt.DASH_RIGHT) {
+ const yOffset = Math.floor((itemHeight - labelHeight) / 2);
+
+ const xOffset = 4;
+
+ x = stageX - xOffset - this.label.width;
+ y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight);
+
+ } if (opt.DASH_LEFT) {
+ const yOffset = Math.floor((itemHeight - labelHeight) / 2);
+
+ const xOffset = 4;
+
+ x = stageX + this.width + xOffset;
+ y = Math.clamp(stageY + yOffset, 0, global.stage.height - labelHeight);
+ }
+
+ this.label.set_position(x, y);
+ this.label.ease({
+ opacity: 255,
+ duration: DASH_ITEM_LABEL_SHOW_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this.label.set_position(x, y);
+ this.label.ease({
+ opacity: 255,
+ duration: DASH_ITEM_LABEL_SHOW_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+}
+
+var DashCommonOverride = {
+ _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(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon &&
+ !actor.animatingOut;
+ });
+
+ // add new custom icons into the calculation
+ if (this._showAppsIcon.visible) {
+ iconChildren.push(this._showAppsIcon);
+ }
+ if (this._showWindowsIcon) {
+ iconChildren.push(this._showWindowsIcon);
+ }
+ if (this._recentFilesIcon) {
+ iconChildren.push(this._recentFilesIcon);
+ }
+
+ if (!iconChildren.length) return;
+
+ if (this._maxWidth === -1 || this._maxHeight === -1)
+ return;
+
+ const dashHorizontal = !opt.DASH_VERTICAL;
+
+ const themeNode = this.get_theme_node();
+ const maxAllocation = new Clutter.ActorBox({
+ x1: 0,
+ y1: 0,
+ x2: dashHorizontal ? this._maxWidth : 42, // not whatever
+ y2: dashHorizontal ? 42 : this._maxHeight,
+ });
+
+ let maxContent = themeNode.get_content_box(maxAllocation);
+
+ let spacing = themeNode.get_length('spacing');
+
+ let firstButton = iconChildren[0].child;
+ let firstIcon = firstButton._delegate.icon;
+
+ if (!firstIcon.icon) return;
+
+ // Enforce valid spacings during the size request
+ firstIcon.icon.ensure_style();
+ const [, , iconWidth, iconHeight] = firstIcon.icon.get_preferred_size();
+ const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size();
+
+ let availWidth, availHeight, maxIconSize;
+ if (dashHorizontal) {
+ availWidth = maxContent.x2 - maxContent.x1;
+ // Subtract icon padding and box spacing from the available width
+ availWidth -= iconChildren.length * (buttonWidth - iconWidth) +
+ (iconChildren.length - 1) * spacing +
+ 2 * this._background.get_theme_node().get_horizontal_padding();
+
+ availHeight = this._maxHeight;
+ availHeight -= this.margin_top + this.margin_bottom;
+ availHeight -= this._background.get_theme_node().get_vertical_padding();
+ availHeight -= themeNode.get_vertical_padding();
+ availHeight -= buttonHeight - iconHeight;
+
+ maxIconSize = Math.min(availWidth / iconChildren.length, availHeight, opt.MAX_ICON_SIZE);
+ } else {
+ availWidth = this._maxWidth;
+ availWidth -= this._background.get_theme_node().get_horizontal_padding();
+ availWidth -= themeNode.get_horizontal_padding();
+ availWidth -= buttonWidth - iconWidth;
+
+ availHeight = maxContent.y2 - maxContent.y1;
+ availHeight -= iconChildren.length * (buttonHeight - iconHeight) +
+ (iconChildren.length - 1) * spacing +
+ 2 * this._background.get_theme_node().get_vertical_padding();
+
+ maxIconSize = Math.min(availWidth, availHeight / iconChildren.length, opt.MAX_ICON_SIZE);
+ }
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let iconSizes = BaseIconSizes.map(s => s * scaleFactor);
+
+ let newIconSize = BaseIconSizes[0];
+ for (let i = 0; i < iconSizes.length; i++) {
+ if (iconSizes[i] <= maxIconSize)
+ newIconSize = BaseIconSizes[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, not visible or when initially filling
+ // the dash
+ if (!Main.overview.visible || 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);
+
+ icon.icon.ease({
+ width: targetWidth,
+ height: targetHeight,
+ duration: Dash.DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ if (this._separator) {
+ this._separator.ease({
+ width: dashHorizontal ? 1 : this.iconSize,
+ height: dashHorizontal ? this.iconSize : 1,
+ duration: Dash.DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ },
+}
+
+function _updateSearchWindowsIcon(show = opt.SHOW_WINDOWS_ICON) {
+
+ const dash = Main.overview._overview._controls.layoutManager._dash;
+ const dashContainer = dash._dashContainer;
+
+ if (dash._showWindowsIcon) {
+ dashContainer.remove_child(dash._showWindowsIcon);
+ dash._showWindowsIconClickedId && dash._showWindowsIcon.toggleButton.disconnect(dash._showWindowsIconClickedId);
+ dash._showWindowsIconClickedId = undefined;
+ dash._showWindowsIcon && dash._showWindowsIcon.destroy();
+ dash._showWindowsIcon = undefined;
+ }
+
+ if (!show || !opt.WINDOW_SEARCH_PROVIDER_ENABLED) return;
+
+ if (!dash._showWindowsIcon) {
+ dash._showWindowsIcon = new ShowWindowsIcon();
+ dash._showWindowsIcon.show(false);
+ dashContainer.add_child(dash._showWindowsIcon);
+ dash._hookUpLabel(dash._showWindowsIcon);
+ }
+
+ dash._showWindowsIcon.icon.setIconSize(opt.MAX_ICON_SIZE);
+ if (opt.SHOW_WINDOWS_ICON === 1) {
+ dashContainer.set_child_at_index(dash._showWindowsIcon, 0);
+ } else if (opt.SHOW_WINDOWS_ICON === 2) {
+ index = dashContainer.get_children().length - 1;
+ dashContainer.set_child_at_index(dash._showWindowsIcon, index);
+ }
+
+ Main.overview._overview._controls.layoutManager._dash._adjustIconSize();
+}
+
+var ShowWindowsIcon = GObject.registerClass(
+class ShowWindowsIcon extends Dash.DashItemContainer {
+ _init() {
+ super._init();
+
+ this._labelText = _('Search Open Windows (Hotkey: Space)');
+ this.toggleButton = new St.Button({
+ style_class: 'show-apps',
+ track_hover: true,
+ can_focus: true,
+ toggle_mode: false,
+ });
+
+ this._iconActor = null;
+ this.icon = new IconGrid.BaseIcon(this.labelText, {
+ setSizeManually: true,
+ showLabel: false,
+ createIcon: this._createIcon.bind(this),
+ });
+ this.icon.y_align = Clutter.ActorAlign.CENTER;
+
+ this.toggleButton.add_actor(this.icon);
+ this.toggleButton._delegate = this;
+
+ this.setChild(this.toggleButton);
+ }
+
+ _createIcon(size) {
+ this._iconActor = new St.Icon({
+ icon_name: 'focus-windows-symbolic',
+ icon_size: size,
+ style_class: 'show-apps-icon',
+ track_hover: true,
+ });
+ return this._iconActor;
+ }
+});
+
+function _updateRecentFilesIcon(show = opt.SHOW_RECENT_FILES_ICON) {
+
+ const dash = Main.overview._overview._controls.layoutManager._dash;
+ const dashContainer = dash._dashContainer;
+
+ if (dash._recentFilesIcon) {
+ dashContainer.remove_child(dash._recentFilesIcon);
+ dash._recentFilesIconClickedId && dash._recentFilesIcon.toggleButton.disconnect(dash._recentFilesIconClickedId);
+ dash._recentFilesIconClickedId = undefined;
+ dash._recentFilesIcon && dash._recentFilesIcon.destroy();
+ dash._recentFilesIcon = undefined;
+ }
+
+ if (!show || !opt.RECENT_FILES_SEARCH_PROVIDER_ENABLED) return;
+
+ if (!dash._recentFilesIcon) {
+ dash._recentFilesIcon = new ShowRecentFilesIcon();
+ dash._recentFilesIcon.show(false);
+ dashContainer.add_child(dash._recentFilesIcon);
+ dash._hookUpLabel(dash._recentFilesIcon);
+ }
+
+ dash._recentFilesIcon.icon.setIconSize(opt.MAX_ICON_SIZE);
+ if (opt.SHOW_RECENT_FILES_ICON === 1) {
+ dashContainer.set_child_at_index(dash._recentFilesIcon, 0);
+ } else if (opt.SHOW_RECENT_FILES_ICON === 2) {
+ index = dashContainer.get_children().length - 1;
+ dashContainer.set_child_at_index(dash._recentFilesIcon, index);
+ }
+
+ Main.overview._overview._controls.layoutManager._dash._adjustIconSize();
+}
+
+var ShowRecentFilesIcon = GObject.registerClass(
+class ShowRecentFilesIcon extends Dash.DashItemContainer {
+ _init() {
+ super._init();
+
+ this._labelText = _('Search Recent Files (Hotkey: Ctrl + Space)');
+ this.toggleButton = new St.Button({
+ style_class: 'show-apps',
+ track_hover: true,
+ can_focus: true,
+ toggle_mode: false,
+ });
+
+ this._iconActor = null;
+ this.icon = new IconGrid.BaseIcon(this.labelText, {
+ setSizeManually: true,
+ showLabel: false,
+ createIcon: this._createIcon.bind(this),
+ });
+ this.icon.y_align = Clutter.ActorAlign.CENTER;
+
+ this.toggleButton.add_actor(this.icon);
+ this.toggleButton._delegate = this;
+
+ this.setChild(this.toggleButton);
+ }
+
+ _createIcon(size) {
+ this._iconActor = new St.Icon({
+ icon_name: 'document-open-recent-symbolic',
+ icon_size: size,
+ style_class: 'show-apps-icon',
+ track_hover: true,
+ });
+ return this._iconActor;
+ }
+});