[panel] Scale up, center and fade application icon in app menu
Per 20091114 design. https://bugzilla.gnome.org/show_bug.cgi?id=605491
This commit is contained in:
parent
262f92e0be
commit
c17e1249d5
@ -26,6 +26,10 @@
|
|||||||
color: #0000e0;
|
color: #0000e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-shadow {
|
||||||
|
color: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
StScrollBar
|
StScrollBar
|
||||||
{
|
{
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
137
js/ui/panel.js
137
js/ui/panel.js
@ -13,6 +13,7 @@ const Signals = imports.signals;
|
|||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
const _ = Gettext.gettext;
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
const Calendar = imports.ui.calendar;
|
const Calendar = imports.ui.calendar;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const StatusMenu = imports.ui.statusMenu;
|
const StatusMenu = imports.ui.statusMenu;
|
||||||
@ -46,6 +47,99 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
|||||||
'gnome-power-manager': 'battery'
|
'gnome-power-manager': 'battery'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function TextShadower() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextShadower.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new Shell.GenericContainer();
|
||||||
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
|
||||||
|
this._label = new St.Label();
|
||||||
|
this.actor.add_actor(this._label);
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
let actor = new St.Label({ style_class: 'label-shadow' });
|
||||||
|
this.actor.add_actor(actor);
|
||||||
|
}
|
||||||
|
this._label.raise_top();
|
||||||
|
},
|
||||||
|
|
||||||
|
setText: function(text) {
|
||||||
|
let children = this.actor.get_children();
|
||||||
|
for (let i = 0; i < children.length; i++)
|
||||||
|
children[i].set_text(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
|
let [minWidth, natWidth] = this._label.get_preferred_width(forHeight);
|
||||||
|
alloc.min_size = minWidth;
|
||||||
|
alloc.natural_size = natWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||||
|
let [minHeight, natHeight] = this._label.get_preferred_height(forWidth);
|
||||||
|
alloc.min_size = minHeight;
|
||||||
|
alloc.natural_size = natHeight;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function(actor, box, flags) {
|
||||||
|
let children = this.actor.get_children();
|
||||||
|
|
||||||
|
let availWidth = box.x2 - box.x1;
|
||||||
|
let availHeight = box.y2 - box.y1;
|
||||||
|
|
||||||
|
let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] =
|
||||||
|
this._label.get_preferred_size();
|
||||||
|
|
||||||
|
let childWidth = Math.min(natChildWidth, availWidth);
|
||||||
|
let childHeight = Math.min(natChildHeight, availHeight);
|
||||||
|
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
let child = children[i];
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
// The order of the labels here is arbitrary, except
|
||||||
|
// we know the "real" label is at the end because Clutter.Group
|
||||||
|
// sorts by Z order
|
||||||
|
switch (i) {
|
||||||
|
case 0: // top
|
||||||
|
childBox.x1 = 1;
|
||||||
|
childBox.y1 = 0;
|
||||||
|
break;
|
||||||
|
case 1: // right
|
||||||
|
childBox.x1 = 2;
|
||||||
|
childBox.y1 = 1;
|
||||||
|
break;
|
||||||
|
case 2: // bottom
|
||||||
|
childBox.x1 = 1;
|
||||||
|
childBox.y1 = 2;
|
||||||
|
break;
|
||||||
|
case 3: // left
|
||||||
|
childBox.x1 = 0;
|
||||||
|
childBox.y1 = 1;
|
||||||
|
break;
|
||||||
|
case 4: // center
|
||||||
|
childBox.x1 = 1;
|
||||||
|
childBox.y1 = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
childBox.x2 = childBox.x1 + childWidth;
|
||||||
|
childBox.y2 = childBox.y1 + childHeight;
|
||||||
|
child.allocate(childBox, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AppPanelMenu:
|
||||||
|
*
|
||||||
|
* This class manages the "application menu" component. It tracks the
|
||||||
|
* currently focused application. However, when an app is launched,
|
||||||
|
* this menu also handles startup notification for it. So when we
|
||||||
|
* have an active startup notification, we switch modes to display that.
|
||||||
|
*/
|
||||||
function AppPanelMenu() {
|
function AppPanelMenu() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
@ -59,10 +153,11 @@ AppPanelMenu.prototype = {
|
|||||||
this._startupSequences = {};
|
this._startupSequences = {};
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ name: 'appMenu' });
|
this.actor = new St.BoxLayout({ name: 'appMenu' });
|
||||||
this._iconBox = new St.Bin({ name: 'appMenuIcon' });
|
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
||||||
this.actor.add(this._iconBox);
|
this.actor.add(this._iconBox);
|
||||||
this._label = new St.Label();
|
this._label = new TextShadower();
|
||||||
this.actor.add(this._label, { expand: true, y_fill: false });
|
this.actor.add(this._label.actor, { expand: true, y_fill: true });
|
||||||
|
this.actor.connect('notify::allocation', Lang.bind(this, this._repositionLabel));
|
||||||
|
|
||||||
this._startupBox = new St.BoxLayout();
|
this._startupBox = new St.BoxLayout();
|
||||||
this.actor.add(this._startupBox);
|
this.actor.add(this._startupBox);
|
||||||
@ -86,6 +181,16 @@ AppPanelMenu.prototype = {
|
|||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_repositionLabel: function() {
|
||||||
|
this._label.actor.x = Math.floor(AppDisplay.APPICON_SIZE / 2);
|
||||||
|
let actorAlloc = this.actor.allocation;
|
||||||
|
let actorHeight = actorAlloc.y2 - actorAlloc.y1;
|
||||||
|
let labelAlloc = this._label.actor.allocation;
|
||||||
|
let labelHeight = labelAlloc.y2 - labelAlloc.y1;
|
||||||
|
this._label.actor.y = Math.floor((actorHeight - labelHeight) / 2);
|
||||||
|
this._label.actor.fixed_position_set = true;
|
||||||
|
},
|
||||||
|
|
||||||
_sync: function() {
|
_sync: function() {
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
|
||||||
@ -112,21 +217,25 @@ AppPanelMenu.prototype = {
|
|||||||
if (this._iconBox.child != null)
|
if (this._iconBox.child != null)
|
||||||
this._iconBox.child.destroy();
|
this._iconBox.child.destroy();
|
||||||
this._iconBox.hide();
|
this._iconBox.hide();
|
||||||
this._label.set_text('');
|
this._label.setText('');
|
||||||
|
let icon;
|
||||||
if (this._focusedApp != null) {
|
if (this._focusedApp != null) {
|
||||||
let icon = this._focusedApp.create_icon_texture(PANEL_ICON_SIZE);
|
icon = this._focusedApp.create_icon_texture(AppDisplay.APPICON_SIZE);
|
||||||
this._iconBox.set_child(icon);
|
this._label.setText(this._focusedApp.get_name());
|
||||||
this._iconBox.show();
|
|
||||||
let appName = this._focusedApp.get_name();
|
|
||||||
// Use _set_text to work around http://bugzilla.openedhand.com/show_bug.cgi?id=1851
|
|
||||||
this._label.set_text(appName);
|
|
||||||
} else if (this._activeSequence != null) {
|
} else if (this._activeSequence != null) {
|
||||||
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
|
icon = this._activeSequence.create_icon(AppDisplay.APPICON_SIZE);
|
||||||
this._iconBox.set_child(icon);
|
this._label.setText(this._activeSequence.get_name());
|
||||||
this._iconBox.show();
|
} else {
|
||||||
this._label.set_text(this._activeSequence.get_name());
|
icon = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (icon != null) {
|
||||||
|
let faded = Shell.fade_app_icon(icon); /* TODO consider caching */
|
||||||
|
this._iconBox.set_child(faded);
|
||||||
|
this._iconBox.show();
|
||||||
|
}
|
||||||
|
this._repositionLabel();
|
||||||
|
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,71 @@ shell_draw_clock (ClutterCairoTexture *texture,
|
|||||||
cairo_destroy (cr);
|
cairo_destroy (cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_fade_app_icon:
|
||||||
|
* @source: Source #ClutterTexture
|
||||||
|
*
|
||||||
|
* Create a new texture by modifying the alpha channel of the
|
||||||
|
* source texture, adding a horizontal gradient fade.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): A new #ClutterTexture
|
||||||
|
*/
|
||||||
|
ClutterTexture *
|
||||||
|
shell_fade_app_icon (ClutterTexture *source)
|
||||||
|
{
|
||||||
|
CoglHandle texture;
|
||||||
|
guchar *pixels;
|
||||||
|
gint width, height, rowstride;
|
||||||
|
gint fade_start;
|
||||||
|
gint fade_range;
|
||||||
|
guint i, j;
|
||||||
|
ClutterTexture *result;
|
||||||
|
|
||||||
|
texture = clutter_texture_get_cogl_texture (source);
|
||||||
|
if (texture == COGL_INVALID_HANDLE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
width = cogl_texture_get_width (texture);
|
||||||
|
height = cogl_texture_get_height (texture);
|
||||||
|
rowstride = (width * 4 + 3) & ~3;
|
||||||
|
|
||||||
|
pixels = g_malloc0 (rowstride * height);
|
||||||
|
|
||||||
|
cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
||||||
|
rowstride, pixels);
|
||||||
|
cogl_texture_unref (texture);
|
||||||
|
|
||||||
|
fade_start = width / 2;
|
||||||
|
fade_range = width - fade_start;
|
||||||
|
for (i = fade_start; i < width; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
guchar *pixel = &pixels[j * rowstride + i * 4];
|
||||||
|
float fade = 1.0 - ((float) i - fade_start) / fade_range;
|
||||||
|
pixel[0] = 0.5 + pixel[0] * fade;
|
||||||
|
pixel[1] = 0.5 + pixel[1] * fade;
|
||||||
|
pixel[2] = 0.5 + pixel[2] * fade;
|
||||||
|
pixel[3] = 0.5 + pixel[3] * fade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = cogl_texture_new_from_data (width,
|
||||||
|
height,
|
||||||
|
COGL_TEXTURE_NONE,
|
||||||
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
||||||
|
COGL_PIXEL_FORMAT_ANY,
|
||||||
|
rowstride,
|
||||||
|
pixels);
|
||||||
|
g_free (pixels);
|
||||||
|
|
||||||
|
result = (ClutterTexture*)clutter_texture_new ();
|
||||||
|
clutter_texture_set_cogl_texture (result, texture);
|
||||||
|
cogl_texture_unref (texture);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||||
ShellPointerDirection direction,
|
ShellPointerDirection direction,
|
||||||
|
@ -23,6 +23,8 @@ void shell_draw_clock (ClutterCairoTexture *texture,
|
|||||||
int hour,
|
int hour,
|
||||||
int minute);
|
int minute);
|
||||||
|
|
||||||
|
ClutterTexture * shell_fade_app_icon (ClutterTexture *source);
|
||||||
|
|
||||||
guint shell_add_hook_paint_red_border (ClutterActor *actor);
|
guint shell_add_hook_paint_red_border (ClutterActor *actor);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
Loading…
Reference in New Issue
Block a user