panel: Switch to fully dynamic layout

There was lots of fixed positioning in the Panel; now it is completely
dynamic, and width/height is driven from main.js.  We still have a
global constant Panel.PANEL_HEIGHT, but this is a big step towards
eliminating it.

Also, this avoids overdraw in the "way too many tray icons" case.  The
clock will shift left.
This commit is contained in:
Colin Walters 2009-08-11 11:16:25 -04:00
parent e6644b7feb
commit e330c5ea17
3 changed files with 128 additions and 49 deletions

View File

@ -77,6 +77,8 @@ function start() {
} }
}); });
_relayout();
panel.startupAnimation(); panel.startupAnimation();
let display = global.screen.get_display(); let display = global.screen.get_display();
@ -86,6 +88,12 @@ function start() {
Mainloop.idle_add(_removeUnusedWorkspaces); Mainloop.idle_add(_removeUnusedWorkspaces);
} }
function _relayout() {
let global = Shell.Global.get();
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
overview.relayout();
}
// metacity-clutter currently uses the same prefs as plain metacity, // metacity-clutter currently uses the same prefs as plain metacity,
// which probably means we'll be starting out with multiple workspaces; // which probably means we'll be starting out with multiple workspaces;
// remove any unused ones. (We do this from an idle handler, because // remove any unused ones. (We do this from an idle handler, because

View File

