diff --git a/data/theme/_common.scss b/data/theme/_common.scss index 6775ca7ab..d9b0faee8 100644 --- a/data/theme/_common.scss +++ b/data/theme/_common.scss @@ -492,7 +492,11 @@ StScrollBar { } } - + + .dash-item-container > StWidget { + padding: 4px 8px; + } + .dash-label { //osd tooltip border-radius: 7px; padding: 4px 12px; @@ -561,6 +565,12 @@ StScrollBar { } } + + .app-well-app-running-dot { //running apps indicator + width: 10px; height: 3px; + background-color: $selected_bg_color; + margin-bottom: 2px; //FIXME will happen :) + } %icon_tile { border-radius: 4px; diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index ffda8374c..1cbf491a2 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -1399,6 +1399,9 @@ StScrollBar { width: 24px; height: 24px; } +.dash-item-container > StWidget { + padding: 4px 8px; } + .dash-label { border-radius: 7px; padding: 4px 12px; @@ -1468,6 +1471,12 @@ StScrollBar { border-image: none; background-image: none; } +.app-well-app-running-dot { + width: 10px; + height: 3px; + background-color: #215d9c; + margin-bottom: 2px; } + .search-provider-icon, .list-search-result, .app-well-app > .overview-icon, .show-apps > .overview-icon, diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 5a14aca9e..031f1456a 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -1517,13 +1517,33 @@ const AppIcon = new Lang.Class({ this.id = app.get_id(); this.name = app.get_name(); - this.actor = new St.Button({ style_class: 'app-well-app', + // We need to make it track_hover so dash item can connect to + // the hover signal of the actor in _hookupLabel to call + // shouldShowTooltip when hovered. + this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(), reactive: true, - button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO, - can_focus: true, - x_fill: true, - y_fill: true }); + track_hover: true }); + + this._dot = new St.Widget({ style_class: 'app-well-app-running-dot', + layout_manager: new Clutter.BinLayout(), + x_expand: true, y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END }); + + this._dot.hide(); + + this._button = new St.Button({ style_class: 'app-well-app', + reactive: true, + button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO, + can_focus: true, + x_fill: true, + y_fill: true }); + + this.actor.add_actor(this._button); + this.actor.add_actor(this._dot); + this.actor._delegate = this; + this._button._delegate = this; if (!iconParams) iconParams = {}; @@ -1531,20 +1551,20 @@ const AppIcon = new Lang.Class({ iconParams['createIcon'] = Lang.bind(this, this._createIcon); iconParams['setSizeManually'] = true; this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams); - this.actor.set_child(this.icon.actor); + this._button.set_child(this.icon.actor); this.actor.label_actor = this.icon.label; - 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._button.connect('leave-event', Lang.bind(this, this._onLeaveEvent)); + this._button.connect('button-press-event', Lang.bind(this, this._onButtonPress)); + this._button.connect('touch-event', Lang.bind(this, this._onTouchEvent)); + this._button.connect('clicked', Lang.bind(this, this._onClicked)); + this._button.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu)); this._menu = null; this._menuManager = new PopupMenu.PopupMenuManager(this); - this._draggable = DND.makeDraggable(this.actor); + this._draggable = DND.makeDraggable(this._button); this._draggable.connect('drag-begin', Lang.bind(this, function () { this._removeMenuTimeout(); @@ -1569,6 +1589,18 @@ const AppIcon = new Lang.Class({ this._updateRunningStyle(); }, + // Needed for containers that want to track focus of the widget + // for i.e. scroll the container when navigating through items + getFocusReceiver: function () { + return this._button; + }, + + // Needed for containers that want to change style of the widget + // for i.e. set as selected when searching in shell + getStyleReceiver: function () { + return this._button; + }, + _onDestroy: function() { if (this._stateChangedId > 0) this.app.disconnect(this._stateChangedId); @@ -1588,10 +1620,13 @@ const AppIcon = new Lang.Class({ }, _updateRunningStyle: function() { - if (this.app.state != Shell.AppState.STOPPED) - this.actor.add_style_class_name('running'); - else - this.actor.remove_style_class_name('running'); + if (this.app.state != Shell.AppState.STOPPED) { + this._button.add_style_class_name('running'); + this._dot.show(); + } else { + this._button.remove_style_class_name('running'); + this._dot.hide(); + } }, _setPopupTimeout: function() { @@ -1606,7 +1641,7 @@ const AppIcon = new Lang.Class({ }, _onLeaveEvent: function(actor, event) { - this.actor.fake_release(); + this._button.fake_release(); this._removeMenuTimeout(); }, @@ -1644,7 +1679,7 @@ const AppIcon = new Lang.Class({ popupMenu: function() { this._removeMenuTimeout(); - this.actor.fake_release(); + this._button.fake_release(); this._draggable.fakeRelease(); if (!this._menu) { @@ -1663,7 +1698,7 @@ const AppIcon = new Lang.Class({ this.emit('menu-state-changed', true); - this.actor.set_hover(true); + this._button.set_hover(true); this._menu.popup(); this._menuManager.ignoreRelease(); this.emit('sync-tooltip'); @@ -1680,7 +1715,7 @@ const AppIcon = new Lang.Class({ }, _onMenuPoppedDown: function() { - this.actor.sync_hover(); + this._button.sync_hover(); this.emit('menu-state-changed', false); }, diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js index 875450191..38fe16c41 100644 --- a/js/ui/iconGrid.js +++ b/js/ui/iconGrid.js @@ -278,20 +278,6 @@ const IconGrid = new Lang.Class({ this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this._grid.connect('allocate', Lang.bind(this, this._allocate)); - this._grid.connect('actor-added', Lang.bind(this, this._childAdded)); - this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved)); - }, - - _keyFocusIn: function(actor) { - this.emit('key-focus-in', actor); - }, - - _childAdded: function(grid, child) { - child._iconGridKeyFocusInId = child.connect('key-focus-in', Lang.bind(this, this._keyFocusIn)); - }, - - _childRemoved: function(grid, child) { - child.disconnect(child._iconGridKeyFocusInId); }, _getPreferredWidth: function (grid, forHeight, alloc) { @@ -682,6 +668,10 @@ const IconGrid = new Lang.Class({ this._grid.destroy_all_children(); }, + _keyFocusIn: function(actor) { + this.emit('key-focus-in', actor._associatedItem); + }, + addItem: function(item, index) { if (!item.icon instanceof BaseIcon) throw new Error('Only items with a BaseIcon icon property can be added to IconGrid'); @@ -691,9 +681,26 @@ const IconGrid = new Lang.Class({ this._grid.insert_child_at_index(item.actor, index); else this._grid.add_actor(item.actor); + + // Maybe the item actor acts as a container, so ask the item if + // it has a specific actor to track focus + let focusReceiver = item.actor; + if (item.getFocusReceiver) + focusReceiver = item.getFocusReceiver(); + + focusReceiver._associatedItem = item.actor; + focusReceiver._iconGridKeyFocusInId = focusReceiver.connect('key-focus-in', Lang.bind(this, this._keyFocusIn)); }, removeItem: function(item) { + let focusReceiver = item.actor; + if (item.getFocusReceiver) + focusReceiver = item.getFocusReceiver(); + + + focusReceiver._associatedItem = null; + focusReceiver.disconnect(focusReceiver._iconGridKeyFocusInId); + this._grid.remove_child(item.actor); }, diff --git a/js/ui/search.js b/js/ui/search.js index 2a8e0fd8e..acd8802fd 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -659,11 +659,15 @@ const SearchResults = new Lang.Class({ if (!result) return; + let styleReceiver = result.actor; + if (result.getStyleReceiver) + styleReceiver = result.getStyleReceiver(); + if (selected) { - result.actor.add_style_pseudo_class('selected'); + styleReceiver.add_style_pseudo_class('selected'); Util.ensureActorVisibleInScrollView(this._scrollView, result.actor); } else { - result.actor.remove_style_pseudo_class('selected'); + styleReceiver.remove_style_pseudo_class('selected'); } } });