Custom Tooltips for GNOME Shell Panel Launchers

Custom Tooltips for GNOME Shell Panel Launchers
Finnbarr P. Murphy
(fpm@fpmurphy.com) Tooltip positioning in the GNOME Shell 3.2 is quite unsophisticated. I first spotted the problem when I was updating my GNOME Shell panellaunchers extension. I also discovered the same problem also existed in Frippery Panel Favourites by Ron Yorston as shown below.

Just reload GNOME shell to observe the problem.

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se
1/13

on

ly

Custom Tooltips for GNOME Shell Panel Launchers

My first instinct was to modify the tooltip source code to fix the problem. The relevant file is …/src/st/tooltip.c.

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * st-tooltip.c: Plain tooltip actor * * Copyright 2008, 2009 Intel Corporation * Copyright 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * SECTION:st-tooltip * @short_description: A tooltip widget * * #StTooltip implements a single tooltip. It should not normally be created * by the application but by the widget implementing tooltip capabilities, for * example, #st_button_set_tooltip(). */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <math.h> #include <stdlib.h> #include <string.h> #include <glib.h> #include <clutter/clutter.h>

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se
2/13

on

ly

Custom Tooltips for GNOME Shell Panel Launchers #include "st-tooltip.h" #include "st-widget.h" #include "st-label.h" #include "st-private.h" enum { PROP_0, PROP_LABEL, PROP_TIP_AREA }; #define ST_TOOLTIP_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TOOLTIP, StTooltipPrivate)) struct _StTooltipPrivate { StLabel *label; ClutterGeometry *tip_area; }; extern gfloat st_slow_down_factor; G_DEFINE_TYPE (StTooltip, st_tooltip, ST_TYPE_WIDGET); static void st_tooltip_show (ClutterActor *self); static void st_tooltip_show_all (ClutterActor *self); static void st_tooltip_hide_all (ClutterActor *self); static void st_tooltip_constrain (StTooltip *tooltip, const ClutterGeometry *geometry, ClutterGeometry *adjusted_geometry); static void st_tooltip_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { StTooltip *tooltip = ST_TOOLTIP (gobject); switch (prop_id) { case PROP_LABEL: st_tooltip_set_label (tooltip, g_value_get_string (value)); break; case PROP_TIP_AREA: st_tooltip_set_tip_area (tooltip, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_tooltip_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { StTooltipPrivate *priv = ST_TOOLTIP (gobject)->priv; switch (prop_id) { case PROP_LABEL: g_value_set_string (value, st_label_get_text (priv->label)); break; case PROP_TIP_AREA: g_value_set_boxed (value, priv->tip_area); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void st_tooltip_get_preferred_width (ClutterActor *self, gfloat for_height,

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on
3/13

ly

Custom Tooltips for GNOME Shell Panel Launchers gfloat gfloat { StTooltipPrivate *priv = ST_TOOLTIP (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); gfloat label_height; st_theme_node_adjust_for_height (theme_node, &amp;for_height); if (for_height > -1) { label_height = for_height; } else { label_height = -1; } clutter_actor_get_preferred_width (CLUTTER_ACTOR (priv->label), label_height, min_width_p, natural_width_p); st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); *min_width_p, *natural_width_p)

} static void st_tooltip_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StTooltipPrivate *priv = ST_TOOLTIP (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); gfloat min_label_h, natural_label_h; st_theme_node_adjust_for_width (theme_node, &amp;for_width); clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->label), for_width, &amp;min_label_h, &amp;natural_label_h); if (min_height_p) *min_height_p = min_label_h; if (natural_height_p) *natural_height_p = natural_label_h; st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); } static void st_tooltip_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { StTooltipPrivate *priv = ST_TOOLTIP (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); ClutterActorBox content_box, child_box; CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->allocate (self, box, flags); st_theme_node_get_content_box (theme_node, box, &amp;content_box); child_box.x1 = child_box.y1 = 0; child_box.x2 = (box->x2 - box->x1); child_box.y2 = (box->y2 - box->y1); child_box = content_box; clutter_actor_allocate (CLUTTER_ACTOR (priv->label), &amp;child_box, flags); } static void st_tooltip_paint (ClutterActor *self) { StTooltipPrivate *priv = ST_TOOLTIP (self)->priv; CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->paint (self); clutter_actor_paint (CLUTTER_ACTOR (priv->label)); } static void st_tooltip_dispose (GObject *self)

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly
4/13

