Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for 'com.canonical.Unity.LauncherEntry' APIs #590

Closed
wants to merge 12 commits into from
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -2,7 +2,7 @@

UUID = dash-to-dock@micxgx.gmail.com
BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md
EXTRA_MODULES = convenience.js dash.js docking.js appIcons.js windowPreview.js intellihide.js prefs.js theming.js utils.js Settings.ui
EXTRA_MODULES = convenience.js dash.js docking.js appIcons.js launcherAPI.js windowPreview.js intellihide.js prefs.js theming.js utils.js Settings.ui
EXTRA_MEDIA = logo.svg glossy.svg
TOLOCALIZE = prefs.js appIcons.js
MSGSRC = $(wildcard po/*.po)
Expand Down
232 changes: 231 additions & 1 deletion appIcons.js
Expand Up @@ -5,12 +5,14 @@ const GdkPixbuf = imports.gi.GdkPixbuf
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Signals = imports.signals;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Cairo = imports.cairo;

// Use __ () and N__() for the extension gettext domain, and reuse
// the shell domain with the default _() and N_()
Expand Down Expand Up @@ -99,6 +101,7 @@ var MyAppIcon = new Lang.Class({
this.monitorIndex = monitorIndex;
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._nWindows = 0;
this._remoteEntries = [];

this.parent(app, iconParams);

Expand Down Expand Up @@ -132,6 +135,8 @@ var MyAppIcon = new Lang.Class({
}

this._dots = null;
this._progressOverlayArea = null;
this._progress = 0;

let keys = ['apply-custom-theme',
'custom-theme-running-dots',
Expand All @@ -156,6 +161,7 @@ var MyAppIcon = new Lang.Class({
}));
this._optionalScrollCycleWindows();

this._notificationBadge();
this._numberOverlay();

this._previewMenuManager = null;
Expand Down Expand Up @@ -951,6 +957,169 @@ var MyAppIcon = new Lang.Class({
cr.$dispose();
},

_notificationBadge: function() {
this._notificationBadgeLabel = new St.Label();
this._notificationBadgeBin = new St.Bin({
child: this._notificationBadgeLabel,
x_align: St.Align.END, y_align: St.Align.START,
x_expand: true, y_expand: true
});
this._notificationBadgeLabel.add_style_class_name('notification-badge');
this._notificationBadgeCount = 0;
this._notificationBadgeBin.hide();

this._iconContainer.add_child(this._notificationBadgeBin);
this._iconContainer.connect('allocation-changed', Lang.bind(this, this.updateNotificationBadge));
},

updateNotificationBadge: function() {
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let [minWidth, natWidth] = this._iconContainer.get_preferred_width(-1);
let logicalNatWidth = natWidth / scaleFactor;
let font_size = Math.max(10, Math.round(logicalNatWidth / 5));
let margin_left = Math.round(logicalNatWidth / 4);

this._notificationBadgeLabel.set_style(
'font-size: ' + font_size + 'px;' +
'margin-left: ' + margin_left + 'px;'
);

this._notificationBadgeBin.width = Math.round(logicalNatWidth - margin_left);
this._notificationBadgeLabel.clutter_text.ellipsize = Pango.EllipsizeMode.MIDDLE;
},

_notificationBadgeCountToText: function(count) {
if (count <= 9999) {
return count.toString();
} else if (count < 1e5) {
let thousands = count / 1e3;
return thousands.toFixed(1).toString() + "k";
} else if (count < 1e6) {
let thousands = count / 1e3;
return thousands.toFixed(0).toString() + "k";
} else if (count < 1e8) {
let millions = count / 1e6;
return millions.toFixed(1).toString() + "M";
} else if (count < 1e9) {
let millions = count / 1e6;
return millions.toFixed(0).toString() + "M";
} else {
let billions = count / 1e9;
return billions.toFixed(1).toString() + "B";
}
},

setNotificationBadge: function(count) {
this._notificationBadgeCount = count;
let text = this._notificationBadgeCountToText(count);
this._notificationBadgeLabel.set_text(text);
},

toggleNotificationBadge: function(activate) {
if (activate && this._notificationBadgeCount > 0) {
this.updateNotificationBadge();
this._notificationBadgeBin.show();
}
else
this._notificationBadgeBin.hide();
},

_showProgressOverlay: function() {
if (this._progressOverlayArea) {
this._updateProgressOverlay();
return;
}

this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true});
this._progressOverlayArea.connect('repaint', Lang.bind(this, function() {
this._drawProgressOverlay(this._progressOverlayArea);
}));

this._iconContainer.add_child(this._progressOverlayArea);
this._updateProgressOverlay();
},

_hideProgressOverlay: function() {
if (this._progressOverlayArea)
this._progressOverlayArea.destroy();
this._progressOverlayArea = null;
},

_updateProgressOverlay: function() {
if (this._progressOverlayArea)
this._progressOverlayArea.queue_repaint();
},

_drawProgressOverlay: function(area) {
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let [surfaceWidth, surfaceHeight] = area.get_surface_size();
let cr = area.get_context();

let iconSize = this.icon.iconSize * scaleFactor;

let x = Math.floor((surfaceWidth - iconSize) / 2);
let y = Math.floor((surfaceHeight - iconSize) / 2);

let lineWidth = Math.floor(1.0 * scaleFactor);
let padding = Math.floor(iconSize * 0.05);
let width = iconSize - 2.0*padding;
let height = Math.floor(Math.min(18.0*scaleFactor, 0.20*iconSize));
x += padding;
y += iconSize - height - padding;

cr.setLineWidth(lineWidth);

// Draw the outer stroke
let stroke = new Cairo.LinearGradient(0, y, 0, y + height);
let fill = null;
stroke.addColorStopRGBA(0.5, 0.5, 0.5, 0.5, 0.1);
stroke.addColorStopRGBA(0.9, 0.8, 0.8, 0.8, 0.4);
Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill);

// Draw the background
x += lineWidth;
y += lineWidth;
width -= 2.0*lineWidth;
height -= 2.0*lineWidth;

stroke = Cairo.SolidPattern.createRGBA(0.20, 0.20, 0.20, 0.9);
fill = new Cairo.LinearGradient(0, y, 0, y + height);
fill.addColorStopRGBA(0.4, 0.25, 0.25, 0.25, 1.0);
fill.addColorStopRGBA(0.9, 0.35, 0.35, 0.35, 1.0);
Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill);

// Draw the finished bar
x += lineWidth;
y += lineWidth;
width -= 2.0*lineWidth;
height -= 2.0*lineWidth;

let finishedWidth = Math.ceil(this._progress * width);
stroke = Cairo.SolidPattern.createRGBA(0.8, 0.8, 0.8, 1.0);
fill = Cairo.SolidPattern.createRGBA(0.9, 0.9, 0.9, 1.0);

if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
Utils.drawRoundedLine(cr, x + lineWidth/2.0 + width - finishedWidth, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill);
else
Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill);

cr.$dispose();
},

setProgress: function(progress) {
this._progress = Math.min(Math.max(progress, 0.0), 1.0);
this._updateProgressOverlay();
},

toggleProgressOverlay: function(activate) {
if (activate) {
this._showProgressOverlay();
}
else {
this._hideProgressOverlay();
}
},

_numberOverlay: function() {
// Add label for a Hot-Key visual aid
this._numberOverlayLabel = new St.Label();
Expand Down Expand Up @@ -1102,7 +1271,68 @@ var MyAppIcon = new Lang.Class({
// nautilus desktop window.
getInterestingWindows: function() {
return getInterestingWindows(this.app, this._dtdSettings, this.monitorIndex);
}
},

insertEntryRemote: function(remote) {
if (!remote || this._remoteEntries.includes(remote))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azzar1 I used your new script (thanks!), and got this running on Gnome 3.22.

This line is throwing an error for me, since Debian is still on mozJS 24.

Perhaps we can change includes (which is not supported until mozJS 52) with:

if (!remote || this._remoteEntries.indexOf(remote) !== -1)

return;

this._remoteEntries.push(remote);
this._selectEntryRemote(remote);
},

removeEntryRemote: function(remote) {
if (!remote || !this._remoteEntries.includes(remote))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. includes -> indexOf

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

return;

this._remoteEntries.splice(this._remoteEntries.indexOf(remote), 1);

if (this._remoteEntries.length > 0) {
this._selectEntryRemote(this._remoteEntries[this._remoteEntries.length-1]);
} else {
this.setNotificationBadge(0);
this.toggleNotificationBadge(false);
this.setProgress(0);
this.toggleProgressOverlay(false);
}
},

_selectEntryRemote: function(remote) {
if (!remote)
return;

this._signalsHandler.removeWithLabel('entry-remotes');

this._signalsHandler.addWithLabel('entry-remotes', [
remote,
'count-changed',
Lang.bind(this, (remote, value) => {
this.setNotificationBadge(value);
})
], [
remote,
'count-visible-changed',
Lang.bind(this, (remote, value) => {
this.toggleNotificationBadge(value);
})
], [remote,
'progress-changed',
Lang.bind(this, (remote, value) => {
this.setProgress(value);
})
], [
remote,
'progress-visible-changed',
Lang.bind(this, (remote, value) => {
this.toggleProgressOverlay(value);
})
]);

this.setNotificationBadge(remote.count());
this.toggleNotificationBadge(remote.countVisible());
this.setProgress(remote.progress());
this.toggleProgressOverlay(remote.progressVisible());
},
});
/**
* Extend AppIconMenu
Expand Down
15 changes: 10 additions & 5 deletions dash.js
Expand Up @@ -178,13 +178,14 @@ const baseIconSizes = [16, 22, 24, 32, 48, 64, 96, 128];
var MyDash = new Lang.Class({
Name: 'DashToDock.MyDash',

_init: function(settings, monitorIndex) {
_init: function(settings, remoteModel, monitorIndex) {
this._maxHeight = -1;
this.iconSize = 64;
this._availableIconSizes = baseIconSizes;
this._shownInitially = false;

this._dtdSettings = settings;
this._remoteModel = remoteModel;
this._monitorIndex = monitorIndex;
this._position = Utils.getPosition(settings);
this._isHorizontal = ((this._position == St.Side.TOP) ||
Expand Down Expand Up @@ -453,6 +454,10 @@ var MyDash = new Lang.Class({
let appIcon = new AppIcons.MyAppIcon(this._dtdSettings, app, this._monitorIndex,
{ setSizeManually: true,
showLabel: false });
this._remoteModel.lookupById(app.id).forEach(function(entry) {
appIcon.insertEntryRemote(entry);
});

if (appIcon._draggable) {
appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
appIcon.actor.opacity = 50;
Expand Down Expand Up @@ -516,7 +521,7 @@ var MyDash = new Lang.Class({
/**
* Return an array with the "proper" appIcons currently in the dash
*/
_getAppIcons: function() {
getAppIcons: function() {
// Only consider children which are "proper"
// icons (i.e. ignoring drag placeholders) and which are not
// animating out (which means they will be destroyed at the end of
Expand All @@ -536,7 +541,7 @@ var MyDash = new Lang.Class({
},

_updateAppsIconGeometry: function() {
let appIcons = this._getAppIcons();
let appIcons = this.getAppIcons();
appIcons.forEach(function(icon) {
icon.updateIconGeometry();
});
Expand Down Expand Up @@ -870,7 +875,7 @@ var MyDash = new Lang.Class({
},

_updateNumberOverlay: function() {
let appIcons = this._getAppIcons();
let appIcons = this.getAppIcons();
let counter = 1;
appIcons.forEach(function(icon) {
if (counter < 10){
Expand All @@ -891,7 +896,7 @@ var MyDash = new Lang.Class({
},

toggleNumberOverlay: function(activate) {
let appIcons = this._getAppIcons();
let appIcons = this.getAppIcons();
appIcons.forEach(function(icon) {
icon.toggleNumberOverlay(activate);
});
Expand Down