874cf0ba15
Commit 5a8923ef95a2 removed support for legacy status icons from the notification system, as we no longer want them to appear as notifications. As we are unfortunately not quite at a point where we can remove all support for them for good, so we now need an alternative place to put them. Add a small dedicated tray at the bottom which appears when any legacy status icons are active. By default it is almost completely hidden to not interfere with the user's windows, but can be expanded on demand to interact with the icons. https://bugzilla.gnome.org/show_bug.cgi?id=745162
179 lines
7.2 KiB
JavaScript
179 lines
7.2 KiB
JavaScript
const Clutter = imports.gi.Clutter;
|
|
const GObject = imports.gi.GObject;
|
|
const Shell = imports.gi.Shell;
|
|
const St = imports.gi.St;
|
|
|
|
const Lang = imports.lang;
|
|
const Layout = imports.ui.layout;
|
|
const Main = imports.ui.main;
|
|
const Overview = imports.ui.overview;
|
|
const OverviewControls = imports.ui.overviewControls;
|
|
const Tweener = imports.ui.tweener;
|
|
|
|
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
|
'bluetooth-applet': 'bluetooth',
|
|
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
|
|
// when moved to control center
|
|
'gnome-sound-applet': 'volume',
|
|
'nm-applet': 'network',
|
|
'gnome-power-manager': 'battery',
|
|
'keyboard': 'keyboard',
|
|
'a11y-keyboard': 'a11y',
|
|
'kbd-scrolllock': 'keyboard',
|
|
'kbd-numlock': 'keyboard',
|
|
'kbd-capslock': 'keyboard',
|
|
'ibus-ui-gtk': 'keyboard'
|
|
};
|
|
|
|
// Offset of the original position from the bottom-right corner
|
|
const CONCEALED_VISIBLE_FRACTION = 0.2;
|
|
const REVEAL_ANIMATION_TIME = 0.2;
|
|
|
|
const LegacyTray = new Lang.Class({
|
|
Name: 'LegacyTray',
|
|
|
|
_init: function() {
|
|
this.actor = new St.Widget({ clip_to_allocation: true,
|
|
layout_manager: new Clutter.BinLayout() });
|
|
let constraint = new Layout.MonitorConstraint({ primary: true,
|
|
work_area: true });
|
|
this.actor.add_constraint(constraint);
|
|
|
|
this._slideLayout = new OverviewControls.SlideLayout();
|
|
this._slideLayout.translationX = 0;
|
|
this._slideLayout.slideDirection = OverviewControls.SlideDirection.LEFT;
|
|
|
|
this._slider = new St.Widget({ style_class: 'legacy-tray',
|
|
x_expand: true, y_expand: true,
|
|
x_align: Clutter.ActorAlign.START,
|
|
y_align: Clutter.ActorAlign.END,
|
|
layout_manager: this._slideLayout });
|
|
this.actor.add_actor(this._slider);
|
|
|
|
this._box = new St.BoxLayout();
|
|
this._slider.add_actor(this._box);
|
|
|
|
this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle' });
|
|
this._concealHandle.child = new St.Icon({ icon_name: 'go-previous-symbolic' });
|
|
this._box.add_child(this._concealHandle);
|
|
|
|
this._iconBox = new St.BoxLayout({ style_class: 'legacy-tray-icon-box' });
|
|
this._box.add_actor(this._iconBox);
|
|
|
|
this._revealHandle = new St.Button({ style_class: 'legacy-tray-handle' });
|
|
this._revealHandle.child = new St.Icon({ icon_name: 'go-next-symbolic' });
|
|
this._box.add_child(this._revealHandle);
|
|
|
|
this._revealHandle.bind_property('visible',
|
|
this._concealHandle, 'visible',
|
|
GObject.BindingFlags.BIDIRECTIONAL |
|
|
GObject.BindingFlags.INVERT_BOOLEAN);
|
|
this._revealHandle.connect('notify::visible',
|
|
Lang.bind(this, this._sync));
|
|
this._revealHandle.connect('notify::hover',
|
|
Lang.bind(this ,this._sync));
|
|
this._revealHandle.connect('clicked', Lang.bind(this,
|
|
function() {
|
|
this._concealHandle.show();
|
|
}));
|
|
this._concealHandle.connect('clicked', Lang.bind(this,
|
|
function() {
|
|
this._revealHandle.show();
|
|
}));
|
|
|
|
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
|
|
Main.layoutManager.trackChrome(this._slider, { affectsInputRegion: true });
|
|
|
|
this._trayManager = new Shell.TrayManager();
|
|
this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
|
this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
|
this._trayManager.manage_screen(global.screen, this.actor);
|
|
|
|
Main.overview.connect('showing', Lang.bind(this,
|
|
function() {
|
|
Tweener.removeTweens(this._slider);
|
|
Tweener.addTween(this._slider, { opacity: 0,
|
|
time: Overview.ANIMATION_TIME,
|
|
transition: 'easeOutQuad' });
|
|
}));
|
|
Main.overview.connect('shown', Lang.bind(this, this._sync));
|
|
Main.overview.connect('hiding', Lang.bind(this,
|
|
function() {
|
|
this._sync();
|
|
Tweener.removeTweens(this._slider);
|
|
Tweener.addTween(this._slider, { opacity: 255,
|
|
time: Overview.ANIMATION_TIME,
|
|
transition: 'easeOutQuad' });
|
|
}));
|
|
|
|
Main.layoutManager.connect('monitors-changed',
|
|
Lang.bind(this, this._sync));
|
|
global.screen.connect('in-fullscreen-changed',
|
|
Lang.bind(this, this._sync));
|
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
|
|
|
|
this._sync();
|
|
},
|
|
|
|
_onTrayIconAdded: function(tm, icon) {
|
|
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
|
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
|
|
return;
|
|
|
|
let button = new St.Button({ child: icon,
|
|
button_mask: St.ButtonMask.ONE |
|
|
St.ButtonMask.TWO |
|
|
St.ButtonMask.THREE,
|
|
x_fill: true, y_fill: true });
|
|
button.connect('clicked',
|
|
function() {
|
|
icon.click(Clutter.get_current_event());
|
|
});
|
|
|
|
this._iconBox.add_actor(button);
|
|
this._sync();
|
|
},
|
|
|
|
_onTrayIconRemoved: function(tm, icon) {
|
|
if (!this.actor.contains(icon))
|
|
return;
|
|
|
|
icon.get_parent().destroy();
|
|
this._sync();
|
|
},
|
|
|
|
_sync: function() {
|
|
// FIXME: we no longer treat tray icons as notifications
|
|
let allowed = Main.sessionMode.hasNotifications;
|
|
let hasIcons = this._iconBox.get_n_children() > 0;
|
|
let inOverview = Main.overview.visible && !Main.overview.animationInProgress;
|
|
let inFullscreen = Main.layoutManager.primaryMonitor.inFullscreen;
|
|
this.actor.visible = allowed && hasIcons && !inOverview && !inFullscreen;
|
|
|
|
if (!hasIcons)
|
|
this._concealHandle.hide();
|
|
|
|
let targetSlide;
|
|
if (this._concealHandle.visible) {
|
|
targetSlide = 1.0;
|
|
} else if (!hasIcons) {
|
|
targetSlide = 0.0;
|
|
} else {
|
|
let [, boxWidth] = this._box.get_preferred_width(-1);
|
|
let [, handleWidth] = this._revealHandle.get_preferred_width(-1);
|
|
|
|
targetSlide = handleWidth / boxWidth;
|
|
if (!this._revealHandle.hover)
|
|
targetSlide *= CONCEALED_VISIBLE_FRACTION;
|
|
}
|
|
|
|
if (this.actor.visible)
|
|
Tweener.addTween(this._slideLayout,
|
|
{ slideX: targetSlide,
|
|
time: REVEAL_ANIMATION_TIME,
|
|
transition: 'easeOutQuad' });
|
|
else
|
|
this._slideLayout.slideX = targetSlide;
|
|
}
|
|
});
|