Custom Tooltips for GNOME Shell Panel Launchers { StTooltipPrivate *priv = ST_TOOLTIP (self)->priv; if (priv->label) { clutter_actor_destroy (CLUTTER_ACTOR (priv->label)); priv->label = NULL; } G_OBJECT_CLASS (st_tooltip_parent_class)->dispose (self); } static void st_tooltip_class_init (StTooltipClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (StTooltipPrivate)); gobject_class->set_property = st_tooltip_set_property; gobject_class->get_property = st_tooltip_get_property; gobject_class->dispose = st_tooltip_dispose; actor_class->get_preferred_width = st_tooltip_get_preferred_width; actor_class->get_preferred_height = st_tooltip_get_preferred_height; actor_class->allocate = st_tooltip_allocate; actor_class->paint = st_tooltip_paint; actor_class->show = st_tooltip_show; actor_class->show_all = st_tooltip_show_all; actor_class->hide_all = st_tooltip_hide_all; pspec = g_param_spec_string ("label", "Label", "Label of the tooltip", NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_LABEL, pspec); pspec = g_param_spec_boxed ("tip-area", "Tip Area", "Area on the stage the tooltip applies to", CLUTTER_TYPE_GEOMETRY, ST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec); } static void st_tooltip_init (StTooltip *tooltip) { tooltip->priv = ST_TOOLTIP_GET_PRIVATE (tooltip); tooltip->priv->label = ST_LABEL (st_label_new (NULL)); tooltip->priv->tip_area = NULL; clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label), CLUTTER_ACTOR (tooltip)); g_object_set (tooltip, "show-on-set-parent", FALSE, NULL); clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE); } static void st_tooltip_update_position (StTooltip *tooltip) { StTooltipPrivate *priv = tooltip->priv; ClutterGeometry *tip_area = tooltip->priv->tip_area; ClutterGeometry geometry; ClutterGeometry adjusted_geometry; gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y; /* if no area set, just position ourselves top left */ if (!priv->tip_area) { clutter_actor_set_anchor_point ((ClutterActor*) tooltip, 0, 0); return; } /* we need to have a style in case there are padding/border values to take into * account when calculating width/height */ st_widget_ensure_style ((StWidget *) tooltip); /* find out the tooltip's size */ clutter_actor_get_size ((ClutterActor*) tooltip, &amp;tooltip_w, &amp;tooltip_h);

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly
5/13

Custom Tooltips for GNOME Shell Panel Launchers /* attempt to place the tooltip */ tooltip_x = (int)(tip_area->x + (tip_area->width / 2) - (tooltip_w / 2)); tooltip_y = (int)(tip_area->y + tip_area->height); geometry.x = tooltip_x; geometry.y = tooltip_y; geometry.width = ceil (tooltip_w); geometry.height = ceil (tooltip_h); st_tooltip_constrain (tooltip, &amp;geometry, &amp;adjusted_geometry); tooltip_x = adjusted_geometry.x; tooltip_y = adjusted_geometry.y; /* Since we are updating the position out of st_widget_allocate(), we can't * call clutter_actor_set_position(), since that would trigger another * allocation cycle. Instead, we adjust the anchor position which moves * the tooltip actor on the screen without changing its allocation */ clutter_actor_set_anchor_point ((ClutterActor*) tooltip, -tooltip_x, -tooltip_y); } /** * st_tooltip_get_label: * @tooltip: a #StTooltip * * Get the text displayed on the tooltip * * Returns: the text for the tooltip. This must not be freed by the application */ const gchar * st_tooltip_get_label (StTooltip *tooltip) { g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL); return st_label_get_text (tooltip->priv->label); } /** * st_tooltip_set_label: * @tooltip: a #StTooltip * @text: text to set the label to * * Sets the text displayed on the tooltip */ void st_tooltip_set_label (StTooltip *tooltip, const gchar *text) { StTooltipPrivate *priv; g_return_if_fail (ST_IS_TOOLTIP (tooltip)); priv = tooltip->priv; st_label_set_text (priv->label, text); g_object_notify (G_OBJECT (tooltip), "label"); } static void st_tooltip_show (ClutterActor *self) { StTooltip *tooltip = ST_TOOLTIP (self); st_tooltip_update_position (tooltip); /* finally show the tooltip... */ CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->show (self); } static void st_tooltip_show_all (ClutterActor *self) { CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->show_all (self); clutter_actor_show_all (CLUTTER_ACTOR (ST_TOOLTIP (self)->priv->label)); } static void st_tooltip_hide_all (ClutterActor *self) { CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->hide_all (self); clutter_actor_hide_all (CLUTTER_ACTOR (ST_TOOLTIP (self)->priv->label)); }

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on
6/13

