diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 2eaf51b4e..a939a834f 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -19,6 +19,10 @@ const Workspaces = imports.ui.workspaces; const ENTERED_MENU_COLOR = new Clutter.Color(); ENTERED_MENU_COLOR.from_pixel(0x00ff0022); +const GLOW_COLOR = new Clutter.Color(); +GLOW_COLOR.from_pixel(0x4f6ba4ff); +const GLOW_PADDING = 5; + const APP_ICON_SIZE = 48; const APP_PADDING = 18; @@ -464,7 +468,6 @@ WellDisplayItem.prototype = { border: 0, padding: 1, border_color: GenericDisplay.ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR, - width: APP_ICON_SIZE + APP_PADDING, reactive: true }); this.actor.connect('enter-event', Lang.bind(this, function(o, event) { @@ -494,25 +497,46 @@ WellDisplayItem.prototype = { this._windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id()); - let nameBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, + let nameBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, x_align: Big.BoxAlignment.CENTER }); + this._nameBox = nameBox; + + this._wordWidth = Shell.Global.get().get_max_word_width(this.actor, appInfo.get_name(), + "Sans 12px"); this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR, font_name: "Sans 12px", - ellipsize: Pango.EllipsizeMode.END, line_alignment: Pango.Alignment.CENTER, line_wrap: true, - line_wrap_mode: Pango.WrapMode.WORD_CHAR, + line_wrap_mode: Pango.WrapMode.WORD, text: appInfo.get_name() }); - nameBox.append(this._name, Big.BoxPackFlags.EXPAND); + nameBox.append(this._name, Big.BoxPackFlags.NONE); if (this._windows.length > 0) { - let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR, - border: 1, - padding: 1 */ }); - runningBox.append(nameBox, Big.BoxPackFlags.EXPAND); - this.actor.append(runningBox, Big.BoxPackFlags.NONE); - } else { - this.actor.append(nameBox, Big.BoxPackFlags.NONE); + let glow = new Shell.DrawingArea({}); + glow.connect('redraw', Lang.bind(this, function (e, tex) { + Shell.Global.clutter_cairo_texture_draw_glow(tex, + GLOW_COLOR.red / 255, + GLOW_COLOR.green / 255, + GLOW_COLOR.blue / 255, + GLOW_COLOR.alpha / 255); + })); + this._name.connect('notify::allocation', Lang.bind(this, function (n, alloc) { + let x = this._name.x; + let y = this._name.y; + let width = this._name.width; + let height = this._name.height; + // If we're smaller than the allocated box width, pad out the glow a bit + // to make it more visible + if ((width + GLOW_PADDING * 2) < this._nameBox.width) { + width += GLOW_PADDING * 2; + x -= GLOW_PADDING; + } + glow.set_size(width, height); + glow.set_position(x, y); + })); + nameBox.add_actor(glow); + glow.lower(this._name); } + this.actor.append(nameBox, Big.BoxPackFlags.NONE); }, _handleActivate: function () { @@ -560,6 +584,14 @@ WellDisplayItem.prototype = { // that represents the item as it is being dragged. getDragActorSource: function() { return this._icon; + }, + + getWordWidth: function() { + return this._wordWidth; + }, + + setWidth: function(width) { + this._nameBox.width = width + GLOW_PADDING * 2; } }; @@ -587,14 +619,32 @@ WellArea.prototype = { v.destroy(); })); + this._maxWordWidth = 0; + let displays = [] for (let i = 0; i < infos.length; i++) { let app = infos[i]; let display = new WellDisplayItem(app, this.isFavorite); + displays.push(display); + let width = display.getWordWidth(); + if (width > this._maxWordWidth) + this._maxWordWidth = width; display.connect('activated', Lang.bind(this, function (display) { this.emit('activated', display); })); this.actor.add_actor(display.actor); } + this._displays = displays; + }, + + getWordWidth: function() { + return this._maxWordWidth; + }, + + setItemWidth: function(width) { + for (let i = 0; i < this._displays.length; i++) { + let display = this._displays[i]; + display.setWidth(width); + } }, // Draggable target interface @@ -710,6 +760,11 @@ AppWell.prototype = { let running = this._lookupApps(runningIds); this._favoritesArea.redisplay(favorites); this._runningArea.redisplay(running); + let maxWidth = this._favoritesArea.getWordWidth(); + if (this._runningArea.getWordWidth() > maxWidth) + maxWidth = this._runningArea.getWordWidth(); + this._favoritesArea.setItemWidth(maxWidth); + this._runningArea.setItemWidth(maxWidth); // If it's empty, we hide it so the top border doesn't show up if (running.length == 0) this._runningBox.hide(); diff --git a/src/shell-global.c b/src/shell-global.c index 8429cd1af..0c70e302e 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -1061,6 +1061,46 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global) return clutter_clone_new (global->root_pixmap); } +/** + * shell_global_get_max_word_width: + * @global: + * @ref: Actor to use for font information + * @text: Text to analyze + * @font: Given font size to use + * + * Compute the largest pixel size among the individual words in text, when + * rendered in the given font. + */ +guint +shell_global_get_max_word_width (ShellGlobal *global, ClutterActor *ref, const char *text, const char *font) +{ + PangoLayout *layout; + PangoFontDescription *fontdesc; + char **components; + char **iter; + gint max; + + layout = clutter_actor_create_pango_layout (ref, NULL); + fontdesc = pango_font_description_from_string (font); + pango_layout_set_font_description (layout, fontdesc); + pango_font_description_free (fontdesc); + + max = 0; + components = g_strsplit (text, " ", 0); + for (iter = components; *iter; iter++) + { + char *component = *iter; + gint width, height; + pango_layout_set_text (layout, component, -1); + pango_layout_get_pixel_size (layout, &width, &height); + if (width > max) + max = width; + } + g_object_unref (layout); + g_strfreev (components); + return (guint)max; +} + void shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture, int hour, @@ -1107,3 +1147,37 @@ shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture, cairo_destroy (cr); } + + +void +shell_global_clutter_cairo_texture_draw_glow (ClutterCairoTexture *texture, + double red, + double green, + double blue, + double alpha) +{ + cairo_t *cr; + guint width, height; + cairo_pattern_t *gradient; + + clutter_cairo_texture_get_surface_size (texture, &width, &height); + + clutter_cairo_texture_clear (texture); + cr = clutter_cairo_texture_create (texture); + + cairo_save (cr); + cairo_translate (cr, width / 2.0, height / 2.0); + cairo_scale (cr, width / 2.0, height / 2.0); + + gradient = cairo_pattern_create_radial (0.0, 0.0, 0.0, 0.0, 0.0, 1.0); + cairo_pattern_add_color_stop_rgba (gradient, 0.0, red, green, blue, alpha); + cairo_pattern_add_color_stop_rgba (gradient, 0.7, red, green, blue, alpha * 0.7); + cairo_pattern_add_color_stop_rgba (gradient, 1.0, red, green, blue, alpha * 0.3); + cairo_set_source (cr, gradient); + + cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI); + cairo_fill (cr); + cairo_restore (cr); + cairo_pattern_destroy (gradient); + cairo_destroy (cr); +} diff --git a/src/shell-global.h b/src/shell-global.h index 813d42ab8..52cd9efb4 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -81,10 +81,18 @@ void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta, ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global); +guint shell_global_get_max_word_width (ShellGlobal *global, ClutterActor *ref, const char *text, const char *font); + void shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture, int hour, int minute); +void shell_global_clutter_cairo_texture_draw_glow (ClutterCairoTexture *texture, + double red, + double blue, + double green, + double alpha); + G_END_DECLS #endif /* __SHELL_GLOBAL_H__ */