5c3f9f6999
We had one osdWindow that we displayed either on the primary monitor or on whatever monitor index got passed over dbus. Change that to show the osd on all monitors when no explicit monitor is requested. A monitor should be requested in cases like display brightness where it makes sense to only show the osd on the affected monitor. https://bugzilla.gnome.org/show_bug.cgi?id=722684
276 lines
9.0 KiB
JavaScript
276 lines
9.0 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
const GLib = imports.gi.GLib;
|
|
const St = imports.gi.St;
|
|
|
|
const Lang = imports.lang;
|
|
const Layout = imports.ui.layout;
|
|
const Main = imports.ui.main;
|
|
const Mainloop = imports.mainloop;
|
|
const Tweener = imports.ui.tweener;
|
|
const Meta = imports.gi.Meta;
|
|
|
|
const HIDE_TIMEOUT = 1500;
|
|
const FADE_TIME = 0.1;
|
|
const LEVEL_ANIMATION_TIME = 0.1;
|
|
|
|
const LevelBar = new Lang.Class({
|
|
Name: 'LevelBar',
|
|
|
|
_init: function() {
|
|
this._level = 0;
|
|
|
|
this.actor = new St.Bin({ style_class: 'level',
|
|
x_fill: true, y_fill: true });
|
|
this._bar = new St.DrawingArea();
|
|
this._bar.connect('repaint', Lang.bind(this, this._repaint));
|
|
|
|
this.actor.set_child(this._bar);
|
|
},
|
|
|
|
get level() {
|
|
return this._level;
|
|
},
|
|
|
|
set level(value) {
|
|
let newValue = Math.max(0, Math.min(value, 100));
|
|
if (newValue == this._level)
|
|
return;
|
|
this._level = newValue;
|
|
this._bar.queue_repaint();
|
|
},
|
|
|
|
_repaint: function() {
|
|
let cr = this._bar.get_context();
|
|
|
|
let node = this.actor.get_theme_node();
|
|
let radius = node.get_border_radius(0); // assume same radius for all corners
|
|
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
|
|
|
let [w, h] = this._bar.get_surface_size();
|
|
w *= (this._level / 100.);
|
|
|
|
if (w == 0)
|
|
return;
|
|
|
|
cr.moveTo(radius, 0);
|
|
if (w >= radius)
|
|
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
|
|
else
|
|
cr.lineTo(w, 0);
|
|
if (w >= radius)
|
|
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
|
|
else
|
|
cr.lineTo(w, h);
|
|
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
|
|
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
|
|
cr.fill();
|
|
cr.$dispose();
|
|
}
|
|
});
|
|
|
|
const OsdWindow = new Lang.Class({
|
|
Name: 'OsdWindow',
|
|
|
|
_init: function(monitorIndex) {
|
|
this._popupSize = 0;
|
|
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._box = new St.BoxLayout({ style_class: 'osd-window',
|
|
vertical: true });
|
|
this.actor.add_actor(this._box);
|
|
|
|
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
|
this._box.connect('notify::height', Lang.bind(this,
|
|
function() {
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
|
function() {
|
|
this._box.width = this._box.height;
|
|
}));
|
|
}));
|
|
|
|
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 LevelBar();
|
|
this._box.add(this._level.actor);
|
|
|
|
this._hideTimeoutId = 0;
|
|
this._reset();
|
|
|
|
Main.layoutManager.connect('monitors-changed',
|
|
Lang.bind(this, this._monitorsChanged));
|
|
this._monitorsChanged();
|
|
Main.uiGroup.add_child(this.actor);
|
|
},
|
|
|
|
setIcon: function(icon) {
|
|
this._icon.gicon = icon;
|
|
},
|
|
|
|
setLabel: function(label) {
|
|
this._label.visible = (label != undefined);
|
|
if (label)
|
|
this._label.text = label;
|
|
},
|
|
|
|
setLevel: function(level) {
|
|
this._level.actor.visible = (level != undefined);
|
|
if (level) {
|
|
if (this.actor.visible)
|
|
Tweener.addTween(this._level,
|
|
{ level: level,
|
|
time: LEVEL_ANIMATION_TIME,
|
|
transition: 'easeOutQuad' });
|
|
else
|
|
this._level.level = level;
|
|
}
|
|
},
|
|
|
|
show: function() {
|
|
if (!this._icon.gicon)
|
|
return;
|
|
|
|
if (!this.actor.visible) {
|
|
Meta.disable_unredirect_for_screen(global.screen);
|
|
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,
|
|
transition: 'easeOutQuad' });
|
|
}
|
|
|
|
if (this._hideTimeoutId)
|
|
Mainloop.source_remove(this._hideTimeoutId);
|
|
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
|
|
Lang.bind(this, this._hide));
|
|
GLib.Source.set_name_by_id(this._hideTimeoutId, '[gnome-shell] this._hide');
|
|
},
|
|
|
|
cancel: function() {
|
|
if (!this._hideTimeoutId)
|
|
return;
|
|
|
|
Mainloop.source_remove(this._hideTimeoutId);
|
|
this._hide();
|
|
},
|
|
|
|
_hide: function() {
|
|
this._hideTimeoutId = 0;
|
|
Tweener.addTween(this.actor,
|
|
{ opacity: 0,
|
|
time: FADE_TIME,
|
|
transition: 'easeOutQuad',
|
|
onComplete: Lang.bind(this, function() {
|
|
this._reset();
|
|
Meta.enable_unredirect_for_screen(global.screen);
|
|
})
|
|
});
|
|
return GLib.SOURCE_REMOVE;
|
|
},
|
|
|
|
_reset: function() {
|
|
this.actor.hide();
|
|
this.setLabel(null);
|
|
this.setLevel(null);
|
|
},
|
|
|
|
_monitorsChanged: function() {
|
|
/* assume 110x110 on a 640x480 display and scale from there */
|
|
let monitor = Main.layoutManager.monitors[this._monitorIndex];
|
|
|
|
let scalew = monitor.width / 640.0;
|
|
let scaleh = monitor.height / 480.0;
|
|
let scale = Math.min(scalew, scaleh);
|
|
this._popupSize = 110 * Math.max(1, scale);
|
|
|
|
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
this._icon.icon_size = this._popupSize / (2 * scaleFactor);
|
|
this._box.translation_y = monitor.height / 4;
|
|
this._box.style_changed();
|
|
},
|
|
|
|
_onStyleChanged: function() {
|
|
let themeNode = this._box.get_theme_node();
|
|
let horizontalPadding = themeNode.get_horizontal_padding();
|
|
let verticalPadding = themeNode.get_vertical_padding();
|
|
let topBorder = themeNode.get_border_width(St.Side.TOP);
|
|
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
|
|
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
|
|
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
|
|
|
|
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
|
|
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
|
|
|
|
// minWidth/minHeight here are in real pixels,
|
|
// but the theme takes measures in unscaled dimensions
|
|
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
|
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor);
|
|
}
|
|
});
|
|
|
|
const OsdWindowManager = new Lang.Class({
|
|
Name: 'OsdWindowManager',
|
|
|
|
_init: function() {
|
|
this._osdWindows = [];
|
|
Main.layoutManager.connect('monitors-changed',
|
|
Lang.bind(this, this._monitorsChanged));
|
|
this._monitorsChanged();
|
|
},
|
|
|
|
_monitorsChanged: function() {
|
|
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: function(monitorIndex, icon, label, level) {
|
|
this._osdWindows[monitorIndex].setIcon(icon);
|
|
this._osdWindows[monitorIndex].setLabel(label);
|
|
this._osdWindows[monitorIndex].setLevel(level);
|
|
this._osdWindows[monitorIndex].show();
|
|
},
|
|
|
|
show: function(monitorIndex, icon, label, level) {
|
|
if (monitorIndex != -1) {
|
|
for (let i = 0; i < this._osdWindows.length; i++) {
|
|
if (i == monitorIndex)
|
|
this._showOsdWindow(i, icon, label, level);
|
|
else
|
|
this._osdWindows[i].cancel();
|
|
}
|
|
} else {
|
|
for (let i = 0; i < this._osdWindows.length; i++)
|
|
this._showOsdWindow(i, icon, label, level);
|
|
}
|
|
},
|
|
|
|
hideAll: function() {
|
|
for (let i = 0; i < this._osdWindows.length; i++)
|
|
this._osdWindows[i].cancel();
|
|
}
|
|
});
|