ly

Custom Tooltips for GNOME Shell Panel Launchers /** * st_tooltip_set_tip_area: * @tooltip: A #StTooltip * @area: A #ClutterGeometry * * Set the area on the stage that the tooltip applies to. */ void st_tooltip_set_tip_area (StTooltip *tooltip, const ClutterGeometry *area) { g_return_if_fail (ST_IS_TOOLTIP (tooltip)); if (tooltip->priv->tip_area) g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area); tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area); if (clutter_actor_get_stage (CLUTTER_ACTOR (tooltip))) st_tooltip_update_position (tooltip); } /** * st_tooltip_get_tip_area: * @tooltip: A #StTooltip * * Retrieve the area on the stage that the tooltip currently applies to * * Returns: the #ClutterGeometry, owned by the tooltip which must not be freed * by the application. */ const ClutterGeometry* st_tooltip_get_tip_area (StTooltip *tooltip) { g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL); return tooltip->priv->tip_area; } typedef struct { StTooltipConstrainFunc func; gpointer data; GDestroyNotify notify; } ConstrainFuncClosure; static void constrain_func_closure_free (gpointer data) { ConstrainFuncClosure *closure = data; if (closure->notify) closure->notify (closure->data); g_slice_free (ConstrainFuncClosure, data); } static GQuark st_tooltip_constrain_func_quark (void) { static GQuark value = 0; if (G_UNLIKELY (value == 0)) value = g_quark_from_static_string ("st-tooltip-constrain-func"); return value; } /** * st_tooltip_set_constrain_func: * @stage: a #ClutterStage * @func: (allow-none): function to be called to constrain tooltip position * @data: (allow-none): user data to pass to @func * @notify: (allow-none): function to be called when @data is no longer needed * * Sets a callback function that will be used to constrain the position * of tooltips within @stage. This can be used, for example, if the stage * spans multiple monitors and tooltips should be positioned not to cross * monitors. */ void st_tooltip_set_constrain_func (ClutterStage *stage,

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on
7/13

ly

Custom Tooltips for GNOME Shell Panel Launchers StTooltipConstrainFunc gpointer GDestroyNotify { ConstrainFuncClosure *closure; g_return_if_fail (CLUTTER_IS_STAGE (stage)); if (func) { closure = g_slice_new (ConstrainFuncClosure); closure->func = func; closure->data = data; closure->notify = notify; } else closure = NULL; g_object_set_qdata_full (G_OBJECT (stage), st_tooltip_constrain_func_quark (), closure, constrain_func_closure_free); func, data, notify)

Fo r

} static void st_tooltip_constrain (StTooltip *tooltip, const ClutterGeometry *geometry, ClutterGeometry *adjusted_geometry) { ConstrainFuncClosure *closure; ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (tooltip)); *adjusted_geometry = *geometry; if (stage == NULL) return; closure = g_object_get_qdata (G_OBJECT (stage), st_tooltip_constrain_func_quark ()); if (closure) { closure->func (tooltip, geometry, adjusted_geometry, closure->data); } else { ClutterActor *parent; gfloat parent_w, parent_h; parent = clutter_actor_get_parent ((ClutterActor *) tooltip); clutter_actor_get_size (parent, &amp;parent_w, &amp;parent_h); /* make sure the tooltip is not off parent horizontally */ if (adjusted_geometry->x < 0) adjusted_geometry->x = 0; else if (adjusted_geometry->x + adjusted_geometry->width > parent_w) adjusted_geometry->x = (int)(parent_w) - adjusted_geometry->width; /* make sure the tooltip is not off parent vertically */ if (adjusted_geometry->y + adjusted_geometry->height > parent_h) adjusted_geometry->y = parent_h - adjusted_geometry->height; } }

As you can see from the above code, there is no easy way to add functionality to directly position the tooltip where you want it to be displayed. You could possibly use st_tooltip_set_tip_area to specify the tooltip area. Alternatively, you could probably write a callback function to constrain a tooltip’s position (See st_tooltip_set_constrain_func) but I suspect that lots of problems would lie in wait if I choose that option. Both of those choices involved Clutter.Geometry. By the way, a useful way to see what functionality is available to you in libgnome-shell is to uncompile /usr/share/lib[64]/gnome-shell/St-1.0.typelib using the g-ir-generate utility. Here is the relevant output.

