mirror of
https://github.com/moparisthebest/FireTray
synced 2025-01-09 04:28:16 -05:00
implement experimental cross-fading for blinking chat icon
This commit is contained in:
parent
4360e3f499
commit
208db6f906
@ -84,27 +84,28 @@ firetray.Chat = {
|
||||
log.debug('startGetAttentionMaybe');
|
||||
let convIsCurrentlyShown = this.isConvCurrentlyShown(conv);
|
||||
log.debug("convIsCurrentlyShown="+convIsCurrentlyShown);
|
||||
if (!convIsCurrentlyShown) { // don't blink when conv tab already on top
|
||||
this.acknowledgeOnFocus.must = true;
|
||||
this.acknowledgeOnFocus.conv = conv;
|
||||
if (convIsCurrentlyShown) // don't blink when conv tab already on top
|
||||
return;
|
||||
|
||||
/* there can potentially be multiple windows, each with a Chat tab and
|
||||
the same conv open... so we need to handle all windows */
|
||||
for (let xid in firetray.Handler.windows) {
|
||||
let win = firetray.Handler.windows[xid].chromeWin;
|
||||
let contactlist = win.document.getElementById("contactlistbox");
|
||||
for (let i=0; i<contactlist.itemCount; ++i) {
|
||||
let item = contactlist.getItemAtIndex(i);
|
||||
if (item.localName !== 'imconv')
|
||||
continue;
|
||||
if (item.hasOwnProperty('conv') && item.conv.target === conv) {
|
||||
firetray.ChatStatusIcon.setUrgency(xid, true);
|
||||
}
|
||||
this.acknowledgeOnFocus.must = true;
|
||||
this.acknowledgeOnFocus.conv = conv;
|
||||
|
||||
/* there can potentially be multiple windows, each with a Chat tab and
|
||||
the same conv open... so we need to handle all windows */
|
||||
for (let xid in firetray.Handler.windows) {
|
||||
let win = firetray.Handler.windows[xid].chromeWin;
|
||||
let contactlist = win.document.getElementById("contactlistbox");
|
||||
for (let i=0; i<contactlist.itemCount; ++i) {
|
||||
let item = contactlist.getItemAtIndex(i);
|
||||
if (item.localName !== 'imconv')
|
||||
continue;
|
||||
if (item.hasOwnProperty('conv') && item.conv.target === conv) {
|
||||
firetray.ChatStatusIcon.setUrgency(xid, true);
|
||||
}
|
||||
}
|
||||
|
||||
firetray.ChatStatusIcon.startIconBlinking();
|
||||
}
|
||||
|
||||
firetray.ChatStatusIcon.startIconBlinking();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -140,6 +140,8 @@ function gdk_defines(lib) {
|
||||
this.GDK_SUBSTRUCTURE_MASK = 1 << 20,
|
||||
this.GDK_SCROLL_MASK = 1 << 21,
|
||||
this.GDK_ALL_EVENTS_MASK = 0x3FFFFE
|
||||
this.GdkColorspace = ctypes.int; // enum
|
||||
this.GDK_COLORSPACE_RGB = 0;
|
||||
|
||||
this.GdkWindow = ctypes.StructType("GdkWindow");
|
||||
this.GdkByteOrder = ctypes.int; // enum
|
||||
@ -262,9 +264,21 @@ function gdk_defines(lib) {
|
||||
lib.lazy_bind("gdk_screen_get_toplevel_windows", gobject.GList.ptr, this.GdkScreen.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_new_from_file", this.GdkPixbuf.ptr, gobject.gchar.ptr, glib.GError.ptr.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_copy", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_composite", ctypes.void_t, this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.double, ctypes.double, ctypes.double, ctypes.double, ctypes.int, ctypes.int);
|
||||
lib.lazy_bind("gdk_pixbuf_get_has_alpha", gobject.gboolean, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_add_alpha", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, gobject.gboolean, gobject.guchar, gobject.guchar, gobject.guchar);
|
||||
|
||||
lib.lazy_bind("gdk_pixbuf_get_colorspace", this.GdkColorspace, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_n_channels", ctypes.int, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_has_alpha", gobject.gboolean, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_bits_per_sample", ctypes.int, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_pixels", gobject.guchar.ptr, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_width", ctypes.int, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_height", ctypes.int, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_composite", ctypes.void_t, this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.double, ctypes.double, ctypes.double, ctypes.double, ctypes.int, ctypes.int);
|
||||
lib.lazy_bind("gdk_pixbuf_get_rowstride", ctypes.int, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_get_byte_length", gobject.gsize, this.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gdk_pixbuf_copy", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr);
|
||||
|
||||
lib.lazy_bind("gdk_screen_get_system_colormap", this.GdkColormap.ptr, this.GdkScreen.ptr);
|
||||
lib.lazy_bind("gdk_colormap_get_visual", this.GdkVisual.ptr, this.GdkColormap.ptr);
|
||||
lib.lazy_bind("gdk_color_parse", gobject.gboolean, gobject.gchar.ptr, this.GdkColor.ptr);
|
||||
|
@ -11,6 +11,7 @@ const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
|
||||
Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
|
||||
|
||||
function gio_defines(lib) {
|
||||
this.GIcon = ctypes.StructType("GIcon");
|
||||
@ -18,6 +19,7 @@ function gio_defines(lib) {
|
||||
|
||||
lib.lazy_bind("g_themed_icon_new", this.GIcon.ptr, ctypes.char.ptr);
|
||||
lib.lazy_bind("g_themed_icon_new_from_names", this.GIcon.ptr, ctypes.char.ptr.ptr, ctypes.int);
|
||||
lib.lazy_bind("g_themed_icon_get_names", gobject.gchar.ptr.ptr, this.GThemedIcon.ptr);
|
||||
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,15 @@ function gtk_defines(lib) {
|
||||
this.FIRETRAY_REQUIRED_GTK_MINOR_VERSION = 20;
|
||||
this.FIRETRAY_REQUIRED_GTK_MICRO_VERSION = 0;
|
||||
|
||||
this.GTK_ICON_SIZE_MENU = 1; // enum GtkIconSize
|
||||
this.GtkIconSize = ctypes.int; // enum
|
||||
this.GTK_ICON_SIZE_INVALID = 0;
|
||||
this.GTK_ICON_SIZE_MENU = 1;
|
||||
this.GTK_ICON_SIZE_SMALL_TOOLBAR = 2;
|
||||
this.GTK_ICON_SIZE_LARGE_TOOLBAR = 3;
|
||||
this.GTK_ICON_SIZE_BUTTON = 4;
|
||||
this.GTK_ICON_SIZE_DND = 5;
|
||||
this.GTK_ICON_SIZE_DIALOG = 6;
|
||||
|
||||
this.GTK_WINDOW_TOPLEVEL = 0; // enum GtkWindowType
|
||||
|
||||
this.GtkStatusIcon = ctypes.StructType("GtkStatusIcon");
|
||||
@ -56,6 +64,13 @@ function gtk_defines(lib) {
|
||||
this.GtkWindow = ctypes.StructType("GtkWindow");
|
||||
this.GtkWindowType = ctypes.int; // enum
|
||||
this.GtkSeparatorMenuItem = ctypes.StructType("GtkSeparatorMenuItem");
|
||||
this.GtkIconInfo = ctypes.StructType("GtkIconInfo");
|
||||
this.GtkIconLookupFlags = ctypes.int; // enum
|
||||
this.GTK_ICON_LOOKUP_NO_SVG = 1 << 0;
|
||||
this.GTK_ICON_LOOKUP_FORCE_SVG = 1 << 1;
|
||||
this.GTK_ICON_LOOKUP_USE_BUILTIN = 1 << 2;
|
||||
this.GTK_ICON_LOOKUP_GENERIC_FALLBACK = 1 << 3;
|
||||
this.GTK_ICON_LOOKUP_FORCE_SIZE = 1 << 4;
|
||||
|
||||
this.GtkMenuPositionFunc_t = ctypes.FunctionType(
|
||||
ctypes.default_abi, ctypes.void_t,
|
||||
@ -88,6 +103,9 @@ function gtk_defines(lib) {
|
||||
lib.lazy_bind("gtk_icon_theme_get_for_screen", this.GtkIconTheme.ptr, gdk.GdkScreen.ptr);
|
||||
lib.lazy_bind("gtk_icon_theme_append_search_path", ctypes.void_t, this.GtkIconTheme.ptr, gobject.gchar.ptr);
|
||||
lib.lazy_bind("gtk_icon_theme_prepend_search_path", ctypes.void_t, this.GtkIconTheme.ptr, gobject.gchar.ptr);
|
||||
lib.lazy_bind("gtk_icon_theme_choose_icon", this.GtkIconInfo.ptr, this.GtkIconTheme.ptr, gobject.gchar.ptr.array(), gobject.gint, this.GtkIconLookupFlags);
|
||||
lib.lazy_bind("gtk_icon_info_load_icon", gdk.GdkPixbuf.ptr, this.GtkIconInfo.ptr, glib.GError.ptr.ptr);
|
||||
lib.lazy_bind("gtk_icon_info_free", ctypes.void_t, this.GtkIconInfo.ptr);
|
||||
|
||||
lib.lazy_bind("gtk_status_icon_new", this.GtkStatusIcon.ptr);
|
||||
lib.lazy_bind("gtk_status_icon_set_from_file", ctypes.void_t, this.GtkStatusIcon.ptr, ctypes.char.ptr);
|
||||
@ -112,6 +130,9 @@ function gtk_defines(lib) {
|
||||
lib.lazy_bind("gtk_widget_create_pango_layout", pango.PangoLayout.ptr, this.GtkWidget.ptr, gobject.gchar.ptr);
|
||||
lib.lazy_bind("gtk_widget_destroy", ctypes.void_t, this.GtkWidget.ptr);
|
||||
lib.lazy_bind("gtk_status_icon_set_from_pixbuf", ctypes.void_t, this.GtkStatusIcon.ptr, gdk.GdkPixbuf.ptr);
|
||||
lib.lazy_bind("gtk_status_icon_get_pixbuf", gdk.GdkPixbuf.ptr, this.GtkStatusIcon.ptr);
|
||||
lib.lazy_bind("gtk_status_icon_get_gicon", gio.GIcon.ptr, this.GtkStatusIcon.ptr);
|
||||
lib.lazy_bind("gtk_status_icon_get_storage_type", ctypes.int, this.GtkStatusIcon.ptr); // TEST
|
||||
lib.lazy_bind("gtk_window_list_toplevels", gobject.GList.ptr);
|
||||
lib.lazy_bind("gtk_window_get_title", gobject.gchar.ptr, this.GtkWindow.ptr);
|
||||
lib.lazy_bind("gtk_window_is_active", gobject.gboolean, this.GtkWindow.ptr);
|
||||
|
@ -16,6 +16,7 @@ Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
|
||||
Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
|
||||
Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
|
||||
Cu.import("resource://firetray/commons.js");
|
||||
Cu.import("resource://firetray/promise.js");
|
||||
firetray.Handler.subscribeLibsForClosing([gobject, gio, gtk]);
|
||||
|
||||
if ("undefined" == typeof(firetray.Handler))
|
||||
@ -88,6 +89,95 @@ firetray.ChatStatusIcon = {
|
||||
gtk.gtk_status_icon_set_from_pixbuf(this.trayIcon, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL fancy blinking.
|
||||
* TODO: how to wait for last fade in to restore themedIconNameCurrent
|
||||
*/
|
||||
crossFade: function() {
|
||||
|
||||
/* borrowed from mozmill utils.js*/
|
||||
function sleep(milliseconds) {
|
||||
var timeup = false;
|
||||
function wait() { timeup = true; }
|
||||
let timer = Components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(Components.interfaces.nsITimer);
|
||||
timer.initWithCallback(wait, milliseconds, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
var thread = Components.classes["@mozilla.org/thread-manager;1"].
|
||||
getService().currentThread;
|
||||
while(!timeup) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
let icon_theme = gtk.gtk_icon_theme_get_for_screen(gdk.gdk_screen_get_default());
|
||||
firetray.ChatStatusIcon.timers['cross-fade'] = firetray.Utils.timer(
|
||||
500, Ci.nsITimer.TYPE_REPEATING_SLACK, function() {
|
||||
|
||||
// get pixbuf
|
||||
let arry = gobject.gchar.ptr.array()(2);
|
||||
arry[0] = gobject.gchar.array()(firetray.ChatStatusIcon.themedIconNameCurrent);
|
||||
arry[1] = null;
|
||||
log.debug("theme="+icon_theme+", arry="+arry);
|
||||
let icon_info = gtk.gtk_icon_theme_choose_icon(icon_theme, arry, 22, gtk.GTK_ICON_LOOKUP_FORCE_SIZE);
|
||||
|
||||
// create pixbuf
|
||||
let pixbuf = gdk.gdk_pixbuf_copy(gtk.gtk_icon_info_load_icon(icon_info, null));
|
||||
gtk.gtk_icon_info_free(icon_info); // gobject.g_object_unref(icon_info) in 3.8
|
||||
|
||||
// checks
|
||||
if (gdk.gdk_pixbuf_get_colorspace(pixbuf) != gdk.GDK_COLORSPACE_RGB)
|
||||
log.error("wrong colorspace for pixbuf");
|
||||
if (gdk.gdk_pixbuf_get_bits_per_sample(pixbuf) != 8)
|
||||
log.error("wrong bits_per_sample for pixbuf");
|
||||
if (!gdk.gdk_pixbuf_get_has_alpha(pixbuf))
|
||||
log.error("pixbuf doesn't have alpha");
|
||||
let n_channels = gdk.gdk_pixbuf_get_n_channels(pixbuf);
|
||||
if (n_channels != 4)
|
||||
log.error("wrong nb of channels for pixbuf");
|
||||
|
||||
// init transform
|
||||
let width = gdk.gdk_pixbuf_get_width(pixbuf);
|
||||
let height = gdk.gdk_pixbuf_get_height(pixbuf);
|
||||
log.warn("width="+width+", height="+height);
|
||||
let rowstride = gdk.gdk_pixbuf_get_rowstride(pixbuf);
|
||||
log.warn("rowstride="+rowstride);
|
||||
let length = width*height*n_channels;
|
||||
let pixels = ctypes.cast(gdk.gdk_pixbuf_get_pixels(pixbuf),
|
||||
gobject.guchar.array(length).ptr);
|
||||
log.warn("pixels="+pixels);
|
||||
|
||||
// backup alpha for later fade-in
|
||||
let buffer = new ArrayBuffer(width*height);
|
||||
let alpha_bak = new Uint8Array(buffer);
|
||||
for (let i=3; i<length; i+=n_channels)
|
||||
alpha_bak[(i-3)/n_channels] = pixels.contents[i];
|
||||
|
||||
const ALPHA_STEP = 5;
|
||||
|
||||
// fade out
|
||||
for (let a=255; a>0; a-=ALPHA_STEP) {
|
||||
for(let i=3; i<length; i+=n_channels)
|
||||
if (pixels.contents[i]-ALPHA_STEP>0)
|
||||
pixels.contents[i] -= ALPHA_STEP;
|
||||
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf);
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
// fade in
|
||||
for (let a=255; a>0; a-=ALPHA_STEP) {
|
||||
for(let i=3; i<length; i+=n_channels)
|
||||
if (pixels.contents[i]+ALPHA_STEP<=alpha_bak[(i-3)/n_channels]) {
|
||||
pixels.contents[i] += ALPHA_STEP;
|
||||
}
|
||||
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf);
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
gobject.g_object_unref(pixbuf);
|
||||
});
|
||||
},
|
||||
|
||||
startIconBlinking: function() { // gtk_status_icon_set_blinking() deprecated
|
||||
this.on = true;
|
||||
firetray.ChatStatusIcon.timers['blink'] = firetray.Utils.timer(
|
||||
|
@ -26,7 +26,6 @@ firetray.PopupMenu = {
|
||||
menu: null,
|
||||
menuShell: null,
|
||||
menuSeparatorWindows: null,
|
||||
MIN_FONT_SIZE: 4,
|
||||
|
||||
init: function() {
|
||||
this.menu = gtk.gtk_menu_new();
|
||||
|
@ -26,6 +26,7 @@ if ("undefined" == typeof(firetray.Handler))
|
||||
|
||||
|
||||
firetray.StatusIcon = {
|
||||
MIN_FONT_SIZE: 4,
|
||||
FILENAME_BLANK: null,
|
||||
|
||||
initialized: false,
|
||||
|
Loading…
Reference in New Issue
Block a user