@ -127,8 +127,6 @@ Overview.prototype = {
this._transparentBackground.lower_bottom(); this._transparentBackground.lower_bottom();
this._paneContainer.lower_bottom(); this._paneContainer.lower_bottom();
this._repositionChildren();
this._workspaces = null; this._workspaces = null;
}, },
@ -148,7 +146,7 @@ Overview.prototype = {
} }
}, },
_repositionChildren: function () { relayout: function () {
let global = Shell.Global.get(); let global = Shell.Global.get();
let contentHeight = global.screen_height - Panel.PANEL_HEIGHT; let contentHeight = global.screen_height - Panel.PANEL_HEIGHT;

View File

@ -15,6 +15,8 @@ const Main = imports.ui.main;
const PANEL_HEIGHT = 26; const PANEL_HEIGHT = 26;
const TRAY_HEIGHT = PANEL_HEIGHT - 1; const TRAY_HEIGHT = PANEL_HEIGHT - 1;
const DEFAULT_PADDING = 4;
const PANEL_BACKGROUND_COLOR = new Clutter.Color(); const PANEL_BACKGROUND_COLOR = new Clutter.Color();
PANEL_BACKGROUND_COLOR.from_pixel(0x000000ff); PANEL_BACKGROUND_COLOR.from_pixel(0x000000ff);
const PANEL_FOREGROUND_COLOR = new Clutter.Color(); const PANEL_FOREGROUND_COLOR = new Clutter.Color();
@ -55,26 +57,99 @@ Panel.prototype = {
_init : function() { _init : function() {
let global = Shell.Global.get(); let global = Shell.Global.get();
// Put the background under the panel within a group. this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
this.actor = new Clutter.Group(); background_color: PANEL_BACKGROUND_COLOR
});
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER,
padding_right: DEFAULT_PADDING });
this._centerBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER });
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
y_align: Big.BoxAlignment.CENTER,
padding_left: DEFAULT_PADDING });
// backBox contains the panel background and the clock. /* This box container ensures that the centerBox is positioned in the *absolute*
let backBox = new Big.Box({ width: global.screen_width, * center, but can be pushed aside if necessary. */
height: PANEL_HEIGHT, this._boxContainer = new Shell.GenericContainer();
backgroundColor: PANEL_BACKGROUND_COLOR, this.actor.append(this._boxContainer, Big.BoxPackFlags.EXPAND);
x_align: Big.BoxAlignment.CENTER }); this._boxContainer.add_actor(this._leftBox);
this.actor.add_actor(backBox); this._boxContainer.add_actor(this._centerBox);
this._boxContainer.add_actor(this._rightBox);
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
alloc.min_size += childMin;
alloc.natural_size += childNatural;
}
}));
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
if (childMin > alloc.min_size)
alloc.min_size = childMin;
if (childNatural > alloc.natural_size)
alloc.natural_size = childNatural;
}
}));
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
let leftWidth, centerWidth, rightWidth;
if (allocWidth < (leftNaturalWidth + centerNaturalWidth + rightNaturalWidth)) {
leftWidth = leftMinWidth;
centerWidth = centerMinWidth;
rightWidth = rightMinWidth;
} else {
leftWidth = leftNaturalWidth;
centerWidth = centerNaturalWidth;
rightWidth = rightNaturalWidth;
}
let box = new Big.Box({ x: 0, let x;
y: 0, let childBox = new Clutter.ActorBox();
height: PANEL_HEIGHT, childBox.x1 = box.x1;
width: global.screen_width, childBox.y1 = box.y1;
orientation: Big.BoxOrientation.HORIZONTAL, childBox.x2 = x = childBox.x1 + leftWidth;
spacing: 4 }); childBox.y2 = box.y2;
this._leftBox.allocate(childBox, flags);
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, PANEL_FOREGROUND_COLOR, true, null, PANEL_HEIGHT, DEFAULT_FONT); let centerNaturalX = Math.floor((box.x2 - box.x1) / 2 - (centerWidth / 2));
/* Check left side */
if (x < centerNaturalX) {
/* We didn't overflow the left, use the natural. */
x = centerNaturalX;
}
/* Check right side */
if (x + centerWidth > (box.x2 - rightWidth)) {
x = box.x2 - rightWidth - centerWidth;
}
childBox = new Clutter.ActorBox();
childBox.x1 = x;
childBox.y1 = box.y1;
childBox.x2 = x = childBox.x1 + centerWidth;
childBox.y2 = box.y2;
this._centerBox.allocate(childBox, flags);
box.append(this.button.button, Big.BoxPackFlags.NONE); childBox = new Clutter.ActorBox();
childBox.x1 = box.x2 - rightWidth;
childBox.y1 = box.y1;
childBox.x2 = box.x2;
childBox.y2 = box.y2;
this._rightBox.allocate(childBox, flags);
}));
/* left side */
this.button = new Button.Button("Activities", PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
PANEL_FOREGROUND_COLOR, true, null, PANEL_HEIGHT, DEFAULT_FONT);
this._leftBox.append(this.button.button, Big.BoxPackFlags.NONE);
let hotCorner = new Clutter.Rectangle({ width: 1, let hotCorner = new Clutter.Rectangle({ width: 1,
height: 1, height: 1,
@ -89,43 +164,22 @@ Panel.prototype = {
hotCorner.connect('button-release-event', hotCorner.connect('button-release-event',
Lang.bind(this, this._onHotCornerTriggered)); Lang.bind(this, this._onHotCornerTriggered));
box.add_actor(hotCorner); this._leftBox.append(hotCorner, Big.BoxPackFlags.FIXED);
let statusbox = new Big.Box(); /* center */
let statusmenu = this._statusmenu = new Shell.StatusMenu();
statusmenu.get_icon().hide();
statusmenu.get_name().fontName = DEFAULT_FONT;
statusmenu.get_name().color = PANEL_FOREGROUND_COLOR;
statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
let statusbutton = new Button.Button(statusbox,
PANEL_BUTTON_COLOR,
PRESSED_BUTTON_BACKGROUND_COLOR,
PANEL_FOREGROUND_COLOR,
true, null, PANEL_HEIGHT);
statusbutton.button.connect('button-press-event', function (b, e) {
statusmenu.toggle(e);
return false;
});
box.append(statusbutton.button, Big.BoxPackFlags.END);
// We get a deactivated event when the popup disappears
this._statusmenu.connect('deactivated', function (sm) {
statusbutton.release();
});
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT, this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
color: PANEL_FOREGROUND_COLOR, color: PANEL_FOREGROUND_COLOR,
text: "" }); text: "" });
let clockbox = new Big.Box({ y_align: Big.BoxAlignment.CENTER, this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
padding_left: 4,
padding_right: 4 }); /* right */
clockbox.append(this._clock, Big.BoxPackFlags.NONE);
backBox.append(clockbox, Big.BoxPackFlags.EXPAND);
// The tray icons live in trayBox within trayContainer. // The tray icons live in trayBox within trayContainer.
// The trayBox is hidden when there are no tray icons. // The trayBox is hidden when there are no tray icons.
let trayContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL, let trayContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
y_align: Big.BoxAlignment.START }); y_align: Big.BoxAlignment.START });
box.append(trayContainer, Big.BoxPackFlags.END); this._rightBox.append(trayContainer, Big.BoxPackFlags.NONE);
let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL, let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
height: TRAY_HEIGHT, height: TRAY_HEIGHT,
padding: TRAY_PADDING, padding: TRAY_PADDING,
@ -164,6 +218,27 @@ Panel.prototype = {
})); }));
this._traymanager.manage_stage(global.stage); this._traymanager.manage_stage(global.stage);
let statusbox = new Big.Box();
let statusmenu = this._statusmenu = new Shell.StatusMenu();
statusmenu.get_icon().hide();
statusmenu.get_name().fontName = DEFAULT_FONT;
statusmenu.get_name().color = PANEL_FOREGROUND_COLOR;
statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
let statusbutton = new Button.Button(statusbox,
PANEL_BUTTON_COLOR,
PRESSED_BUTTON_BACKGROUND_COLOR,
PANEL_FOREGROUND_COLOR,
true, null, PANEL_HEIGHT);
statusbutton.button.connect('button-press-event', function (b, e) {
statusmenu.toggle(e);
return false;
});
this._rightBox.append(statusbutton.button, Big.BoxPackFlags.NONE);
// We get a deactivated event when the popup disappears
this._statusmenu.connect('deactivated', function (sm) {
statusbutton.release();
});
// TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.) // TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably // We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants // have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
@ -176,9 +251,7 @@ Panel.prototype = {
Main.overview.connect('showing', Lang.bind(this.button, this.button.pressIn)); Main.overview.connect('showing', Lang.bind(this.button, this.button.pressIn));
Main.overview.connect('hiding', Lang.bind(this.button, this.button.release)); Main.overview.connect('hiding', Lang.bind(this.button, this.button.release));
this.actor.add_actor(box); Main.chrome.addActor(this.actor);
Main.chrome.addActor(this.actor, box);
Main.chrome.setVisibleInOverview(this.actor, true); Main.chrome.setVisibleInOverview(this.actor, true);
// Start the clock // Start the clock