<class name="Tooltip" parent="Widget" glib:type-struct="TooltipClass" glib:type-name=" StTooltip" glib:get-type="st_tooltip_get_type"> <implements name="Atk.ImplementorIface"/>

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly
8/13

Custom Tooltips for GNOME Shell Panel Launchers <implements name="Clutter.Animatable"/> <implements name="Clutter.Scriptable"/> <field name="parent_instance"> <type name="Widget"/> </field> <field name="priv"> <type name="TooltipPrivate"/> </field> <function name="set_constrain_func" c:identifier="st_tooltip_set_constrain_func"> <return-value transfer-ownership="none"> <type name="none"/> </return-value> <parameters> <parameter name="stage" transfer-ownership="none"> <type name="Clutter.Stage"/> </parameter> <parameter name="func" transfer-ownership="none" allow-none="1" scope="notified" closure="2" destroy="3"> <type name="TooltipConstrainFunc"/> </parameter> <parameter name="data" transfer-ownership="none" allow-none="1"> <type name="any"/> </parameter> <parameter name="notify" transfer-ownership="none" allow-none="1" scope="async"> <type name="GLib.DestroyNotify"/> </parameter> </parameters> </function> <method name="get_label" c:identifier="st_tooltip_get_label"> <return-value transfer-ownership="none"> <type name="utf8"/> </return-value> </method> <method name="get_tip_area" c:identifier="st_tooltip_get_tip_area"> <return-value transfer-ownership="none"> <type name="Clutter.Geometry"/> </return-value> </method> <method name="set_label" c:identifier="st_tooltip_set_label"> <return-value transfer-ownership="none"> <type name="none"/> </return-value> <parameters> <parameter name="text" transfer-ownership="none"> <type name="utf8"/> </parameter> </parameters> </method> <method name="set_tip_area" c:identifier="st_tooltip_set_tip_area"> <return-value transfer-ownership="none"> <type name="none"/> </return-value> <parameters> <parameter name="area" transfer-ownership="none"> <type name="Clutter.Geometry"/> </parameter> </parameters> </method> <property name="label" writable="1" transfer-ownership="none"> <type name="utf8"/> </property> <property name="tip-area" writable="1" transfer-ownership="none"> <type name="Clutter.Geometry"/> </property> </class>

Fo r

For some time, I have followed the discussions in GNOME Shell Bug 666166. Now that the

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly

9/13

Custom Tooltips for GNOME Shell Panel Launchers

resulting code changes developed by Seif Lotfy (lead for the Zeitgeist project) are committed, I decided to implement similar tooltip functionality in my panellauncher extension to ensure that tooltips were displayed where I wanted them to be displayed.

// // Copyright (c) 2011. Finnbarr P. Murphy. All rights reserved. // const Lang = imports.lang; const Mainloop = imports.mainloop; const Shell = imports.gi.Shell; const St = imports.gi.St; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const PanelMenu = imports.ui.panelMenu; const AppFavorites = imports.ui.appFavorites; const Tweener = imports.ui.tweener; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; const ICON_SIZE = 20; const TOOLTIP_LABEL_SHOW_TIME = 0.15; const TOOLTIP_LABEL_HIDE_TIME = 0.1; const TOOLTIP_HOVER_TIMEOUT = 300; function PanelLauncher(app) { this._init(app); } PanelLauncher.prototype = { _init: function(app) { this._labelTimeoutId = 0; this._label = null; this.actor = new St.Button({ style_class: 'panel-launcher', reactive: true }); this.actor.set_child(app.create_icon_texture(ICON_SIZE));

Fo r

12-28-2011

pe rs o

The following code, which again is simple panel launcher for favorites (i.e. the Dash items) extension shows you how to roll your own tooltips for horizontal icon menus. It enables you to control precisely the position of tooltips, their styling, and their fade in and out properties using a combination of constants and stylesheet properties.

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se
10/13

on

ly

