476816732f
The different units - seconds for Tweener and milliseconds for timeouts - are not a big issue currently, as there is little overlap. However this will change when we start using Clutter's own animation framework (which uses milliseconds as well), in particular where constants are shared between modules. In order to prepare for the transition, define all animation times as milliseconds and adjust them when passing them to Tweener. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/663
245 lines
7.9 KiB
JavaScript
245 lines
7.9 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/* exported OsdWindowManager */
|
|
|
|
const { Clutter, GLib, GObject, Meta, St } = imports.gi;
|
|
const Mainloop = imports.mainloop;
|
|
|
|
const BarLevel = imports.ui.barLevel;
|
|
const Layout = imports.ui.layout;
|
|
const Main = imports.ui.main;
|
|
const Tweener = imports.ui.tweener;
|
|
|
|
var HIDE_TIMEOUT = 1500;
|
|
var FADE_TIME = 100;
|
|
var LEVEL_ANIMATION_TIME = 100;
|
|
|
|
var OsdWindowConstraint = GObject.registerClass(
|
|
class OsdWindowConstraint extends Clutter.Constraint {
|
|
_init(props) {
|
|
this._minSize = 0;
|
|
super._init(props);
|
|
}
|
|
|
|
set minSize(v) {
|
|
this._minSize = v;
|
|
if (this.actor)
|
|
this.actor.queue_relayout();
|
|
}
|
|
|
|
vfunc_update_allocation(actor, actorBox) {
|
|
// Clutter will adjust the allocation for margins,
|
|
// so add it to our minimum size
|
|
let minSize = this._minSize + actor.margin_top + actor.margin_bottom;
|
|
let [width, height] = actorBox.get_size();
|
|
|
|
// Enforce a ratio of 1
|
|
let size = Math.ceil(Math.max(minSize, height));
|
|
actorBox.set_size(size, size);
|
|
|
|
// Recenter
|
|
let [x, y] = actorBox.get_origin();
|
|
actorBox.set_origin(Math.ceil(x + width / 2 - size / 2),
|
|
Math.ceil(y + height / 2 - size / 2));
|
|
}
|
|
});
|
|
|
|
var OsdWindow = class {
|
|
constructor(monitorIndex) {
|
|
this.actor = new St.Widget({ x_expand: true,
|
|
y_expand: true,
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
y_align: Clutter.ActorAlign.CENTER });
|
|
|
|
this._monitorIndex = monitorIndex;
|
|
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
|
|
this.actor.add_constraint(constraint);
|
|
|
|
this._boxConstraint = new OsdWindowConstraint();
|
|
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
|
vertical: true });
|
|
this._box.add_constraint(this._boxConstraint);
|
|
this.actor.add_actor(this._box);
|
|
|
|
this._icon = new St.Icon();
|
|
this._box.add(this._icon, { expand: true });
|
|
|
|
this._label = new St.Label();
|
|
this._box.add(this._label);
|
|
|
|
this._level = new BarLevel.BarLevel(0, { styleClass: 'level' });
|
|
this._box.add(this._level.actor);
|
|
|
|
this._hideTimeoutId = 0;
|
|
this._reset();
|
|
|
|
this.actor.connect('destroy', this._onDestroy.bind(this));
|
|
|
|
this._monitorsChangedId =
|
|
Main.layoutManager.connect('monitors-changed',
|
|
this._relayout.bind(this));
|
|
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
this._scaleChangedId =
|
|
themeContext.connect('notify::scale-factor',
|
|
this._relayout.bind(this));
|
|
this._relayout();
|
|
Main.uiGroup.add_child(this.actor);
|
|
}
|
|
|
|
_onDestroy() {
|
|
if (this._monitorsChangedId)
|
|
Main.layoutManager.disconnect(this._monitorsChangedId);
|
|
this._monitorsChangedId = 0;
|
|
|
|
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
if (this._scaleChangedId)
|
|
themeContext.disconnect(this._scaleChangedId);
|
|
this._scaleChangedId = 0;
|
|
}
|
|
|
|
setIcon(icon) {
|
|
this._icon.gicon = icon;
|
|
}
|
|
|
|
setLabel(label) {
|
|
this._label.visible = (label != undefined);
|
|
if (label)
|
|
this._label.text = label;
|
|
}
|
|
|
|
setLevel(value) {
|
|
this._level.actor.visible = (value != undefined);
|
|
if (value != undefined) {
|
|
if (this.actor.visible)
|
|
Tweener.addTween(this._level,
|
|
{ value: value,
|
|
time: LEVEL_ANIMATION_TIME / 1000,
|
|
transition: 'easeOutQuad' });
|
|
else
|
|
this._level.value = value;
|
|
}
|
|
}
|
|
|
|
setMaxLevel(maxLevel = 1) {
|
|
this._level.maximum_value = maxLevel;
|
|
}
|
|
|
|
show() {
|
|
if (!this._icon.gicon)
|
|
return;
|
|
|
|
if (!this.actor.visible) {
|
|
Meta.disable_unredirect_for_display(global.display);
|
|
this.actor.show();
|
|
this.actor.opacity = 0;
|
|
this.actor.get_parent().set_child_above_sibling(this.actor, null);
|
|
|
|
Tweener.addTween(this.actor,
|
|
{ opacity: 255,
|
|
time: FADE_TIME / 1000,
|
|
transition: 'easeOutQuad' });
|
|
}
|
|
|
|
if (this._hideTimeoutId)
|
|
Mainloop.source_remove(this._hideTimeoutId);
|
|
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
|
|
this._hide.bind(this));
|
|
GLib.Source.set_name_by_id(this._hideTimeoutId, '[gnome-shell] this._hide');
|
|
}
|
|
|
|
cancel() {
|
|
if (!this._hideTimeoutId)
|
|
return;
|
|
|
|
Mainloop.source_remove(this._hideTimeoutId);
|
|
this._hide();
|
|
}
|
|
|
|
_hide() {
|
|
this._hideTimeoutId = 0;
|
|
Tweener.addTween(this.actor,
|
|
{ opacity: 0,
|
|
time: FADE_TIME / 1000,
|
|
transition: 'easeOutQuad',
|
|
onComplete: () => {
|
|
this._reset();
|
|
Meta.enable_unredirect_for_display(global.display);
|
|
}
|
|
});
|
|
return GLib.SOURCE_REMOVE;
|
|
}
|
|
|
|
_reset() {
|
|
this.actor.hide();
|
|
this.setLabel(null);
|
|
this.setMaxLevel(null);
|
|
this.setLevel(null);
|
|
}
|
|
|
|
_relayout() {
|
|
/* assume 110x110 on a 640x480 display and scale from there */
|
|
let monitor = Main.layoutManager.monitors[this._monitorIndex];
|
|
if (!monitor)
|
|
return; // we are about to be removed
|
|
|
|
let scalew = monitor.width / 640.0;
|
|
let scaleh = monitor.height / 480.0;
|
|
let scale = Math.min(scalew, scaleh);
|
|
let popupSize = 110 * Math.max(1, scale);
|
|
|
|
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
this._icon.icon_size = popupSize / (2 * scaleFactor);
|
|
this._box.translation_y = Math.round(monitor.height / 4);
|
|
this._boxConstraint.minSize = popupSize;
|
|
}
|
|
};
|
|
|
|
var OsdWindowManager = class {
|
|
constructor() {
|
|
this._osdWindows = [];
|
|
Main.layoutManager.connect('monitors-changed',
|
|
this._monitorsChanged.bind(this));
|
|
this._monitorsChanged();
|
|
}
|
|
|
|
_monitorsChanged() {
|
|
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
|
|
if (this._osdWindows[i] == undefined)
|
|
this._osdWindows[i] = new OsdWindow(i);
|
|
}
|
|
|
|
for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
|
|
this._osdWindows[i].actor.destroy();
|
|
this._osdWindows[i] = null;
|
|
}
|
|
|
|
this._osdWindows.length = Main.layoutManager.monitors.length;
|
|
}
|
|
|
|
_showOsdWindow(monitorIndex, icon, label, level, maxLevel) {
|
|
this._osdWindows[monitorIndex].setIcon(icon);
|
|
this._osdWindows[monitorIndex].setLabel(label);
|
|
this._osdWindows[monitorIndex].setMaxLevel(maxLevel);
|
|
this._osdWindows[monitorIndex].setLevel(level);
|
|
this._osdWindows[monitorIndex].show();
|
|
}
|
|
|
|
show(monitorIndex, icon, label, level, maxLevel) {
|
|
if (monitorIndex != -1) {
|
|
for (let i = 0; i < this._osdWindows.length; i++) {
|
|
if (i == monitorIndex)
|
|
this._showOsdWindow(i, icon, label, level, maxLevel);
|
|
else
|
|
this._osdWindows[i].cancel();
|
|
}
|
|
} else {
|
|
for (let i = 0; i < this._osdWindows.length; i++)
|
|
this._showOsdWindow(i, icon, label, level, maxLevel);
|
|
}
|
|
}
|
|
|
|
hideAll() {
|
|
for (let i = 0; i < this._osdWindows.length; i++)
|
|
this._osdWindows[i].cancel();
|
|
}
|
|
};
|