gnome-shell/js/ui/sidebar.js

265 lines
9.0 KiB
JavaScript

/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener;
const Widget = imports.ui.widget;
const WidgetBox = imports.ui.widgetBox;
const SIDEBAR_SPACING = 4;
const SIDEBAR_PADDING = 4;
// The total sidebar width is the widget width plus the widget padding
// (counted twice for the widget box, and once again for the
// out-of-screen padding), plus the empty space between the border of
// the bar and of the windows
const SIDEBAR_COLLAPSED_WIDTH = Widget.COLLAPSED_WIDTH + 3 * WidgetBox.WIDGETBOX_PADDING + SIDEBAR_PADDING;
const SIDEBAR_EXPANDED_WIDTH = Widget.EXPANDED_WIDTH + 3 * WidgetBox.WIDGETBOX_PADDING + SIDEBAR_PADDING;
function Sidebar() {
this._init();
}
Sidebar.prototype = {
_init : function() {
// The top-left corner of the sidebar is fixed at:
// x = -WidgetBox.WIDGETBOX_PADDING, y = Panel.PANEL_HEIGHT.
// (The negative X is so that we don't see the rounded
// WidgetBox corners on the screen edge side.)
this.actor = new Clutter.Group({ x: -WidgetBox.WIDGETBOX_PADDING,
y: Panel.PANEL_HEIGHT,
width: SIDEBAR_EXPANDED_WIDTH });
// The actual widgets go into a Big.Box inside this.actor. The
// box's width will vary during the expand/collapse animations,
// but this.actor's width will remain constant until we adjust
// it at the end of the animation, because we don't want the
// wm strut to move and cause windows to move multiple times
// during the animation.
this.box = new Big.Box ({ padding_top: SIDEBAR_PADDING,
padding_bottom: SIDEBAR_PADDING,
padding_right: 0,
padding_left: 0,
spacing: SIDEBAR_SPACING });
this.actor.add_actor(this.box);
this._gconf = Shell.GConf.get_default();
this._expanded = this._gconf.get_boolean ("sidebar/expanded");
if (!this._expanded)
this.actor.width = SIDEBAR_COLLAPSED_WIDTH;
this._visible = this._gconf.get_boolean ("sidebar/visible");
if (this._visible)
Main.chrome.addActor(this.actor);
this._hidden = false;
this._hideTimeoutId = 0;
this._widgets = [];
this.addWidget(new ToggleWidget());
let default_widgets = this._gconf.get_string_list("sidebar/widgets");
for (let i = 0; i < default_widgets.length; i++)
this.addWidget(default_widgets[i]);
this._gconf.connect('changed::sidebar/expanded',
Lang.bind(this, this._expandedChanged));
this._gconf.connect('changed::sidebar/visible',
Lang.bind(this, this._visibleChanged));
this._gconf.connect('changed::sidebar/autohide',
Lang.bind(this, this._autohideChanged));
this.actor.connect('enter-event',Lang.bind(this,this._restoreHidden));
this.actor.connect('leave-event',Lang.bind(this,this._startHideTimeout));
this._adjustPosition();
this._autohideChanged();
},
addWidget: function(widget) {
let widgetBox;
try {
widgetBox = new WidgetBox.WidgetBox(widget, this._expanded);
} catch(e) {
logError(e, "Failed to add widget '" + widget + "'");
return;
}
this.box.append(widgetBox.actor, Big.BoxPackFlags.NONE);
this._widgets.push(widgetBox);
this._adjustPosition();
},
_adjustPosition: function() {
let primary=global.get_primary_monitor();
this.actor.y = Math.floor(Math.max(primary.y + Panel.PANEL_HEIGHT,primary.height/2 - this.actor.height/2));
this.actor.x = primary.x;
},
_visibleChanged: function() {
let visible = this._gconf.get_boolean("sidebar/visible");
if (visible == this._visible)
return;
this._visible = visible;
if (visible)
Main.chrome.addActor(this.actor);
else
Main.chrome.removeActor(this.actor);
},
_expandedChanged: function() {
let expanded = this._gconf.get_boolean("sidebar/expanded");
if (expanded == this._expanded)
return;
this._expanded = expanded;
if (expanded)
this._expand();
else
this._collapse();
},
_autohideChanged: function() {
let autohide = this._gconf.get_boolean("sidebar/autohide");
if (autohide == this._autohide)
return;
this._autohide = autohide;
if (autohide) {
this.actor.set_reactive(true);
this._hide();
} else {
this.actor.set_reactive(false);
this._restore();
}
},
_expand: function() {
this._expanded = true;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].expand();
// Updated the strut/stage area after the animation completes
Tweener.addTween(this, { time: WidgetBox.ANIMATION_TIME,
onComplete: function () {
this.actor.width = SIDEBAR_EXPANDED_WIDTH;
} });
},
_collapse: function() {
this._expanded = false;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].collapse();
// Update the strut/stage area after the animation completes
Tweener.addTween(this, { time: WidgetBox.ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.width = SIDEBAR_COLLAPSED_WIDTH;
}) });
},
_hide: function() {
if (!this._expanded) {
this._hidden = true;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].hide();
// Update the strut/stage area after the animation completes
Tweener.addTween(this, { time: WidgetBox.ANIMATION_TIME / 2,
onComplete: Lang.bind(this, function () {
this.actor.width = Math.floor(WidgetBox.WIDGETBOX_PADDING * 2 + SIDEBAR_PADDING);
}) });
}
},
_restore: function() {
if (!this._expanded) {
this._hidden = false;
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].restore();
// Updated the strut/stage area after the animation completes
Tweener.addTween(this, { time: WidgetBox.ANIMATION_TIME / 2,
onComplete: function () {
this.actor.width = SIDEBAR_COLLAPSED_WIDTH;
} });
}
},
_restoreHidden: function(actor, event) {
this._cancelHideTimeout();
if (this._hidden)
this._restore();
return false;
},
_startHideTimeout: function(actor, event) {
if (!this._expanded) {
this._cancelHideTimeout();
this._hideTimeoutId = Mainloop.timeout_add_seconds(2, Lang.bind(this,this._hideTimeoutFunc));
}
return false;
},
_cancelHideTimeout: function() {
if (this._hideTimeoutId != 0) {
Mainloop.source_remove(this._hideTimeoutId);
this._hideTimeoutId = 0;
}
},
_hideTimeoutFunc: function() {
this._hide();
return false;
},
destroy: function() {
this.hide();
for (let i = 0; i < this._widgets.length; i++)
this._widgets[i].destroy();
this.actor.destroy();
}
};
const LEFT_DOUBLE_ARROW = "\u00AB";
const RIGHT_DOUBLE_ARROW = "\u00BB";
function ToggleWidget() {
this._init();
}
ToggleWidget.prototype = {
__proto__ : Widget.Widget.prototype,
_init : function() {
this._gconf = Shell.GConf.get_default();
this.actor = new Clutter.Text({ font_name: "Sans Bold 16px",
text: LEFT_DOUBLE_ARROW,
reactive: true });
this.actor.connect('button-release-event',
Lang.bind(this, this._collapse));
this.collapsedActor = new Clutter.Text({ font_name: "Sans Bold 16px",
text: RIGHT_DOUBLE_ARROW,
reactive: true });
this.collapsedActor.connect('button-release-event',
Lang.bind(this, this._expand));
},
_collapse : function () {
this._gconf.set_boolean ("sidebar/expanded", false);
},
_expand : function () {
this._gconf.set_boolean ("sidebar/expanded", true);
}
};