Custom Tooltips for GNOME Shell Panel Launchers this.setLabelText(app.get_name()); this.actor._delegate = this; this._app = app; this.actor.connect('clicked', Lang.bind(this, function() { this._app.open_new_window(-1); })); this.actor.connect('notify::hover', Lang.bind(this, function() { this._onButtonHover(); })); }, _onButtonHover: function () { if (this.actor.get_hover()) { if (this._labelTimeoutId == 0) { this._labelTimeoutId = Mainloop.timeout_add(TOOLTIP_HOVER_TIMEOUT, Lang.b ind(this, function() { this.showLabel(); return false; })); } } else { if (this._labelTimeoutId > 0) Mainloop.source_remove(this._labelTimeoutId); this._labelTimeoutId = 0; this.hideLabel(); } }, setLabelText: function(text) { if (this._label == null) this._label = new St.Label({ style_class: 'panel-launcher-label'}); this._label.set_text(text); Main.layoutManager.addChrome(this._label); this._label.hide(); }, showLabel: function() { if (this._label == null) return; this._label.opacity = 0; this._label.show(); let [stageX, stageY] = this.actor.get_transformed_position(); let node = this._label.get_theme_node(); let xOffset = node.get_length('-x-offset'); let yOffset = node.get_length('-y-offset'); let y = stageY + yOffset; let x = stageX - (this._label.get_width())/2 + xOffset; this._label.set_position(x, y); Tweener.addTween(this._label, { opacity: 255, time: TOOLTIP_LABEL_SHOW_TIME, transition: 'easeOutQuad', }); }, hideLabel: function () { this._label.opacity = 255; Tweener.addTween(this._label, { opacity: 0, time: TOOLTIP_LABEL_HIDE_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, function() { this._lab el.hide(); }) }); } }; function PanelPopupMenuItem() { this._init.apply(this, arguments); } PanelPopupMenuItem.prototype = { __proto__: PopupMenu.PopupBaseMenuItem.prototype, _init: function(icon, text, menu_icon_first, params) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params); this.label = new St.Label({ text: text }); if (menu_icon_first) {

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly

11/13

Custom Tooltips for GNOME Shell Panel Launchers this.box = new St.BoxLayout({ style_class: 'panel-launcher-box'}); this.box.add(icon); this.box.add(this.label); this.addActor(this.box); } else { this.addActor(this.label); this.addActor(icon); } } }; function Favorites() { this._init(); } Favorites.prototype = { FAVORITE_APPS_KEY: 'favorite-apps', _init: function() { this._buttons = []; this._appSystem = Shell.AppSystem.get_default(); this.actor = new St.BoxLayout({ style_class: 'panel-launcher-box'}); this._display(); this._appSystem.connect('installed-changed', Lang.bind(this, this._redisplay)); AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._redisplay) ); }, _display: function() { let launchers = global.settings.get_strv(this.FAVORITE_APPS_KEY); let appSys = Shell.AppSystem.get_default(); let j = 0; for ( let i = 0; i < launchers.length; ++i ) { if ((app = appSys.lookup_app(launchers[i]))) { this._buttons[j] = new PanelLauncher(app); this.actor.add(this._buttons[j].actor); ++j; } } }, _redisplay: function() { for ( let i = 0; i < this._buttons.length; ++i ) this._buttons[i].actor.destroy(); this._display(); }, enable: function() { Main.panel._leftBox.insert_actor(this.actor, 1); }, disable: function() { Main.panel._leftBox.remove_actor(this.actor); } }; function init(extensionMeta) { return new Favorites(); }

Fo r

Here is the stylesheet that I use with this extension:

panel-launcher-box { spacing: 7px; } .panel-launcher { } .panel-launcher-label { border: 1px solid rgba(255,255,255,0.6); border-radius: 5px; padding: 2px 12px; background-color: rgba(0,0,0,0.9); color: #ffffff;

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly

12/13

Custom Tooltips for GNOME Shell Panel Launchers font-size: 0.8em; font-weight: bold; text-align: center; -x-offset: 8px; -y-offset: 30px; }

The interesting lines in the above stylesheet are -x-offset and -y-offset. These are not approved CSS properties. However the GNOME Shell does not complain about these non-standard properties. Here is a simple example of how to retrieve the value of these two non-standard properties:

By the way, you can download this particular extension from here. Merry Christmas and a Happy New Year to all my readers.

Fo r

12-28-2011

pe rs o

Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se
13/13

on

let let let let

label = new St.Label({ style_class: 'panel-launcher-label'}); node = label.get_theme_node(); xOffset = node.get_length('-x-offset'); yOffset = node.get_length('-y-offset');

ly

Sign up to vote on this title
UsefulNot useful