diff --git a/data/Makefile.am b/data/Makefile.am
index 912ad3dbe..eef11bd01 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -17,7 +17,6 @@ dist_images_DATA = \
add-workspace.svg \
app-well-glow.png \
close-black.svg \
- info.svg \
magnifier.svg \
remove-workspace.svg
@@ -25,6 +24,7 @@ themedir = $(pkgdatadir)/theme
dist_theme_DATA = \
theme/gnome-shell.css \
theme/close.svg \
+ theme/close-window.svg \
theme/scroll-button-down.png \
theme/scroll-button-down-hover.png \
theme/scroll-button-up.png \
diff --git a/data/info.svg b/data/info.svg
deleted file mode 100644
index 41b5066a9..000000000
--- a/data/info.svg
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/data/theme/close-window.svg b/data/theme/close-window.svg
new file mode 100644
index 000000000..a15eade3b
--- /dev/null
+++ b/data/theme/close-window.svg
@@ -0,0 +1,76 @@
+
+
+
\ No newline at end of file
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 922a7b418..efcc65442 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -70,17 +70,76 @@ StTooltip {
/* Panel */
+#panel {
+ color: #ffffff;
+ font-size: 16px;
+ background-gradient-direction: vertical;
+ background-gradient-start: #161616;
+ background-gradient-end: #000000;
+}
+
+#panelLeft, #panelCenter, #panelRight {
+ spacing: 4px;
+}
+
+#panelLeft {
+ padding-right: 4px;
+}
+
+#panelRight {
+ padding-left: 4px;
+}
+
+.panel-button:pressed {
+ background-color: rgba(50,76,111,0.98);
+ border-radius: 4px;
+}
+
+#appMenu {
+ spacing: 4px;
+}
+
+.app-menu-icon {
+ width: 24px;
+ height: 24px;
+}
+
.panel-button {
padding: 4px 12px 3px;
border-radius: 5px;
font: 16px sans-serif;
- color: white;
}
.panel-button:active, .panel-button:checked {
background-color: #314a6c;
}
+#panelStatus {
+ spacing: 4px;
+}
+
+/* Overlay */
+
+.workspaces {
+ color: white;
+}
+
+.window-caption {
+ background: rgba(0,0,0,0.8);
+ border: 1px solid rgba(128,128,128,0.40);
+ border-radius: 10px;
+ font-size: 12px;
+ padding: 2px 8px;
+ -shell-caption-spacing: 4px;
+}
+
+.window-close {
+ background-image: url("close-window.svg");
+ height: 24px;
+ width: 24px;
+ -shell-close-overlap: 16px;
+}
+
/* Dash */
#dash {
@@ -119,8 +178,9 @@ StTooltip {
.section-header {
border: 1px solid #262626;
- -shell-gradient-top: #161616;
- -shell-gradient-bottom: #000000;
+ background-gradient-direction: vertical;
+ background-gradient-start: #161616;
+ background-gradient-end: #000000;
font-weight: bold;
font-size: 12px;
}
@@ -213,12 +273,56 @@ StTooltip {
font-weight: bold;
}
-/* AppIcon */
+/* Apps */
-.app-icon-label {
+#dashAppWell {
+ spacing: 2px;
+ -shell-grid-item-size: 74px;
+}
+
+.app-well-app {
+ border: 1px solid #080808;
+ border-radius: 2px;
+ padding: 2px;
+ width: 74px;
+ height: 74px;
font-size: 12px;
}
+.app-well-app:hover {
+ border: 1px solid #202020;
+}
+
+.app-well-app:active {
+ background-color: #1e1e1e;
+ border: 1px solid #5f5f5f;
+}
+
+.app-well-app-glow {
+ -shell-glow-extend-vertical: 3px;
+ -shell-glow-shrink-horizontal: 3px;
+}
+
+.app-well-menu {
+ border: 1px solid #5f5f5f;
+ border-radius: 4px;
+ padding: 4px;
+ background-color: rgba(0,0,0,0.9);
+ color: #ffffff;
+ -shell-arrow-width: 12px;
+ -shell-menu-spacing: 4px;
+}
+
+.app-well-menu-item:hover {
+ background-color: #1e1e1e;
+}
+
+.app-well-menu-separator {
+ padding-top: 1px;
+ border-bottom: 1px solid #5f5f5f;
+ height: 1px;
+}
+
/* Places */
.places-actions {
@@ -371,9 +475,3 @@ StTooltip {
background: rgba(255,255,255,0.33);
}
-/* Status Menu */
-#StatusMenu {
- spacing: 4px;
- font: 16px sans-serif;
- color: white;
-}
diff --git a/js/misc/Makefile.am b/js/misc/Makefile.am
index 084a45cad..9118b1f5c 100644
--- a/js/misc/Makefile.am
+++ b/js/misc/Makefile.am
@@ -2,4 +2,5 @@ jsmiscdir = $(pkgdatadir)/js/misc
dist_jsmisc_DATA = \
docInfo.js \
- format.js
+ format.js \
+ params.js
diff --git a/js/misc/params.js b/js/misc/params.js
new file mode 100644
index 000000000..77c51b9e4
--- /dev/null
+++ b/js/misc/params.js
@@ -0,0 +1,33 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+// parse:
+// @params: caller-provided parameter object, or %null
+// @default: function-provided defaults object
+// @allowExtras: whether or not to allow properties not in @default
+//
+// Examines @params and fills in default values from @defaults for
+// any properties in @defaults that don't appear in @params. If
+// @allowExtras is not %true, it will throw an error if @params
+// contains any properties that aren't in @defaults.
+//
+// If @params is %null, this returns @defaults.
+//
+// Return value: the updated params
+function parse(params, defaults, allowExtras) {
+ if (!params)
+ return defaults;
+
+ if (!allowExtras) {
+ for (let prop in params) {
+ if (!(prop in defaults))
+ throw new Error('Unrecognized parameter "' + prop + '"');
+ }
+ }
+
+ for (let prop in defaults) {
+ if (!(prop in params))
+ params[prop] = defaults[prop];
+ }
+
+ return params;
+}
\ No newline at end of file
diff --git a/js/ui/Makefile.am b/js/ui/Makefile.am
index f303aa685..270c0ebf6 100644
--- a/js/ui/Makefile.am
+++ b/js/ui/Makefile.am
@@ -4,8 +4,6 @@ dist_jsui_DATA = \
altTab.js \
appDisplay.js \
appFavorites.js \
- appIcon.js \
- button.js \
calendar.js \
chrome.js \
dash.js \
diff --git a/js/ui/altTab.js b/js/ui/altTab.js
index dd7726631..3ed39f8c5 100644
--- a/js/ui/altTab.js
+++ b/js/ui/altTab.js
@@ -11,7 +11,6 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
-const AppIcon = imports.ui.appIcon;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@@ -438,25 +437,20 @@ SwitcherList.prototype = {
},
addItem : function(item) {
- // We want the St.Bin's padding to be clickable (since it will
- // be part of the highlighted background color), so we put the
- // bin inside the ButtonBox rather than vice versa.
- let bin = new St.Bin({ style_class: 'item-box' });
- let bbox = new Shell.ButtonBox({ reactive: true });
+ let bbox = new St.Clickable({ style_class: 'item-box',
+ reactive: true });
- bin.add_actor(item);
- bbox.append(bin, Big.BoxPackFlags.NONE);
+ bbox.set_child(item);
this._list.add_actor(bbox);
let n = this._items.length;
- bbox.connect('activate', Lang.bind(this, function () {
+ bbox.connect('clicked', Lang.bind(this, function () {
this._itemActivated(n);
}));
bbox.connect('enter-event', Lang.bind(this, function () {
this._itemEntered(n);
}));
- bbox._bin = bin;
this._items.push(bbox);
},
@@ -468,15 +462,15 @@ SwitcherList.prototype = {
highlight: function(index, justOutline) {
if (this._highlighted != -1)
- this._items[this._highlighted]._bin.style_class = 'item-box';
+ this._items[this._highlighted].style_class = 'item-box';
this._highlighted = index;
if (this._highlighted != -1) {
if (justOutline)
- this._items[this._highlighted]._bin.style_class = 'outlined-item-box';
+ this._items[this._highlighted].style_class = 'outlined-item-box';
else
- this._items[this._highlighted]._bin.style_class = 'selected-item-box';
+ this._items[this._highlighted].style_class = 'selected-item-box';
}
},
@@ -588,6 +582,22 @@ SwitcherList.prototype = {
Signals.addSignalMethods(SwitcherList.prototype);
+function AppIcon(app) {
+ this._init(app);
+}
+
+AppIcon.prototype = {
+ _init: function(app) {
+ this.app = app;
+ this.actor = new St.BoxLayout({ style_class: "alt-tab-app",
+ vertical: true });
+ this._icon = this.app.create_icon_texture(POPUP_APPICON_SIZE);
+ this.actor.add(this._icon, { x_fill: false, y_fill: false });
+ this._label = new St.Label({ text: this.app.get_name() });
+ this.actor.add(this._label, { x_fill: false });
+ }
+}
+
function AppSwitcher(apps) {
this._init(apps);
}
@@ -603,8 +613,7 @@ AppSwitcher.prototype = {
let workspaceIcons = [];
let otherIcons = [];
for (let i = 0; i < apps.length; i++) {
- let appIcon = new AppIcon.AppIcon({ app: apps[i],
- size: POPUP_APPICON_SIZE });
+ let appIcon = new AppIcon(apps[i]);
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = appIcon.app.get_windows();
@@ -681,12 +690,8 @@ AppSwitcher.prototype = {
this.icons.push(appIcon);
this.addItem(appIcon.actor);
- // SwitcherList creates its own Shell.ButtonBox; we want to
- // avoid intercepting the events it wants.
- appIcon.actor.reactive = false;
-
let n = this._arrows.length;
- let arrow = new Shell.DrawingArea();
+ let arrow = new St.DrawingArea();
arrow.connect('redraw', Lang.bind(this,
function (area, texture) {
Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index c88191016..8bf359d08 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -9,28 +9,19 @@ const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
+const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const AppFavorites = imports.ui.appFavorites;
-const AppIcon = imports.ui.appIcon;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
const Workspaces = imports.ui.workspaces;
-const ENTERED_MENU_COLOR = new Clutter.Color();
-ENTERED_MENU_COLOR.from_pixel(0x00ff0022);
-
-const WELL_DEFAULT_COLUMNS = 4;
-const WELL_ITEM_MIN_HSPACING = 4;
-const WELL_ITEM_VSPACING = 4;
-
-const MENU_ARROW_SIZE = 12;
-const MENU_SPACING = 7;
-
-const MAX_ITEMS = 30;
+const APPICON_SIZE = 48;
+const WELL_MAX_COLUMNS = 8;
/* This class represents a single display item containing information about an application.
*
@@ -86,79 +77,6 @@ AppDisplayItem.prototype = {
}
};
-const MENU_UNSELECTED = 0;
-const MENU_SELECTED = 1;
-const MENU_ENTERED = 2;
-
-function MenuItem(name, id) {
- this._init(name, id);
-}
-
-/**
- * MenuItem:
- * Shows the list of menus in the sidebar.
- */
-MenuItem.prototype = {
- _init: function(name, id) {
- this.id = id;
-
- this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
- spacing: 4,
- corner_radius: 4,
- padding_right: 4,
- padding_left: 4,
- reactive: true });
- this.actor.connect('button-press-event', Lang.bind(this, function (a, e) {
- this.setState(MENU_SELECTED);
- }));
-
- this._text = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
- font_name: "Sans 14px",
- text: name });
-
- // We use individual boxes for the label and the arrow to ensure that they
- // are aligned vertically. Just setting y_align: Big.BoxAlignment.CENTER
- // on this.actor does not seem to achieve that.
- let labelBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER,
- padding: 4 });
-
- labelBox.append(this._text, Big.BoxPackFlags.NONE);
-
- this.actor.append(labelBox, Big.BoxPackFlags.EXPAND);
-
- let arrowBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
-
- this._arrow = new Shell.Arrow({ surface_width: MENU_ARROW_SIZE,
- surface_height: MENU_ARROW_SIZE,
- direction: Gtk.ArrowType.RIGHT,
- opacity: 0 });
- arrowBox.append(this._arrow, Big.BoxPackFlags.NONE);
- this.actor.append(arrowBox, Big.BoxPackFlags.NONE);
- },
-
- getState: function() {
- return this._state;
- },
-
- setState: function (state) {
- if (state == this._state)
- return;
- this._state = state;
- if (this._state == MENU_UNSELECTED) {
- this.actor.background_color = null;
- this._arrow.set_opacity(0);
- } else if (this._state == MENU_ENTERED) {
- this.actor.background_color = ENTERED_MENU_COLOR;
- this._arrow.set_opacity(0xFF/2);
- } else {
- this.actor.background_color = GenericDisplay.ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
- this._arrow.set_opacity(0xFF);
- }
- this.emit('state-changed')
- }
-}
-Signals.addSignalMethods(MenuItem.prototype);
-
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their popularity by default, and based on
* their name if some search filter is applied.
@@ -302,42 +220,203 @@ AppDisplay.prototype = {
Signals.addSignalMethods(AppDisplay.prototype);
-function BaseWellItem(app, isFavorite, hasMenu) {
- this._init(app, isFavorite, hasMenu);
+
+function BaseWellItem(app, isFavorite) {
+ this._init(app, isFavorite);
}
BaseWellItem.prototype = {
- __proto__: AppIcon.AppIcon.prototype,
+ _init : function(app, isFavorite) {
+ this.app = app;
- _init: function(app, isFavorite) {
- AppIcon.AppIcon.prototype._init.call(this, { app: app,
- menuType: AppIcon.MenuType.ON_RIGHT,
- glow: true });
+ this._glowExtendVertical = 0;
+ this._glowShrinkHorizontal = 0;
- this.isFavorite = isFavorite;
+ this.actor = new St.Clickable({ style_class: 'app-well-app',
+ reactive: true });
+ this.actor._delegate = this;
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
+
+ let box = new St.BoxLayout({ vertical: true });
+ this.actor.set_child(box);
+
+ this.actor.connect('clicked', Lang.bind(this, this._onClicked));
+
+ this._menu = null;
+
+ this.icon = this.app.create_icon_texture(APPICON_SIZE);
+
+ box.add(this.icon, { expand: true, x_fill: false, y_fill: false });
+
+ let nameBox = new Shell.GenericContainer();
+ nameBox.connect('get-preferred-width', Lang.bind(this, this._nameBoxGetPreferredWidth));
+ nameBox.connect('get-preferred-height', Lang.bind(this, this._nameBoxGetPreferredHeight));
+ nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate));
+ this._nameBox = nameBox;
+
+ this._name = new St.Label({ text: this.app.get_name() });
+ this._name.clutter_text.line_alignment = Pango.Alignment.CENTER;
+ nameBox.add_actor(this._name);
+ this._glowBox = new St.BoxLayout({ style_class: 'app-well-app-glow' });
+ this._glowBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
+ this._nameBox.add_actor(this._glowBox);
+ this._glowBox.lower(this._name);
+ this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._rerenderGlow));
+ this._rerenderGlow();
+
+ box.add(nameBox);
this._draggable = DND.makeDraggable(this.actor, true);
+ this._dragStartX = null;
+ this._dragStartY = null;
- // Do these as anonymous functions to avoid conflict with handlers in subclasses
- this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
- let [stageX, stageY] = event.get_coords();
- this._dragStartX = stageX;
- this._dragStartY = stageY;
- return false;
- }));
- this.actor.connect('notify::hover', Lang.bind(this, function () {
- let hover = this.actor.hover;
- if (!hover) {
- if (this.actor.pressed && this._dragStartX != null) {
- this.actor.fake_release();
- this._draggable.startDrag(this._dragStartX, this._dragStartY,
- Main.currentTime());
- } else {
- this._dragStartX = null;
- this._dragStartY = null;
- }
+ this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
+ this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChange));
+ },
+
+ _nameBoxGetPreferredWidth: function (nameBox, forHeight, alloc) {
+ let [min, natural] = this._name.get_preferred_width(forHeight);
+ alloc.min_size = min;
+ alloc.natural_size = natural;
+ },
+
+ _nameBoxGetPreferredHeight: function (nameBox, forWidth, alloc) {
+ let [min, natural] = this._name.get_preferred_height(forWidth);
+ alloc.min_size = min + this._glowExtendVertical * 2;
+ alloc.natural_size = natural + this._glowExtendVertical * 2;
+ },
+
+ _nameBoxAllocate: function (nameBox, box, flags) {
+ let childBox = new Clutter.ActorBox();
+ let [minWidth, naturalWidth] = this._name.get_preferred_width(-1);
+ let [minHeight, naturalHeight] = this._name.get_preferred_height(-1);
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+ let targetWidth = availWidth;
+ let xPadding = 0;
+ if (naturalWidth < availWidth) {
+ xPadding = Math.floor((availWidth - naturalWidth) / 2);
+ }
+ childBox.x1 = xPadding;
+ childBox.x2 = availWidth - xPadding;
+ childBox.y1 = this._glowExtendVertical;
+ childBox.y2 = availHeight - this._glowExtendVertical;
+ this._name.allocate(childBox, flags);
+
+ // Now the glow
+ let glowPaddingHoriz = Math.max(0, xPadding - this._glowShrinkHorizontal);
+ glowPaddingHoriz = Math.max(this._glowShrinkHorizontal, glowPaddingHoriz);
+ childBox.x1 = glowPaddingHoriz;
+ childBox.x2 = availWidth - glowPaddingHoriz;
+ childBox.y1 = 0;
+ childBox.y2 = availHeight;
+ this._glowBox.allocate(childBox, flags);
+ },
+
+ _onDestroy: function() {
+ if (this._appWindowChangedId > 0)
+ this.app.disconnect(this._appWindowChangedId);
+ },
+
+ _onMapped: function() {
+ if (!this._queuedGlowRerender)
+ return;
+ this._queuedGlowRerender = false;
+ this._rerenderGlow();
+ },
+
+ _rerenderGlow: function() {
+ if (!this.actor.mapped) {
+ this._queuedGlowRerender = true;
+ return;
+ }
+ this._glowBox.destroy_children();
+ let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
+ let windows = this.app.get_windows();
+ for (let i = 0; i < windows.length && i < 3; i++) {
+ let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
+ glowPath, -1, -1);
+ glow.keep_aspect_ratio = false;
+ this._glowBox.add(glow);
+ }
+ },
+
+ _onButtonPress: function(actor, event) {
+ let [stageX, stageY] = event.get_coords();
+ this._dragStartX = stageX;
+ this._dragStartY = stageY;
+ },
+
+ _onHoverChange: function(actor) {
+ let hover = this.actor.hover;
+ if (!hover) {
+ if (this.actor.pressed && this._dragStartX != null) {
+ this.actor.fake_release();
+ this._draggable.startDrag(this._dragStartX, this._dragStartY,
+ Main.currentTime());
+ } else {
+ this._dragStartX = null;
+ this._dragStartY = null;
}
- }));
+ }
+ },
+
+ _onClicked: function(actor, event) {
+ let button = event.get_button();
+ if (button == 1) {
+ this._onActivate(event);
+ } else if (button == 3) {
+ // Don't bind to the right click here; we want left click outside the
+ // area to deactivate as well.
+ this.popupMenu(0);
+ }
+ return false;
+ },
+
+ _onStyleChanged: function() {
+ let themeNode = this._glowBox.get_theme_node();
+
+ let success, len;
+ [success, len] = themeNode.get_length('-shell-glow-extend-vertical', false);
+ if (success)
+ this._glowExtendVertical = len;
+ [success, len] = themeNode.get_length('-shell-glow-shrink-horizontal', false);
+ if (success)
+ this._glowShrinkHorizontal = len;
+ this.actor.queue_relayout();
+ },
+
+ popupMenu: function(activatingButton) {
+ if (!this._menu) {
+ this._menu = new AppIconMenu(this);
+ this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
+ this.highlightWindow(window);
+ }));
+ this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
+ this.activateWindow(window);
+ }));
+ this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
+ if (isPoppedUp) {
+ this._onMenuPoppedUp();
+ } else {
+ this._onMenuPoppedDown();
+ }
+ }));
+ }
+
+ this._menu.popup(activatingButton);
+
+ return false;
+ },
+
+ // Default implementations; AppDisplay.RunningWellItem overrides these
+ highlightWindow: function(window) {
+ this.emit('highlight-window', window);
+ },
+
+ activateWindow: function(window) {
+ this.emit('activate-window', window);
},
shellWorkspaceLaunch : function() {
@@ -353,7 +432,7 @@ BaseWellItem.prototype = {
},
getDragActor: function() {
- return this.createDragActor();
+ return this.app.create_icon_texture(APPICON_SIZE);
},
// Returns the original icon that is being used as a source for the cloned texture
@@ -362,6 +441,305 @@ BaseWellItem.prototype = {
return this.actor;
}
}
+Signals.addSignalMethods(BaseWellItem.prototype);
+
+function AppIconMenu(source) {
+ this._init(source);
+}
+
+AppIconMenu.prototype = {
+ _init: function(source) {
+ this._source = source;
+
+ this._arrowSize = 4; // CSS default
+ this._spacing = 0; // CSS default
+
+ this._dragStartX = 0;
+ this._dragStartY = 0;
+
+ this.actor = new Shell.GenericContainer({ reactive: true });
+ 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._windowContainerBox = new St.Bin({ style_class: 'app-well-menu' });
+ this._windowContainer = new Shell.Menu({ orientation: Big.BoxOrientation.VERTICAL,
+ width: Main.overview._dash.actor.width });
+ this._windowContainerBox.set_child(this._windowContainer);
+ this._windowContainer.connect('unselected', Lang.bind(this, this._onItemUnselected));
+ this._windowContainer.connect('selected', Lang.bind(this, this._onItemSelected));
+ this._windowContainer.connect('cancelled', Lang.bind(this, this._onWindowSelectionCancelled));
+ this._windowContainer.connect('activate', Lang.bind(this, this._onItemActivate));
+ this.actor.add_actor(this._windowContainerBox);
+
+ // Stay popped up on release over application icon
+ this._windowContainer.set_persistent_source(this._source.actor);
+
+ // Intercept events while the menu has the pointer grab to do window-related effects
+ this._windowContainer.connect('enter-event', Lang.bind(this, this._onMenuEnter));
+ this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
+ this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
+
+ this._borderColor = new Clutter.Color();
+ this._backgroundColor = new Clutter.Color();
+ this._windowContainerBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
+
+ this._arrow = new St.DrawingArea();
+ this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
+ Shell.draw_box_pointer(texture,
+ Shell.PointerDirection.LEFT,
+ this._borderColor,
+ this._backgroundColor);
+ }));
+ this.actor.add_actor(this._arrow);
+
+ // Chain our visibility and lifecycle to that of the source
+ source.actor.connect('notify::mapped', Lang.bind(this, function () {
+ if (!source.actor.mapped)
+ this._windowContainer.popdown();
+ }));
+ source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
+
+ global.stage.add_actor(this.actor);
+ },
+
+ _getPreferredWidth: function(actor, forHeight, alloc) {
+ let [min, natural] = this._windowContainerBox.get_preferred_width(forHeight);
+ min += this._arrowSize;
+ natural += this._arrowSize;
+ alloc.min_size = min;
+ alloc.natural_size = natural;
+ },
+
+ _getPreferredHeight: function(actor, forWidth, alloc) {
+ let [min, natural] = this._windowContainerBox.get_preferred_height(forWidth);
+ alloc.min_size = min;
+ alloc.natural_size = natural;
+ },
+
+ _allocate: function(actor, box, flags) {
+ let childBox = new Clutter.ActorBox();
+ let themeNode = this._windowContainerBox.get_theme_node();
+
+ let width = box.x2 - box.x1;
+ let height = box.y2 - box.y1;
+
+ childBox.x1 = 0;
+ childBox.x2 = this._arrowSize;
+ childBox.y1 = Math.floor((height / 2) - (this._arrowSize / 2));
+ childBox.y2 = childBox.y1 + this._arrowSize;
+ this._arrow.allocate(childBox, flags);
+
+ // Ensure the arrow is above the border area
+ let border = themeNode.get_border_width(St.Side.LEFT);
+ childBox.x1 = this._arrowSize - border;
+ childBox.x2 = width;
+ childBox.y1 = 0;
+ childBox.y2 = height;
+ this._windowContainerBox.allocate(childBox, flags);
+ },
+
+ _redisplay: function() {
+ this._windowContainer.remove_all();
+
+ let windows = this._source.app.get_windows();
+
+ this._windowContainer.show();
+
+ let iconsDiffer = false;
+ let texCache = Shell.TextureCache.get_default();
+ if (windows.length > 0) {
+ let firstIcon = windows[0].mini_icon;
+ for (let i = 1; i < windows.length; i++) {
+ if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
+ iconsDiffer = true;
+ break;
+ }
+ }
+ }
+
+ // Display the app windows menu items and the separator between windows
+ // of the current desktop and other windows.
+ let activeWorkspace = global.screen.get_active_workspace();
+ let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
+
+ for (let i = 0; i < windows.length; i++) {
+ if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
+ this._appendSeparator();
+ separatorShown = true;
+ }
+ let box = this._appendMenuItem(windows[i].title);
+ box._window = windows[i];
+ }
+
+ if (windows.length > 0)
+ this._appendSeparator();
+
+ let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
+
+ this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
+
+ if (windows.length > 0)
+ this._appendSeparator();
+ this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
+ : _("Add to Favorites"));
+
+ this._highlightedItem = null;
+ },
+
+ _appendSeparator: function () {
+ let bin = new St.Bin({ style_class: "app-well-menu-separator" });
+ this._windowContainer.append_separator(bin, Big.BoxPackFlags.NONE);
+ },
+
+ _appendMenuItem: function(labelText) {
+ let box = new St.BoxLayout({ style_class: 'app-well-menu-item',
+ reactive: true });
+ let label = new St.Label({ text: labelText });
+ box.add(label);
+ this._windowContainer.append(box, Big.BoxPackFlags.NONE);
+ return box;
+ },
+
+ popup: function(activatingButton) {
+ let [stageX, stageY] = this._source.actor.get_transformed_position();
+ let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
+
+ this._redisplay();
+
+ this._windowContainer.popup(activatingButton, Main.currentTime());
+
+ this.emit('popup', true);
+
+ let x, y;
+ x = Math.floor(stageX + stageWidth);
+ y = Math.floor(stageY + (stageHeight / 2) - (this.actor.height / 2));
+
+ this.actor.set_position(x, y);
+ this.actor.show();
+ },
+
+ popdown: function() {
+ this._windowContainer.popdown();
+ this.emit('popup', false);
+ this.actor.hide();
+ },
+
+ selectWindow: function(metaWindow) {
+ this._selectMenuItemForWindow(metaWindow);
+ },
+
+ _findMetaWindowForActor: function (actor) {
+ if (actor._delegate instanceof Workspaces.WindowClone)
+ return actor._delegate.metaWindow;
+ else if (actor.get_meta_window)
+ return actor.get_meta_window();
+ return null;
+ },
+
+ // This function is called while the menu has a pointer grab; what we want
+ // to do is see if the mouse was released over a window representation
+ _onMenuButtonRelease: function (actor, event) {
+ let metaWindow = this._findMetaWindowForActor(event.get_source());
+ if (metaWindow) {
+ this.emit('activate-window', metaWindow);
+ }
+ },
+
+ _updateHighlight: function (item) {
+ if (this._highlightedItem) {
+ this._highlightedItem.set_style_pseudo_class(null);
+ this.emit('highlight-window', null);
+ }
+ this._highlightedItem = item;
+ if (this._highlightedItem) {
+ item.set_style_pseudo_class('hover');
+ let window = this._highlightedItem._window;
+ if (window)
+ this.emit('highlight-window', window);
+ }
+ },
+
+ _selectMenuItemForWindow: function (metaWindow) {
+ let children = this._windowContainer.get_children();
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ let menuMetaWindow = child._window;
+ if (menuMetaWindow == metaWindow)
+ this._updateHighlight(child);
+ }
+ },
+
+ // Called while menu has a pointer grab
+ _onMenuEnter: function (actor, event) {
+ let metaWindow = this._findMetaWindowForActor(event.get_source());
+ if (metaWindow) {
+ this._selectMenuItemForWindow(metaWindow);
+ }
+ },
+
+ // Called while menu has a pointer grab
+ _onMenuLeave: function (actor, event) {
+ let metaWindow = this._findMetaWindowForActor(event.get_source());
+ if (metaWindow) {
+ this._updateHighlight(null);
+ }
+ },
+
+ _onItemUnselected: function (actor, child) {
+ this._updateHighlight(null);
+ },
+
+ _onItemSelected: function (actor, child) {
+ this._updateHighlight(child);
+ },
+
+ _onItemActivate: function (actor, child) {
+ if (child._window) {
+ let metaWindow = child._window;
+ this.emit('activate-window', metaWindow);
+ } else if (child == this._newWindowMenuItem) {
+ this._source.app.launch();
+ this.emit('activate-window', null);
+ } else if (child == this._toggleFavoriteMenuItem) {
+ let favs = AppFavorites.getAppFavorites();
+ let isFavorite = favs.isFavorite(this._source.app.get_id());
+ if (isFavorite)
+ favs.removeFavorite(this._source.app.get_id());
+ else
+ favs.addFavorite(this._source.app.get_id());
+ }
+ this.popdown();
+ },
+
+ _onWindowSelectionCancelled: function () {
+ this.emit('highlight-window', null);
+ this.popdown();
+ },
+
+ _onStyleChanged: function() {
+ let themeNode = this._windowContainerBox.get_theme_node();
+ let [success, len] = themeNode.get_length('-shell-arrow-size', false);
+ if (success) {
+ this._arrowSize = len;
+ this.actor.queue_relayout();
+ }
+ [success, len] = themeNode.get_length('-shell-menu-spacing', false)
+ if (success) {
+ this._windowContainer.spacing = len;
+ }
+ let color = new Clutter.Color();
+ if (themeNode.get_background_color(color)) {
+ this._backgroundColor = color;
+ color = new Clutter.Color();
+ }
+ if (themeNode.get_border_color(St.Side.LEFT, color)) {
+ this._borderColor = color;
+ }
+ this._arrow.emit_redraw();
+ }
+};
+Signals.addSignalMethods(AppIconMenu.prototype);
function RunningWellItem(app, isFavorite) {
this._init(app, isFavorite);
@@ -372,14 +750,9 @@ RunningWellItem.prototype = {
_init: function(app, isFavorite) {
BaseWellItem.prototype._init.call(this, app, isFavorite);
-
- this._dragStartX = 0;
- this._dragStartY = 0;
-
- this.actor.connect('activate', Lang.bind(this, this._onActivate));
},
- _onActivate: function (actor, event) {
+ _onActivate: function (event) {
let modifiers = Shell.get_event_state(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
@@ -406,11 +779,11 @@ RunningWellItem.prototype = {
Main.overview.hide();
},
- menuPoppedUp: function() {
+ _onMenuPoppedUp: function() {
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
},
- menuPoppedDown: function() {
+ _onMenuPoppedDown: function() {
if (this._didActivateWindow)
return;
@@ -427,25 +800,18 @@ InactiveWellItem.prototype = {
_init : function(app, isFavorite) {
BaseWellItem.prototype._init.call(this, app, isFavorite);
-
- this.actor.connect('notify::pressed', Lang.bind(this, this._onPressedChanged));
- this.actor.connect('activate', Lang.bind(this, this._onActivate));
},
- _onPressedChanged: function() {
- this.setHighlight(this.actor.pressed);
- },
-
- _onActivate: function() {
+ _onActivate: function(event) {
this.app.launch();
Main.overview.hide();
return true;
},
- menuPoppedUp: function() {
+ _onMenuPoppedUp: function() {
},
- menuPoppedDown: function() {
+ _onMenuPoppedDown: function() {
}
};
@@ -455,156 +821,123 @@ function WellGrid() {
WellGrid.prototype = {
_init: function() {
- this.actor = new Shell.GenericContainer();
+ this.actor = new St.Bin({ name: "dashAppWell" });
+ // Pulled from CSS, but hardcode some defaults here
+ this._spacing = 0;
+ this._item_size = 48;
+ this._grid = new Shell.GenericContainer();
+ this.actor.set_child(this._grid);
+ this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
- this._separator = new Big.Box({ height: 1 });
- this.actor.add_actor(this._separator);
- this._separatorIndex = 0;
- this._cachedSeparatorY = 0;
-
- 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._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+ this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+ this._grid.connect('allocate', Lang.bind(this, this._allocate));
},
_getPreferredWidth: function (grid, forHeight, alloc) {
- let [itemMin, itemNatural] = this._getItemPreferredWidth();
- let children = this._getItemChildren();
- let nColumns;
- if (children.length < WELL_DEFAULT_COLUMNS)
- nColumns = children.length;
- else
- nColumns = WELL_DEFAULT_COLUMNS;
- alloc.min_size = itemMin;
- alloc.natural_size = itemNatural * nColumns;
+ let children = this._grid.get_children();
+ let nColumns = children.length;
+ let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
+ // Kind of a lie, but not really an issue right now. If
+ // we wanted to support some sort of hidden/overflow that would
+ // need higher level design
+ alloc.min_size = this._item_size;
+ alloc.natural_size = nColumns * this._item_size + totalSpacing;
},
_getPreferredHeight: function (grid, forWidth, alloc) {
- let [rows, columns, itemWidth, itemHeight] = this._computeLayout(forWidth);
- let totalVerticalSpacing = Math.max(rows - 1, 0) * WELL_ITEM_VSPACING;
-
- let [separatorMin, separatorNatural] = this._separator.get_preferred_height(forWidth);
- alloc.min_size = alloc.natural_size = rows * itemHeight + totalVerticalSpacing + separatorNatural;
+ let children = this._grid.get_children();
+ let [nColumns, usedWidth] = this._computeLayout(forWidth);
+ let nRows;
+ if (nColumns > 0)
+ nRows = Math.ceil(children.length / nColumns);
+ else
+ nRows = 0;
+ let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
+ let height = nRows * this._item_size + totalSpacing;
+ alloc.min_size = height;
+ alloc.natural_size = height;
},
_allocate: function (grid, box, flags) {
- let children = this._getItemChildren();
+ let children = this._grid.get_children();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
- let [rows, columns, itemWidth, itemHeight] = this._computeLayout(availWidth);
+ let [nColumns, usedWidth] = this._computeLayout(availWidth);
- let [separatorMin, separatorNatural] = this._separator.get_preferred_height(-1);
+ let overallPaddingX = Math.floor((availWidth - usedWidth) / 2);
- let x = box.x1;
+ let x = box.x1 + overallPaddingX;
let y = box.y1;
let columnIndex = 0;
for (let i = 0; i < children.length; i++) {
- let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(-1);
+ let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
+ = children[i].get_preferred_size();
/* Center the item in its allocation horizontally */
- let width = Math.min(itemWidth, childNaturalWidth);
- let horizSpacing = (itemWidth - width) / 2;
+ let width = Math.min(this._item_size, childNaturalWidth);
+ let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
+ let height = Math.min(this._item_size, childNaturalHeight);
+ let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
let childBox = new Clutter.ActorBox();
- childBox.x1 = Math.floor(x + horizSpacing);
- childBox.y1 = y;
+ childBox.x1 = Math.floor(x + childXSpacing);
+ childBox.y1 = Math.floor(y + childYSpacing);
childBox.x2 = childBox.x1 + width;
- childBox.y2 = childBox.y1 + itemHeight;
+ childBox.y2 = childBox.y1 + height;
children[i].allocate(childBox, flags);
columnIndex++;
- if (columnIndex == columns) {
+ if (columnIndex == nColumns) {
columnIndex = 0;
}
if (columnIndex == 0) {
- y += itemHeight + WELL_ITEM_VSPACING;
- x = box.x1;
+ y += this._item_size + this._spacing;
+ x = box.x1 + overallPaddingX;
} else {
- x += itemWidth;
+ x += this._item_size + this._spacing;
}
}
},
- removeAll: function () {
- let itemChildren = this._getItemChildren();
- for (let i = 0; i < itemChildren.length; i++) {
- itemChildren[i].destroy();
- }
- },
-
- _getItemChildren: function () {
- let children = this.actor.get_children();
- children.shift();
- return children;
- },
-
_computeLayout: function (forWidth) {
- let [itemMinWidth, itemNaturalWidth] = this._getItemPreferredWidth();
- let columnsNatural;
- let i;
- let children = this._getItemChildren();
- if (children.length == 0)
- return [0, WELL_DEFAULT_COLUMNS, 0, 0];
+ let children = this._grid.get_children();
let nColumns = 0;
let usedWidth = 0;
- // Big.Box will allocate us at 0x0 if we are not visible; this is probably a
- // Big.Box bug but it can't be fixed because if children are skipped in allocate()
- // Clutter gets confused (see http://bugzilla.openedhand.com/show_bug.cgi?id=1831)
- if (forWidth <= 0) {
- nColumns = WELL_DEFAULT_COLUMNS;
- } else {
- while (nColumns < WELL_DEFAULT_COLUMNS &&
- nColumns < children.length &&
- usedWidth + itemMinWidth <= forWidth) {
- // By including WELL_ITEM_MIN_HSPACING in usedWidth, we are ensuring
- // that the number of columns we end up with will allow the spacing
- // between the columns to be at least that value.
- usedWidth += itemMinWidth + WELL_ITEM_MIN_HSPACING;
- nColumns++;
- }
+ while (nColumns < WELL_MAX_COLUMNS &&
+ nColumns < children.length &&
+ (usedWidth + this._item_size <= forWidth)) {
+ usedWidth += this._item_size + this._spacing;
+ nColumns += 1;
}
- if (nColumns == 0) {
- log("WellGrid: couldn't fit a column in width " + forWidth);
- /* FIXME - fall back to smaller icon size */
- }
+ if (nColumns > 0)
+ usedWidth -= this._spacing;
- let minWidth = itemMinWidth * nColumns;
-
- let lastColumnIndex = nColumns - 1;
- let rows = Math.ceil(children.length / nColumns);
-
- let itemWidth;
- if (forWidth <= 0) {
- itemWidth = itemNaturalWidth;
- } else {
- itemWidth = Math.floor(forWidth / nColumns);
- }
-
- let itemNaturalHeight = 0;
- for (let i = 0; i < children.length; i++) {
- let [childMin, childNatural] = children[i].get_preferred_height(itemWidth);
- if (childNatural > itemNaturalHeight)
- itemNaturalHeight = childNatural;
- }
-
- return [rows, nColumns, itemWidth, itemNaturalHeight];
+ return [nColumns, usedWidth];
},
- _getItemPreferredWidth: function () {
- let children = this._getItemChildren();
- let minWidth = 0;
- let naturalWidth = 0;
- for (let i = 0; i < children.length; i++) {
- let [childMin, childNatural] = children[i].get_preferred_width(-1);
- if (childMin > minWidth)
- minWidth = childMin;
- if (childNatural > naturalWidth)
- naturalWidth = childNatural;
- }
- return [minWidth, naturalWidth];
+ _onStyleChanged: function() {
+ let themeNode = this.actor.get_theme_node();
+ let [success, len] = themeNode.get_length('spacing', false);
+ if (success)
+ this._spacing = len;
+ [success, len] = themeNode.get_length('-shell-grid-item-size', false);
+ if (success)
+ this._item_size = len;
+ this._grid.queue_relayout();
+ },
+
+ removeAll: function () {
+ this._grid.get_children().forEach(Lang.bind(this, function (child) {
+ child.destroy();
+ }));
+ },
+
+ addItem: function(actor) {
+ this._grid.add_actor(actor);
}
}
@@ -671,6 +1004,7 @@ AppWell.prototype = {
let running = this._tracker.get_running_apps(contextId);
let runningIds = this._appIdListToHash(running);
+ let nFavorites = 0;
for (let id in favorites) {
let app = favorites[id];
let display;
@@ -679,7 +1013,8 @@ AppWell.prototype = {
} else {
display = new InactiveWellItem(app, true);
}
- this._grid.actor.add_actor(display.actor);
+ this._grid.addItem(display.actor);
+ nFavorites++;
}
for (let i = 0; i < running.length; i++) {
@@ -687,14 +1022,12 @@ AppWell.prototype = {
if (app.get_id() in favorites)
continue;
let display = new RunningWellItem(app, false);
- this._grid.actor.add_actor(display.actor);
+ this._grid.addItem(display.actor);
}
- if (this._grid.actor.get_n_children() == 1) {
- let text = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
- font_name: "Sans 14px",
- text: _("Drag here to add favorites")});
- this._grid.actor.add_actor(text);
+ if (running.length == 0 && nFavorites == 0) {
+ let text = new St.Label({ text: _("Drag here to add favorites")});
+ this._grid.actor.set_child(text);
}
},
diff --git a/js/ui/appIcon.js b/js/ui/appIcon.js
index 571e05097..5202a1da6 100644
--- a/js/ui/appIcon.js
+++ b/js/ui/appIcon.js
@@ -307,7 +307,7 @@ AppIconMenu.prototype = {
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
- this._arrow = new Shell.DrawingArea();
+ this._arrow = new St.DrawingArea();
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
Shell.draw_box_pointer(texture,
this._type == MenuType.ON_RIGHT ? Shell.PointerDirection.LEFT : Shell.PointerDirection.UP,
diff --git a/js/ui/button.js b/js/ui/button.js
deleted file mode 100644
index 7cae476af..000000000
--- a/js/ui/button.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
-
-const Big = imports.gi.Big;
-const Clutter = imports.gi.Clutter;
-const Lang = imports.lang;
-const Mainloop = imports.mainloop;
-const Shell = imports.gi.Shell;
-const Signals = imports.signals;
-const Tweener = imports.ui.tweener;
-
-const DEFAULT_BUTTON_COLOR = new Clutter.Color();
-DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
-
-const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
-DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaa66);
-
-const DEFAULT_TEXT_COLOR = new Clutter.Color();
-DEFAULT_TEXT_COLOR.from_pixel(0x000000ff);
-
-const DEFAULT_FONT = 'Sans Bold 16px';
-
-// Padding on the left and right side of the button.
-const SIDE_PADDING = 14;
-
-function Button(widget, buttonColor, pressedButtonColor, textColor, font) {
- this._init(widget, buttonColor, pressedButtonColor, textColor, font);
-}
-
-Button.prototype = {
- _init : function(widgetOrText, buttonColor, pressedButtonColor, textColor, font) {
- this._buttonColor = buttonColor
- if (buttonColor == null)
- this._buttonColor = DEFAULT_BUTTON_COLOR;
-
- this._pressedButtonColor = pressedButtonColor
- if (pressedButtonColor == null)
- this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR;
-
- this._textColor = textColor;
- if (textColor == null)
- this._textColor = DEFAULT_TEXT_COLOR;
-
- this._font = font;
- if (font == null)
- this._font = DEFAULT_FONT;
-
- this._isBetweenPressAndRelease = false;
- this._mouseIsOverButton = false;
-
- this.actor = new Shell.ButtonBox({ reactive: true,
- corner_radius: 5,
- padding_left: SIDE_PADDING,
- padding_right: SIDE_PADDING,
- orientation: Big.BoxOrientation.HORIZONTAL,
- y_align: Big.BoxAlignment.CENTER
- });
- if (typeof widgetOrText == 'string') {
- this._widget = new Clutter.Text({ font_name: this._font,
- color: this._textColor,
- text: widgetOrText });
- } else {
- this._widget = widgetOrText;
- }
-
- this.actor.append(this._widget, Big.BoxPackFlags.EXPAND);
-
- this.actor.connect('notify::hover', Lang.bind(this, this._updateColors));
- this.actor.connect('notify::pressed', Lang.bind(this, this._updateColors));
- this.actor.connect('notify::active', Lang.bind(this, this._updateColors));
- },
-
- _updateColors : function() {
- if (this.actor.active || this.actor.pressed)
- this.actor.backgroundColor = this._pressedButtonColor;
- else if (this.actor.hover)
- this.actor.backgroundColor = this._buttonColor;
- else
- this.actor.backgroundColor = null;
- }
-};
-
-Signals.addSignalMethods(Button.prototype);
-
-/* Delay before the icon should appear, in seconds after the pointer has entered the parent */
-const ANIMATION_TIME = 0.25;
-
-/* This is an icon button that fades in/out when mouse enters/leaves the parent.
- * A delay is used before the fading starts. You can force it to be shown if needed.
- *
- * parent -- used to show/hide the button depending on mouse entering/leaving it
- * size -- size in pixels of both the button and the icon it contains
- * texture -- optional, must be used if the texture for the icon is already created (else, use setIconFromName)
- */
-function IconButton(parent, size, texture) {
- this._init(parent, size, texture);
-}
-
-IconButton.prototype = {
- _init : function(parent, size, texture) {
- this._size = size;
- if (texture)
- this.actor = texture;
- else
- this.actor = new Clutter.Texture({ width: this._size, height: this._size });
- this.actor.set_reactive(true);
- this.actor.set_opacity(0);
- parent.connect("enter-event", Lang.bind(this, function(actor, event) {
- this._shouldHide = false;
- // Nothing to do if the cursor has come back from a child of the parent actor
- if (actor.get_children().indexOf(event.get_related()) != -1)
- return;
-
- this._fadeIn();
- }));
- parent.connect("leave-event", Lang.bind(this, function(actor, event) {
- // Nothing to do if the cursor has merely entered a child of the parent actor
- if (actor.get_children().indexOf(event.get_related()) != -1)
- return;
-
- // Remember that we should not be visible to hide the button if forceShow is unset
- if (this._forceShow) {
- this._shouldHide = true;
- return;
- }
-
- this._fadeOut();
- }));
- },
-
- /// Private methods ///
-
- setIconFromName : function(iconName) {
- let iconTheme = Gtk.IconTheme.get_default();
- let iconInfo = iconTheme.lookup_icon(iconName, this._size, 0);
- if (!iconInfo)
- return;
-
- let iconPath = iconInfo.get_filename();
- this.actor.set_from_file(iconPath);
- },
-
- // Useful if we want to show the button immediately,
- // e.g. in case the mouse is already in the parent when the button is created
- show : function() {
- this.actor.set_opacity(255);
- },
-
- // If show is true, prevents the button from fading out
- forceShow : function(show) {
- this._forceShow = show;
- // Hide the button if it should have been hidden under normal conditions
- if (!this._forceShow && this._shouldHide) {
- this._fadeOut();
- }
- },
-
- /// Private methods ///
-
- _fadeIn : function() {
- Tweener.removeTweens(this.actor);
- Tweener.addTween(this.actor, { opacity: 255,
- time: ANIMATION_TIME,
- transition :"easeInQuad" });
- },
-
- _fadeOut : function() {
- Tweener.removeTweens(this.actor);
- Tweener.addTween(this.actor, { opacity: 0,
- time: ANIMATION_TIME,
- transition :"easeOutQuad" });
- }
-};
diff --git a/js/ui/chrome.js b/js/ui/chrome.js
index e5cf650c4..daa4c4dd1 100644
--- a/js/ui/chrome.js
+++ b/js/ui/chrome.js
@@ -7,6 +7,7 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
+const Params = imports.misc.params;
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
@@ -54,7 +55,7 @@ Chrome.prototype = {
// addActor:
// @actor: an actor to add to the chrome layer
- // @shapeActor: optional "shape actor".
+ // @params: (optional) additional params
//
// Adds @actor to the chrome layer and extends the input region
// and window manager struts to include it. (Window manager struts
@@ -64,59 +65,45 @@ Chrome.prototype = {
// in its visibility will affect the input region, but NOT the
// struts.
//
- // If @shapeActor is provided, it will be used instead of @actor
- // for the input region/strut shape. (This lets you have things like
- // drop shadows in @actor that don't affect the struts.) It must
- // be a child of @actor. Alternatively, you can pass %null for
- // @shapeActor to indicate that @actor should not affect the input
- // region or struts at all.
- addActor: function(actor, shapeActor) {
- if (shapeActor === undefined)
- shapeActor = actor;
- else if (shapeActor && !this._verifyAncestry(shapeActor, actor))
- throw new Error('shapeActor is not a descendent of actor');
+ // If %visibleInOverview is %true in @params, @actor will remain
+ // visible when the overview is brought up. Otherwise it will
+ // automatically be hidden. If %affectsStruts or %affectsInputRegion
+ // is %false, the actor will not have the indicated effect.
+ addActor: function(actor, params) {
+ params = Params.parse(params, { visibleInOverview: false,
+ affectsStruts: true,
+ affectsInputRegion: true });
- this.nonOverviewActor.add_actor(actor);
-
- if (shapeActor)
- this._trackActor(shapeActor, true, true);
- },
-
- // setVisibleInOverview:
- // @actor: an actor in the chrome layer
- // @visible: Overview visibility
- //
- // By default, actors in the chrome layer are automatically hidden
- // when the Overview is shown. This can be used to override that
- // behavior
- setVisibleInOverview: function(actor, visible) {
- if (!this._verifyAncestry(actor, this.actor))
- throw new Error('actor is not a descendent of the chrome layer');
-
- if (visible)
- actor.reparent(this.actor);
+ if (params.visibleInOverview)
+ this.actor.add_actor(actor);
else
- actor.reparent(this.nonOverviewActor);
+ this.nonOverviewActor.add_actor(actor);
+
+ this._trackActor(actor, params.affectsInputRegion, params.affectsStruts);
},
- // addInputRegionActor:
- // @actor: an actor to add to the stage input region
+ // trackActor:
+ // @actor: a descendant of the chrome to begin tracking
+ // @params: parameters describing how to track @actor
//
- // Adds @actor to the stage input region, as with addActor(), but
- // for actors that are already descendants of the chrome layer.
- addInputRegionActor: function(actor) {
+ // Tells the chrome to track @actor, which must be a descendant
+ // of an actor added via addActor(). This can be used to extend the
+ // struts or input region to cover specific children.
+ trackActor: function(actor, params) {
if (!this._verifyAncestry(actor, this.actor))
throw new Error('actor is not a descendent of the chrome layer');
- this._trackActor(actor, true, false);
+ params = Params.parse(params, { affectsStruts: true,
+ affectsInputRegion: true });
+ this._trackActor(actor, params.affectsInputRegion, params.affectsStruts);
},
- // removeInputRegionActor:
- // @actor: an actor previously added to the stage input region
+ // untrackActor:
+ // @actor: an actor previously tracked via trackActor()
//
- // Undoes the effect of addInputRegionActor()
- removeInputRegionActor: function(actor) {
- this._untrackActor(actor, true, false);
+ // Undoes the effect of trackActor()
+ untrackActor: function(actor) {
+ this._untrackActor(actor);
},
// removeActor:
@@ -128,7 +115,7 @@ Chrome.prototype = {
this.nonOverviewActor.remove_actor(actor);
else
this.actor.remove_actor(actor);
- this._untrackActor(actor, true, true);
+ this._untrackActor(actor);
},
_findActor: function(actor) {
@@ -142,23 +129,13 @@ Chrome.prototype = {
_trackActor: function(actor, inputRegion, strut) {
let actorData;
- let i = this._findActor(actor);
- if (i != -1) {
- actorData = this._trackedActors[i];
- if (inputRegion)
- actorData.inputRegion++;
- if (strut)
- actorData.strut++;
- if (!inputRegion && !strut)
- actorData.children++;
- return;
- }
+ if (this._findActor(actor) != -1)
+ throw new Error('trying to re-track existing chrome actor');
actorData = { actor: actor,
- inputRegion: inputRegion ? 1 : 0,
- strut: strut ? 1 : 0,
- children: 0 };
+ inputRegion: inputRegion,
+ strut: strut };
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
@@ -166,54 +143,31 @@ Chrome.prototype = {
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
+ // Note that destroying actor will unset its parent, so we don't
+ // need to connect to 'destroy' too.
this._trackedActors.push(actorData);
-
- actor = actor.get_parent();
- if (actor != this.actor && actor != this.nonOverviewActor)
- this._trackActor(actor, false, false);
-
- if (inputRegion || strut)
- this._queueUpdateRegions();
+ this._queueUpdateRegions();
},
- _untrackActor: function(actor, inputRegion, strut) {
+ _untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
- if (inputRegion)
- actorData.inputRegion--;
- if (strut)
- actorData.strut--;
- if (!inputRegion && !strut)
- actorData.children--;
+ this._trackedActors.splice(i, 1);
+ actor.disconnect(actorData.visibleId);
+ actor.disconnect(actorData.allocationId);
+ actor.disconnect(actorData.parentSetId);
- if (actorData.inputRegion <= 0 && actorData.strut <= 0 && actorData.children <= 0) {
- this._trackedActors.splice(i, 1);
- actor.disconnect(actorData.visibleId);
- actor.disconnect(actorData.allocationId);
- actor.disconnect(actorData.parentSetId);
-
- actor = actor.get_parent();
- if (actor && actor != this.actor && actor != this.nonOverviewActor)
- this._untrackActor(actor, false, false);
- }
-
- if (inputRegion || strut)
- this._queueUpdateRegions();
+ this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
- if (this._verifyAncestry(actor, this.actor)) {
- let newParent = actor.get_parent();
- if (newParent != this.actor && newParent != this.nonOverviewActor)
- this._trackActor(newParent, false, false);
- }
- if (oldParent != this.actor && oldParent != this.nonOverviewActor)
- this._untrackActor(oldParent, false, false);
+ if (!this._verifyAncestry(actor, this.actor))
+ this._untrackActor(actor);
},
_overviewShowing: function() {
diff --git a/js/ui/dash.js b/js/ui/dash.js
index 3398fce9e..1fc2184d6 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -16,7 +16,6 @@ const AppDisplay = imports.ui.appDisplay;
const DocDisplay = imports.ui.docDisplay;
const PlaceDisplay = imports.ui.placeDisplay;
const GenericDisplay = imports.ui.genericDisplay;
-const Button = imports.ui.button;
const Main = imports.ui.main;
const DEFAULT_PADDING = 4;
@@ -390,14 +389,6 @@ SectionHeader.prototype = {
this._innerBox = new St.BoxLayout({ style_class: "section-header-inner" });
this.actor.set_child(this._innerBox);
- this._backgroundGradient = null;
- this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
- this.actor.connect('notify::allocation', Lang.bind(this, function (actor) {
- if (!this._backgroundGradient)
- return;
- this._onStyleChanged();
- }));
-
this.backLink = new BackLink();
this._innerBox.add(this.backLink.actor);
this.backLink.actor.hide();
@@ -422,33 +413,6 @@ SectionHeader.prototype = {
}
},
- _onStyleChanged: function () {
- if (this._backgroundGradient) {
- this._backgroundGradient.destroy();
- }
- // Manually implement the gradient
- let themeNode = this.actor.get_theme_node();
- let gradientTopColor = new Clutter.Color();
- if (!themeNode.get_color("-shell-gradient-top", false, gradientTopColor))
- return;
- let gradientBottomColor = new Clutter.Color();
- if (!themeNode.get_color("-shell-gradient-bottom", false, gradientBottomColor))
- return;
- this._backgroundGradient = Shell.create_vertical_gradient(gradientTopColor,
- gradientBottomColor);
- let box = this.actor.allocation;
- let contentBox = new Clutter.ActorBox();
- themeNode.get_content_box(box, contentBox);
- let width = contentBox.x2 - contentBox.x1;
- let height = contentBox.y2 - contentBox.y1;
- this._backgroundGradient.set_size(width, height);
- // This will set a fixed position, which puts us outside of the normal box layout
- this._backgroundGradient.set_position(0, 0);
-
- this._innerBox.add_actor(this._backgroundGradient);
- this._backgroundGradient.lower_bottom();
- },
-
setTitle : function(title) {
this.text.text = title;
},
diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js
index d5c3e3f38..939d98936 100644
--- a/js/ui/genericDisplay.js
+++ b/js/ui/genericDisplay.js
@@ -13,7 +13,6 @@ const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
-const Button = imports.ui.button;
const DND = imports.ui.dnd;
const Link = imports.ui.link;
const Main = imports.ui.main;
@@ -43,8 +42,6 @@ const PREVIEW_BOX_CORNER_RADIUS = 10;
const PREVIEW_PLACING = 3/4;
const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
-const INFORMATION_BUTTON_SIZE = 16;
-
/* This is a virtual class that represents a single display item containing
* a name, a description, and an icon. It allows selecting an item and represents
* it by highlighting it with a different background color than the default.
@@ -68,44 +65,14 @@ GenericDisplayItem.prototype = {
}));
let draggable = DND.makeDraggable(this.actor);
- draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
this._iconBin = new St.Bin();
this.actor.add(this._iconBin);
- this._infoText = new St.BoxLayout({ style_class: "generic-display-item-text",
+ this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
vertical: true });
this.actor.add(this._infoText, { expand: true, y_fill: false });
- let infoIconUri = "file://" + global.imagedir + "info.svg";
- let infoIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
- infoIconUri,
- INFORMATION_BUTTON_SIZE,
- INFORMATION_BUTTON_SIZE);
- this._informationButton = new Button.IconButton(this.actor, INFORMATION_BUTTON_SIZE, infoIcon);
- let buttonBox = new Big.Box({ width: INFORMATION_BUTTON_SIZE + 2 * DEFAULT_PADDING,
- height: INFORMATION_BUTTON_SIZE,
- padding_left: DEFAULT_PADDING, padding_right: DEFAULT_PADDING,
- y_align: Big.BoxAlignment.CENTER });
- buttonBox.append(this._informationButton.actor, Big.BoxPackFlags.NONE);
- this.actor.add(buttonBox, { x_fill: false, x_align: St.Align.END });
-
- // Connecting to the button-press-event for the information button ensures that the actor,
- // which is a draggable actor, does not get the button-press-event and doesn't initiate
- // the dragging, which then prevents us from getting the button-release-event for the button.
- this._informationButton.actor.connect('button-press-event',
- Lang.bind(this,
- function() {
- return true;
- }));
- this._informationButton.actor.connect('button-release-event',
- Lang.bind(this,
- function() {
- // Selects the item by highlighting it and displaying its details
- this.emit('show-details');
- return true;
- }));
-
this._name = null;
this._description = null;
this._icon = null;
@@ -136,16 +103,10 @@ GenericDisplayItem.prototype = {
//// Public methods ////
- // Shows the information button when the item was drawn under the mouse pointer.
- onDrawnUnderPointer: function() {
- this._informationButton.show();
- },
-
// Highlights the item by setting a different background color than the default
// if isSelected is true, removes the highlighting otherwise.
markSelected: function(isSelected) {
this.actor.set_style_pseudo_class(isSelected ? "selected" : null);
- this._informationButton.forceShow(isSelected)
},
/*
@@ -271,16 +232,9 @@ GenericDisplayItem.prototype = {
// Returns a preview icon for the item.
_createPreviewIcon: function() {
throw new Error("Not implemented");
- },
+ }
//// Private methods ////
-
- // Hides the information button once the item starts being dragged.
- _onDragBegin : function (draggable, time) {
- // For some reason, we are not getting leave-event signal when we are dragging an item,
- // so we should remove the link manually.
- this._informationButton.actor.hide();
- }
};
Signals.addSignalMethods(GenericDisplayItem.prototype);
@@ -637,28 +591,9 @@ GenericDisplay.prototype = {
this.selectFirstItem();
}
- Mainloop.idle_add(Lang.bind(this, this._checkInformationIcon),
- Meta.PRIORITY_BEFORE_REDRAW);
-
this.emit('redisplayed');
},
- // Check if the pointer is over one of the items and display the information button if it is.
- // We want to do this between finishing our changes to the display and the point where
- // the display is redrawn.
- _checkInformationIcon: function() {
- let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
- let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
- x, y);
- if (actor != null) {
- let item = this._findDisplayedByActor(actor);
- if (item != null) {
- item.onDrawnUnderPointer();
- }
- }
- return false;
- },
-
//// Pure virtual protected methods ////
// Performs the steps needed to have the latest information about the items.
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 9e2fdb431..a07075d41 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -13,42 +13,16 @@ const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
-const Button = imports.ui.button;
const Calendar = imports.ui.calendar;
const Main = imports.ui.main;
const StatusMenu = imports.ui.statusMenu;
const PANEL_HEIGHT = 26;
-const TRAY_HEIGHT = PANEL_HEIGHT - 1;
const DEFAULT_PADDING = 4;
const PANEL_ICON_SIZE = 24;
-const BACKGROUND_TOP = new Clutter.Color();
-BACKGROUND_TOP.from_pixel(0x161616ff);
-const BACKGROUND_BOTTOM = new Clutter.Color();
-BACKGROUND_BOTTOM.from_pixel(0x000000ff);
-
-const PANEL_FOREGROUND_COLOR = new Clutter.Color();
-PANEL_FOREGROUND_COLOR.from_pixel(0xffffffff);
-const SN_BACKGROUND_COLOR = new Clutter.Color();
-SN_BACKGROUND_COLOR.from_pixel(0xffff00a0);
-
-const TRANSPARENT_COLOR = new Clutter.Color();
-TRANSPARENT_COLOR.from_pixel(0x00000000);
-
-// Don't make the mouse hover effect visible to the user for a menu feel.
-const PANEL_BUTTON_COLOR = new Clutter.Color();
-PANEL_BUTTON_COLOR.from_pixel(0x00000000);
-
-// Lighten pressed buttons; darkening has no effect on a black background.
-const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
-PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x324c6ffa);
-
-const DEFAULT_FONT = 'Sans 16px';
-
-const TRAY_PADDING = 0;
// See comments around _recomputeTraySize
const TRAY_SPACING = 14;
const TRAY_SPACING_MIN = 8;
@@ -84,24 +58,14 @@ AppPanelMenu.prototype = {
this._activeSequence = null;
this._startupSequences = {};
- this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
- spacing: DEFAULT_PADDING,
- y_align: Big.BoxAlignment.CENTER });
- this._iconBox = new Big.Box({ width: PANEL_ICON_SIZE, height: PANEL_ICON_SIZE,
- x_align: Big.BoxAlignment.CENTER,
- y_align: Big.BoxAlignment.CENTER });
- this.actor.append(this._iconBox, Big.BoxPackFlags.NONE);
- let labelBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
- y_align: Big.BoxAlignment.CENTER });
- this._label = new Clutter.Text({ font_name: DEFAULT_FONT,
- color: PANEL_FOREGROUND_COLOR,
- text: "" });
- labelBox.append(this._label, Big.BoxPackFlags.EXPAND);
- this.actor.append(labelBox, Big.BoxPackFlags.NONE);
+ this.actor = new St.BoxLayout({ name: 'appMenu' });
+ this._iconBox = new St.Bin({ name: 'appMenuIcon' });
+ this.actor.add(this._iconBox);
+ this._label = new St.Label();
+ this.actor.add(this._label, { expand: true, y_fill: false });
- this._startupBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
- y_align: Big.BoxAlignment.CENTER });
- this.actor.append(this._startupBox, Big.BoxPackFlags.NONE);
+ this._startupBox = new St.BoxLayout();
+ this.actor.add(this._startupBox);
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.opacity = 255;
@@ -145,19 +109,20 @@ AppPanelMenu.prototype = {
this._focusedApp = focusedApp;
this._activeSequence = lastSequence;
- this._iconBox.remove_all();
+ if (this._iconBox.child != null)
+ this._iconBox.child.destroy();
this._iconBox.hide();
this._label.set_text('');
if (this._focusedApp != null) {
let icon = this._focusedApp.create_icon_texture(PANEL_ICON_SIZE);
- this._iconBox.append(icon, Big.BoxPackFlags.NONE);
+ this._iconBox.set_child(icon);
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) {
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
- this._iconBox.append(icon, Big.BoxPackFlags.NONE);
+ this._iconBox.set_child(icon);
this._iconBox.show();
this._label.set_text(this._activeSequence.get_name());
}
@@ -175,32 +140,17 @@ function Panel() {
Panel.prototype = {
_init : function() {
- this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL
- });
+ this.actor = new St.BoxLayout({ name: 'panel' });
this.actor._delegate = this;
- let backgroundGradient = Shell.create_vertical_gradient(BACKGROUND_TOP,
- BACKGROUND_BOTTOM);
- this.actor.connect('notify::allocation', Lang.bind(this, function () {
- let [width, height] = this.actor.get_size();
- backgroundGradient.set_size(width, height);
- }));
- this.actor.add_actor(backgroundGradient);
-
- this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
- y_align: Big.BoxAlignment.CENTER,
- spacing: DEFAULT_PADDING,
- 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 });
+ this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
+ this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
+ this._rightBox = new St.BoxLayout({ name: 'panelRight' });
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
this._boxContainer = new Shell.GenericContainer();
- this.actor.append(this._boxContainer, Big.BoxPackFlags.EXPAND);
+ this.actor.add(this._boxContainer, { expand: true });
this._boxContainer.add_actor(this._leftBox);
this._boxContainer.add_actor(this._centerBox);
this._boxContainer.add_actor(this._rightBox);
@@ -274,11 +224,14 @@ Panel.prototype = {
/* Button on the left side of the panel. */
/* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
- this.button = new Button.Button(_("Activities"), PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
- PANEL_FOREGROUND_COLOR, DEFAULT_FONT);
- this.button.actor.height = PANEL_HEIGHT;
+ let label = new St.Label({ text: _("Activities") });
+ this.button = new St.Clickable({ name: 'panelActivities',
+ style_class: 'panel-button',
+ reactive: true });
+ this.button.set_child(label);
+ this.button.height = PANEL_HEIGHT;
- this._leftBox.append(this.button.actor, Big.BoxPackFlags.NONE);
+ this._leftBox.add(this.button);
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
@@ -286,12 +239,16 @@ Panel.prototype = {
// multiple times due to an accidental jitter.
this._hotCornerEntered = false;
- this._hotCornerEnvirons = new Clutter.Rectangle({ width: 3,
+ this._hotCornerEnvirons = new Clutter.Rectangle({ x: 0,
+ y: 0,
+ width: 3,
height: 3,
opacity: 0,
reactive: true });
- this._hotCorner = new Clutter.Rectangle({ width: 1,
+ this._hotCorner = new Clutter.Rectangle({ x: 0,
+ y: 0,
+ width: 1,
height: 1,
opacity: 0,
reactive: true });
@@ -315,23 +272,23 @@ Panel.prototype = {
this._hotCorner.connect('leave-event',
Lang.bind(this, this._onHotCornerLeft));
- this._leftBox.append(this._hotCornerEnvirons, Big.BoxPackFlags.FIXED);
- this._leftBox.append(this._hotCorner, Big.BoxPackFlags.FIXED);
+ this._leftBox.add(this._hotCornerEnvirons);
+ this._leftBox.add(this._hotCorner);
let appMenu = new AppPanelMenu();
- this._leftBox.append(appMenu.actor, Big.BoxPackFlags.NONE);
+ this._leftBox.add(appMenu.actor);
/* center */
let clockButton = new St.Button({ style_class: "panel-button",
- toggle_mode: true });
- this._centerBox.append(clockButton, Big.BoxPackFlags.NONE);
+ toggle_mode: true,
+ x_fill: true,
+ y_fill: true });
+ this._centerBox.add(clockButton, { y_fill: false });
clockButton.connect('clicked', Lang.bind(this, this._toggleCalendar));
- this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
- color: PANEL_FOREGROUND_COLOR,
- text: "" });
- clockButton.add_actor(this._clock);
+ this._clock = new St.Label();
+ clockButton.set_child(this._clock);
this._calendarPopup = null;
@@ -340,11 +297,10 @@ Panel.prototype = {
// The tray icons live in trayBox within trayContainer.
// The trayBox is hidden when there are no tray icons.
let trayContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
- y_align: Big.BoxAlignment.START });
- this._rightBox.append(trayContainer, Big.BoxPackFlags.NONE);
+ y_align: Big.BoxAlignment.CENTER });
+ this._rightBox.add(trayContainer);
let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
- height: TRAY_HEIGHT,
- padding: TRAY_PADDING,
+ height: PANEL_ICON_SIZE,
spacing: TRAY_SPACING });
this._trayBox = trayBox;
@@ -373,37 +329,31 @@ Panel.prototype = {
}));
this._traymanager.manage_stage(global.stage);
- let statusbox = new Big.Box();
let statusmenu = this._statusmenu = new StatusMenu.StatusMenu();
- statusbox.append(this._statusmenu.actor, Big.BoxPackFlags.NONE);
- let statusbutton = new Button.Button(statusbox,
- PANEL_BUTTON_COLOR,
- PRESSED_BUTTON_BACKGROUND_COLOR,
- PANEL_FOREGROUND_COLOR);
- statusbutton.actor.height = PANEL_HEIGHT;
- statusbutton.actor.connect('button-press-event', function (b, e) {
- if (e.get_button() == 1 && e.get_click_count() == 1) {
- statusmenu.toggle(e);
- // The statusmenu might not pop up if it couldn't get a pointer grab
- if (statusmenu.isActive())
- statusbutton.actor.active = true;
- return true;
- } else {
- return false;
- }
+ let statusbutton = new St.Clickable({ name: 'panelStatus',
+ style_class: 'panel-button',
+ reactive: true });
+ statusbutton.set_child(statusmenu.actor);
+ statusbutton.height = PANEL_HEIGHT;
+ statusbutton.connect('clicked', function (b, event) {
+ statusmenu.toggle(event);
+ // The statusmenu might not pop up if it couldn't get a pointer grab
+ if (statusmenu.isActive())
+ statusbutton.active = true;
+ return true;
});
- this._rightBox.append(statusbutton.actor, Big.BoxPackFlags.NONE);
+ this._rightBox.add(statusbutton);
// We get a deactivated event when the popup disappears
this._statusmenu.connect('deactivated', function (sm) {
- statusbutton.actor.active = false;
+ statusbutton.active = false;
});
// 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
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
// to switch to.
- this.button.actor.connect('button-press-event', Lang.bind(this, function(b, e) {
- if (e.get_button() == 1 && e.get_click_count() == 1 && !Main.overview.animationInProgress) {
+ this.button.connect('clicked', Lang.bind(this, function(b, event) {
+ if (!Main.overview.animationInProgress) {
this._maybeToggleOverviewOnClick();
return true;
} else {
@@ -414,14 +364,13 @@ Panel.prototype = {
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
// and to be released when it is exited regardless of how it was triggered.
Main.overview.connect('showing', Lang.bind(this, function() {
- this.button.actor.active = true;
+ this.button.active = true;
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
- this.button.actor.active = false;
+ this.button.active = false;
}));
- Main.chrome.addActor(this.actor);
- Main.chrome.setVisibleInOverview(this.actor, true);
+ Main.chrome.addActor(this.actor, { visibleInOverview: true });
// Start the clock
this._updateClock();
@@ -558,12 +507,8 @@ CalendarPopup.prototype = {
this.calendar = new Calendar.Calendar();
this.actor.add(this.calendar.actor);
- // Directly adding the actor to Main.chrome.actor is a hack to
- // work around the fact that there is no way to add an actor that
- // affects the input region but not the shape.
- // See: https://bugzilla.gnome.org/show_bug.cgi?id=597044
- Main.chrome.actor.add_actor(this.actor);
- Main.chrome.addInputRegionActor(this.actor);
+ Main.chrome.addActor(this.actor, { visibleInOverview: true,
+ affectsStruts: false });
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
},
diff --git a/js/ui/runDialog.js b/js/ui/runDialog.js
index 69c243426..487aeb11b 100644
--- a/js/ui/runDialog.js
+++ b/js/ui/runDialog.js
@@ -206,7 +206,7 @@ RunDialog.prototype = {
this._commandError = false;
this._group.hide();
- this._entry.text = '';
+ this._entry.set_text('');
Main.popModal(this._group);
}
diff --git a/js/ui/widgetBox.js b/js/ui/widgetBox.js
index e12dd7376..b7dae7459 100644
--- a/js/ui/widgetBox.js
+++ b/js/ui/widgetBox.js
@@ -331,7 +331,7 @@ WidgetBox.prototype = {
onCompleteScope: this });
this.state = this._widget.state = Widget.STATE_POPPING_OUT;
- Main.chrome.addInputRegionActor(this._hbox);
+ Main.chrome.trackActor(this._hbox, { affectsStruts: false });
},
_popOutComplete: function() {
@@ -370,7 +370,7 @@ WidgetBox.prototype = {
this._egroup.hide();
this._ebox.x = 0;
- Main.chrome.removeInputRegionActor(this._hbox);
+ Main.chrome.untrackActor(this._hbox);
},
destroy: function() {
diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js
index 8e4a60d97..58e982fc0 100644
--- a/js/ui/workspaces.js
+++ b/js/ui/workspaces.js
@@ -3,12 +3,14 @@
const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
+const Gdk = imports.gi.Gdk;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
+const St = imports.gi.St;
const Signals = imports.signals;
const DND = imports.ui.dnd;
@@ -20,10 +22,6 @@ const Tweener = imports.ui.tweener;
const FOCUS_ANIMATION_TIME = 0.15;
-const WINDOWCLONE_BG_COLOR = new Clutter.Color();
-WINDOWCLONE_BG_COLOR.from_pixel(0x000000f0);
-const WINDOWCLONE_TITLE_COLOR = new Clutter.Color();
-WINDOWCLONE_TITLE_COLOR.from_pixel(0xffffffff);
const FRAME_COLOR = new Clutter.Color();
FRAME_COLOR.from_pixel(0xffffffff);
@@ -113,8 +111,6 @@ WindowClone.prototype = {
this._stackAbove = null;
- this._title = null;
-
this.actor.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
@@ -135,18 +131,6 @@ WindowClone.prototype = {
this._zooming = false;
},
- setVisibleWithChrome: function(visible) {
- if (visible) {
- this.actor.show();
- if (this._title)
- this._title.show();
- } else {
- this.actor.hide();
- if (this._title)
- this._title.hide();
- }
- },
-
setStackAbove: function (actor) {
this._stackAbove = actor;
if (this._inDrag || this._zooming)
@@ -157,8 +141,6 @@ WindowClone.prototype = {
destroy: function () {
this.actor.destroy();
- if (this._title)
- this._title.destroy();
},
_onEnter: function (actor, event) {
@@ -168,8 +150,6 @@ WindowClone.prototype = {
return;
this._havePointer = true;
-
- this._updateTitle();
},
_onLeave: function (actor, event) {
@@ -179,7 +159,6 @@ WindowClone.prototype = {
return;
this._havePointer = false;
- this._updateTitle();
if (this._zoomStep)
this._zoomEnd();
@@ -218,6 +197,7 @@ WindowClone.prototype = {
_zoomStart : function () {
this._zooming = true;
+ this.emit('zoom-start');
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
@@ -246,6 +226,7 @@ WindowClone.prototype = {
_zoomEnd : function () {
this._zooming = false;
+ this.emit('zoom-end');
this.actor.reparent(this._origParent);
this.actor.raise(this._stackAbove);
@@ -253,8 +234,6 @@ WindowClone.prototype = {
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
- this._adjustTitle();
-
this._zoomLightbox.destroy();
Main.overview.disconnect(this._hideEventId);
@@ -273,7 +252,6 @@ WindowClone.prototype = {
_onDragBegin : function (draggable, time) {
this._inDrag = true;
- this._updateTitle();
this.emit('drag-begin');
},
@@ -295,86 +273,6 @@ WindowClone.prototype = {
this.actor.raise(this._stackAbove);
this.emit('drag-end');
- },
-
- // Called by Tweener
- onAnimationStart : function () {
- this._updateTitle();
- },
-
- // Called by Tweener
- onAnimationComplete : function () {
- this._updateTitle();
- },
-
- _createTitle : function () {
- let window = this.realWindow;
-
- let box = new Big.Box({ background_color : WINDOWCLONE_BG_COLOR,
- y_align: Big.BoxAlignment.CENTER,
- corner_radius: 5,
- padding: 4,
- spacing: 4,
- orientation: Big.BoxOrientation.HORIZONTAL });
-
- let title = new Clutter.Text({ color: WINDOWCLONE_TITLE_COLOR,
- font_name: "Sans 12",
- text: this.metaWindow.title,
- ellipsize: Pango.EllipsizeMode.END
- });
- box.append(title, Big.BoxPackFlags.EXPAND);
- // Get and cache the expected width (just the icon), with spacing, plus title
- box.fullWidth = box.width;
- box.hide(); // Hidden by default, show on mouseover
- this._title = box;
-
- // Make the title a sibling of the window
- this.actor.get_parent().add_actor(box);
- },
-
- _adjustTitle : function () {
- let title = this._title;
- if (!title)
- return;
-
- let [cloneScreenWidth, cloneScreenHeight] = this.actor.get_transformed_size();
- let [titleScreenWidth, titleScreenHeight] = title.get_transformed_size();
-
- // Titles are supposed to be "full-size", so adjust its
- // scale to counteract the scaling of its ancestor actors.
- title.set_scale(title.width / titleScreenWidth * title.scale_x,
- title.height / titleScreenHeight * title.scale_y);
-
- title.width = Math.min(title.fullWidth, cloneScreenWidth);
- let xoff = ((cloneScreenWidth - title.width) / 2) * title.scale_x;
- title.set_position(this.actor.x + xoff, this.actor.y);
- },
-
- _showTitle : function () {
- if (!this._title)
- this._createTitle();
-
- this._adjustTitle();
- this._title.show();
- this._title.raise(this.actor);
- },
-
- _hideTitle : function () {
- if (!this._title)
- return;
-
- this._title.hide();
- },
-
- _updateTitle : function () {
- let shouldShow = (this._havePointer &&
- !this._inDrag &&
- !Tweener.isTweening(this.actor));
-
- if (shouldShow)
- this._showTitle();
- else
- this._hideTitle();
}
};
@@ -409,6 +307,207 @@ DesktopClone.prototype = {
Signals.addSignalMethods(DesktopClone.prototype);
+/**
+ * @windowClone: Corresponding window clone
+ * @parentActor: The actor which will be the parent of all overlay items
+ * such as app icon and window caption
+ */
+function WindowOverlay(windowClone, parentActor) {
+ this._init(windowClone, parentActor);
+}
+
+WindowOverlay.prototype = {
+ _init : function(windowClone, parentActor) {
+ let metaWindow = windowClone.metaWindow;
+
+ this._windowClone = windowClone;
+ this._parentActor = parentActor;
+
+ let title = new St.Label({ style_class: "window-caption",
+ text : metaWindow.title });
+ title.connect('style-changed',
+ Lang.bind(this, this._onStyleChanged));
+ title.clutter_text.ellipsize = Pango.EllipsizeMode.END;
+ title._spacing = 0;
+
+ let button = new St.Bin({ style_class: "window-close",
+ reactive: true });
+ button.connect('style-changed',
+ Lang.bind(this, this._onStyleChanged));
+ button._overlap = 0;
+
+ windowClone.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+ windowClone.actor.connect('notify::allocation',
+ Lang.bind(this, this._positionItems));
+ windowClone.actor.connect('enter-event',
+ Lang.bind(this, this._onEnter));
+ windowClone.actor.connect('leave-event',
+ Lang.bind(this, this._onLeave));
+
+ this._idleToggleCloseId = 0;
+ button.connect('button-release-event',
+ Lang.bind(this, this._closeWindow));
+
+ this._windowAddedId = 0;
+ windowClone.connect('zoom-start', Lang.bind(this, this.hide));
+ windowClone.connect('zoom-end', Lang.bind(this, this.show));
+
+ button.hide();
+
+ this.title = title;
+ this.closeButton = button;
+
+ parentActor.add_actor(this.title);
+ parentActor.add_actor(this.closeButton);
+ },
+
+ hide: function() {
+ this.closeButton.hide();
+ this.title.hide();
+ },
+
+ show: function() {
+ let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
+ let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
+ x, y);
+ if (actor == this._windowClone.actor) {
+ this.closeButton.show();
+ }
+ this.title.show();
+ },
+
+ fadeIn: function() {
+ this.title.opacity = 0;
+ this.title.show();
+ this.title.raise_top();
+ Tweener.addTween(this.title,
+ { opacity: 255,
+ time: Overview.ANIMATION_TIME,
+ transition: "easeOutQuad" });
+ },
+
+ chromeWidth: function () {
+ return this.closeButton.width - this.closeButton._overlap;
+ },
+
+ chromeHeight: function () {
+ return this.closeButton.height - this.closeButton._overlap +
+ this.title.height + this.title._spacing;
+ },
+
+ _positionItems: function(win) {
+ let button = this.closeButton;
+ let title = this.title;
+
+ let [x, y] = win.get_transformed_position();
+ let [w, h] = win.get_transformed_size();
+
+ let buttonX = x + w - button._overlap;
+ let buttonY = y - button.height + button._overlap;
+ button.set_position(Math.floor(buttonX), Math.floor(buttonY));
+
+ if (!title.fullWidth)
+ title.fullWidth = title.width;
+ title.width = Math.min(title.fullWidth, w);
+
+ let titleX = x + (w - title.width) / 2;
+ let titleY = y + h + title._spacing;
+ title.set_position(Math.floor(titleX), Math.floor(titleY));
+ },
+
+ _closeWindow: function(actor, event) {
+ let metaWindow = this._windowClone.metaWindow;
+ this._workspace = metaWindow.get_workspace();
+
+ this._windowAddedId = this._workspace.connect('window-added',
+ Lang.bind(this,
+ this._onWindowAdded));
+
+ metaWindow.delete(event.get_time());
+ },
+
+ _onWindowAdded: function(workspace, win) {
+ let metaWindow = this._windowClone.metaWindow;
+
+ if (win.get_transient_for() == metaWindow) {
+ workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+
+ // use an idle handler to avoid mapping problems -
+ // see comment in Workspace._windowAdded
+ Mainloop.idle_add(Lang.bind(this,
+ function() {
+ this._windowClone.emit('selected');
+ return false;
+ }));
+ }
+ },
+
+ _onDestroy: function() {
+ if (this._windowAddedId > 0) {
+ this._workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+ }
+ if (this._idleToggleCloseId > 0) {
+ Mainloop.source_remove(this._idleToggleCloseId);
+ this._idleToggleCloseId = 0;
+ }
+ this.title.destroy();
+ this.closeButton.destroy();
+ },
+
+ _onEnter: function() {
+ this.closeButton.raise_top();
+ this.closeButton.show();
+ this.emit('show-close-button');
+ },
+
+ _onLeave: function() {
+ if (this._idleToggleCloseId == 0)
+ this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton));
+ },
+
+ _idleToggleCloseButton: function() {
+ this._idleToggleCloseId = 0;
+ let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
+ let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
+ x, y);
+ if (actor != this._windowClone.actor && actor != this.closeButton) {
+ this.closeButton.hide();
+ }
+ return false;
+ },
+
+ hideCloseButton: function() {
+ if (this._idleToggleCloseId > 0) {
+ Mainloop.source_remove(this._idleToggleCloseId);
+ this._idleToggleCloseId = 0;
+ }
+ this.closeButton.hide();
+ },
+
+ _onStyleChanged: function() {
+ let titleNode = this.title.get_theme_node();
+
+ let [success, len] = titleNode.get_length('-shell-caption-spacing',
+ false);
+ if (success)
+ this.title._spacing = len;
+
+ let closeNode = this.closeButton.get_theme_node();
+
+ let [success, len] = closeNode.get_length('-shell-close-overlap',
+ false);
+ if (success)
+ this.closeButton._overlap = len;
+
+ this._parentActor.queue_relayout();
+ }
+};
+
+Signals.addSignalMethods(WindowOverlay.prototype);
+
+
/**
* @workspaceNum: Workspace index
* @parentActor: The actor which will be the parent of this workspace;
@@ -460,7 +559,7 @@ Workspace.prototype = {
// Create clones for remaining windows that should be
// visible in the Overview
this._windows = [this._desktop];
- this._windowIcons = [ null ];
+ this._windowOverlays = [ null ];
for (let i = 0; i < windows.length; i++) {
if (this._isOverviewWindow(windows[i])) {
this._addWindowClone(windows[i]);
@@ -676,13 +775,13 @@ Workspace.prototype = {
_resetCloneVisibility: function () {
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
- let icon = this._windowIcons[i];
+ let overlay = this._windowOverlays[i];
if (!this._isCloneVisible(clone)) {
- clone.setVisibleWithChrome(false);
- icon.hide();
+ clone.actor.hide();
+ overlay.hide();
} else {
- clone.setVisibleWithChrome(true);
+ clone.actor.show();
}
}
},
@@ -888,14 +987,17 @@ Workspace.prototype = {
let rect = new Meta.Rectangle();
metaWindow.get_outer_rect(rect);
- let desiredWidth = global.screen_width * fraction;
- let desiredHeight = global.screen_height * fraction;
- let scale = Math.min(desiredWidth / rect.width,
- desiredHeight / rect.height,
+ let chromeHeight = this._windowOverlays[1].chromeHeight() / this.scale;
+ let chromeWidth = this._windowOverlays[1].chromeWidth() / this.scale;
+
+ let desiredWidth = (global.screen_width - chromeWidth) * fraction;
+ let desiredHeight = (global.screen_height - chromeHeight) * fraction;
+ let scale = Math.min(desiredWidth / (rect.width + chromeWidth),
+ desiredHeight / (rect.height + chromeHeight),
1.0 / this.scale);
- let x = xCenter - 0.5 * scale * rect.width;
- let y = yCenter - 0.5 * scale * rect.height;
+ let x = xCenter - 0.5 * scale * (rect.width + chromeWidth);
+ let y = yCenter - 0.5 * scale * (rect.height + chromeHeight);
return [x, y, scale];
},
@@ -918,11 +1020,11 @@ Workspace.prototype = {
let metaWindow = visibleWindows[i];
let mainIndex = this._lookupIndex(metaWindow);
let clone = metaWindow._delegate;
- let icon = this._windowIcons[mainIndex];
+ let overlay = this._windowOverlays[mainIndex];
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
- icon.hide();
+ overlay.hide();
Tweener.addTween(clone.actor,
{ x: x,
y: y,
@@ -932,7 +1034,7 @@ Workspace.prototype = {
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: Lang.bind(this, function() {
- this._fadeInWindowIcon(clone, icon);
+ overlay.fadeIn();
})
});
}
@@ -956,49 +1058,20 @@ Workspace.prototype = {
}
},
- _fadeInWindowIcon: function (clone, icon) {
- icon.opacity = 0;
- icon.show();
- // This is a little messy and complicated because when we
- // start the fade-in we may not have done the final positioning
- // of the workspaces. (Tweener doesn't necessarily finish
- // all animations before calling onComplete callbacks.)
- // So we need to manually compute where the window will
- // be after the workspace animation finishes.
- let [parentX, parentY] = icon.get_parent().get_position();
- let [cloneX, cloneY] = clone.actor.get_position();
- let [cloneWidth, cloneHeight] = clone.actor.get_size();
- cloneX = this.gridX + this.scale * cloneX;
- cloneY = this.gridY + this.scale * cloneY;
- cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
- cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
- // Note we only round the first part, because we're still going to be
- // positioned relative to the parent. By subtracting a possibly
- // non-integral parent X/Y we cancel it out.
- let x = Math.round(cloneX + cloneWidth - icon.width) - parentX;
- let y = Math.round(cloneY + cloneHeight - icon.height) - parentY;
- icon.set_position(x, y);
- icon.raise(this.actor);
- Tweener.addTween(icon,
- { opacity: 255,
- time: Overview.ANIMATION_TIME,
- transition: "easeOutQuad" });
- },
-
- _fadeInAllIcons: function () {
+ _fadeInAllOverlays: function() {
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
- let icon = this._windowIcons[i];
+ let overlay = this._windowOverlays[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
continue;
- this._fadeInWindowIcon(clone, icon);
+ overlay.fadeIn();
}
},
- _hideAllIcons: function () {
- for (let i = 1; i < this._windows.length; i++) {
- let icon = this._windowIcons[i];
- icon.hide();
+ _hideAllOverlays: function() {
+ for (let i = 1; i< this._windows.length; i++) {
+ let overlay = this._windowOverlays[i];
+ overlay.hide();
}
},
@@ -1012,10 +1085,9 @@ Workspace.prototype = {
return;
let clone = this._windows[index];
- let icon = this._windowIcons[index];
this._windows.splice(index, 1);
- this._windowIcons.splice(index, 1);
+ this._windowOverlays.splice(index, 1);
// If metaWin.get_compositor_private() returned non-NULL, that
// means the window still exists (and is just being moved to
@@ -1033,7 +1105,6 @@ Workspace.prototype = {
};
}
clone.destroy();
- icon.destroy();
this.positionWindows(false);
this.updateRemovable();
@@ -1103,7 +1174,7 @@ Workspace.prototype = {
zoomFromOverview : function() {
this.leavingOverview = true;
- this._hideAllIcons();
+ this._hideAllOverlays();
Main.overview.connect('hidden', Lang.bind(this,
this._doneLeavingOverview));
@@ -1140,7 +1211,7 @@ Workspace.prototype = {
// Animates grid shrinking/expanding when a row or column
// of workspaces is added or removed
resizeToGrid : function (oldScale) {
- this._hideAllIcons();
+ this._hideAllOverlays();
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
@@ -1148,7 +1219,7 @@ Workspace.prototype = {
scale_y: this.scale,
time: Overview.ANIMATION_TIME,
transition: "easeOutQuad",
- onComplete: Lang.bind(this, this._fadeInAllIcons)
+ onComplete: Lang.bind(this, this._fadeInAllOverlays)
});
},
@@ -1177,7 +1248,7 @@ Workspace.prototype = {
slideOut : function(onComplete) {
let destX = this.actor.x, destY = this.actor.y;
- this._hideAllIcons();
+ this._hideAllOverlays();
if (this.gridCol > this.gridRow)
destX = global.screen_width;
@@ -1226,50 +1297,41 @@ Workspace.prototype = {
return tracker.is_window_interesting(win.get_meta_window());
},
- _createWindowIcon: function(window) {
- let tracker = Shell.WindowTracker.get_default()
- let app = tracker.get_window_app(window.metaWindow);
- let iconTexture = null;
- // The design is application based, so prefer the application
- // icon here if we have it. FIXME - should move this fallback code
- // into ShellAppMonitor.
- if (app) {
- iconTexture = app.create_icon_texture(48);
- } else {
- let icon = window.metaWindow.icon;
- iconTexture = new Clutter.Texture({ width: 48,
- height: 48,
- keep_aspect_ratio: true });
- Shell.clutter_texture_set_from_pixbuf(iconTexture, icon);
- }
- return iconTexture;
- },
-
// Create a clone of a (non-desktop) window and add it to the window list
_addWindowClone : function(win) {
- let icon = this._createWindowIcon(win);
- this.parentActor.add_actor(icon);
-
let clone = new WindowClone(win);
+ let overlay = new WindowOverlay(clone, this.parentActor);
+
clone.connect('selected',
Lang.bind(this, this._onCloneSelected));
clone.connect('drag-begin',
Lang.bind(this, function() {
- icon.hide();
+ overlay.hide();
}));
clone.connect('drag-end',
Lang.bind(this, function() {
- icon.show();
+ overlay.show();
}));
this.actor.add_actor(clone.actor);
+ overlay.connect('show-close-button', Lang.bind(this, this._onShowOverlayClose));
+
this._windows.push(clone);
- this._windowIcons.push(icon);
+ this._windowOverlays.push(overlay);
return clone;
},
+ _onShowOverlayClose: function (windowOverlay) {
+ for (let i = 1; i < this._windowOverlays.length; i++) {
+ let overlay = this._windowOverlays[i];
+ if (overlay == windowOverlay)
+ continue;
+ overlay.hideCloseButton();
+ }
+ },
+
_computeWindowSlot : function(windowIndex, numberOfWindows) {
if (numberOfWindows in POSITIONS)
return POSITIONS[numberOfWindows][windowIndex];
@@ -1345,7 +1407,10 @@ function Workspaces(width, height, x, y) {
Workspaces.prototype = {
_init : function(width, height, x, y) {
- this.actor = new Clutter.Group();
+ this.actor = new St.Bin({ style_class: "workspaces" });
+ this._actor = new Clutter.Group();
+
+ this.actor.add_actor(this._actor);
this._width = width;
this._height = height;
@@ -1654,9 +1719,9 @@ Workspaces.prototype = {
},
_addWorkspaceActor : function(workspaceNum) {
- let workspace = new Workspace(workspaceNum, this.actor);
+ let workspace = new Workspace(workspaceNum, this._actor);
this._workspaces[workspaceNum] = workspace;
- this.actor.add_actor(workspace.actor);
+ this._actor.add_actor(workspace.actor);
},
_onRestacked: function() {
diff --git a/po/pt_BR.po b/po/pt_BR.po
index b62d8df6a..9af5a83b2 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -7,10 +7,11 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-09-21 08:39-0300\n"
-"PO-Revision-Date: 2009-09-20 08:41-0300\n"
-"Last-Translator: Rodrigo Flores \n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
+"shell&component=general\n"
+"POT-Creation-Date: 2009-11-12 21:33+0000\n"
+"PO-Revision-Date: 2009-11-14 11:53-0200\n"
+"Last-Translator: Amanda Magalhães \n"
"Language-Team: Brazilian Portuguese \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -25,84 +26,115 @@ msgstr "GNOME Shell"
msgid "Window management and application launching"
msgstr "Gerenciamento de janelas e lançador de aplicações"
-#. left side
-#: ../js/ui/panel.js:269
-msgid "Activities"
-msgstr "Atividades"
+#: ../js/ui/appDisplay.js:696
+msgid "Drag here to add favorites"
+msgstr "Arraste até aqui para adicionar aos favoritos"
-#. Translators: This is a time format.
-#: ../js/ui/panel.js:452
-msgid "%a %l:%M %p"
-msgstr "%a %l:%M %p"
+#: ../js/ui/appIcon.js:425
+msgid "New Window"
+msgstr "Nova janela"
-#: ../js/ui/dash.js:283
+#: ../js/ui/appIcon.js:429
+msgid "Remove from Favorites"
+msgstr "Remover dos Favoritos"
+
+#: ../js/ui/appIcon.js:430
+msgid "Add to Favorites"
+msgstr "Adicionar aos Favoritos"
+
+#: ../js/ui/dash.js:237
msgid "Find..."
-msgstr "Encontre..."
-
-#: ../js/ui/dash.js:400
-msgid "Browse"
-msgstr "Navegar"
-
-#: ../js/ui/dash.js:536
-msgid "(see all)"
-msgstr "(veja todos)"
+msgstr "Procurar..."
#. **** Applications ****
-#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
+#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
msgid "APPLICATIONS"
msgstr "APLICATIVOS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
-#: ../js/ui/dash.js:773
+#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
msgid "PLACES"
msgstr "LOCAIS"
#. **** Documents ****
-#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
+#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTOS RECENTES"
#. **** Search Results ****
-#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
+#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
msgid "SEARCH RESULTS"
msgstr "RESULTADOS DA BUSCA"
-#: ../js/ui/dash.js:814
+#: ../js/ui/dash.js:723
msgid "PREFERENCES"
msgstr "PREFERÊNCIAS"
-#: ../js/ui/runDialog.js:101
+#. Button on the left side of the panel.
+#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
+#: ../js/ui/panel.js:274
+msgid "Activities"
+msgstr "Atividades"
+
+#. Translators: This is a time format.
+#: ../js/ui/panel.js:491
+msgid "%a %l:%M %p"
+msgstr "%a %l:%M %p"
+
+#: ../js/ui/placeDisplay.js:84
+msgid "Connect to..."
+msgstr "Conectar ao..."
+
+#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Por favor digite um comando:"
-#: ../src/shell-global.c:799
+#: ../js/ui/runDialog.js:173
+#, c-format
+msgid "Execution of '%s' failed:"
+msgstr "A execução de \"%s\" falhou:"
+
+#. Translators: This is a time format.
+#: ../js/ui/widget.js:163
+msgid "%H:%M"
+msgstr "%H:%M"
+
+#: ../js/ui/widget.js:317
+msgid "Applications"
+msgstr "Aplicações"
+
+#: ../js/ui/widget.js:339
+msgid "Recent Documents"
+msgstr "Documentos Recentes"
+
+#: ../src/shell-global.c:821
msgid "Less than a minute ago"
msgstr "Menos de um minuto atrás"
-#: ../src/shell-global.c:802
+#: ../src/shell-global.c:824
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minuto atrás"
msgstr[1] "%d minutos atrás"
-#: ../src/shell-global.c:805
+#: ../src/shell-global.c:827
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d hora atrás"
msgstr[1] "%d horas atrás"
-#: ../src/shell-global.c:808
+#: ../src/shell-global.c:830
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dia atrás"
msgstr[1] "%d dias atrás"
-#: ../src/shell-global.c:811
+#: ../src/shell-global.c:833
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@@ -183,6 +215,12 @@ msgstr "Procurar"
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
+#~ msgid "Browse"
+#~ msgstr "Navegar"
+
+#~ msgid "(see all)"
+#~ msgstr "(veja todos)"
+
#~ msgid "Find apps or documents"
#~ msgstr "Localizar aplicativos ou documentos"
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index 9cb69f552..a6a85f12e 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -72,7 +72,9 @@ st_source_h = \
st/st-box-layout.h \
st/st-box-layout-child.h \
st/st-button.h \
+ st/st-clickable.h \
st/st-clipboard.h \
+ st/st-drawing-area.h \
st/st-entry.h \
st/st-im-text.h \
st/st-label.h \
@@ -108,7 +110,9 @@ st_source_c = \
st/st-box-layout.c \
st/st-box-layout-child.c \
st/st-button.c \
+ st/st-clickable.c \
st/st-clipboard.c \
+ st/st-drawing-area.c \
st/st-entry.c \
st/st-im-text.c \
st/st-label.c \
diff --git a/src/Makefile.am b/src/Makefile.am
index 4b11e83f3..29c36cb80 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -63,12 +63,8 @@ libgnome_shell_la_SOURCES = \
shell-app-usage.h \
shell-arrow.c \
shell-arrow.h \
- shell-button-box.c \
- shell-button-box.h \
shell-drawing.c \
shell-drawing.h \
- shell-drawing-area.c \
- shell-drawing-area.h \
shell-embedded-window.c \
shell-embedded-window.h \
shell-embedded-window-private.h \
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index bf6c6a04f..011f6ff82 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -23,6 +23,8 @@
* 02111-1307, USA.
*/
+#include "config.h"
+
#define MUTTER_BUILDING_PLUGIN 1
#include
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
index 2ec877472..0f61576d5 100644
--- a/src/shell-app-system.c
+++ b/src/shell-app-system.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-app-system.h"
#include
diff --git a/src/shell-app-usage.c b/src/shell-app-usage.c
index f02dd6503..00a6c2ee5 100644
--- a/src/shell-app-usage.c
+++ b/src/shell-app-usage.c
@@ -1,4 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
#include
#include
@@ -62,7 +65,7 @@
*/
/* How often we save internally app data, in seconds */
-#define SAVE_APPS_TIMEOUT_SECONDS 5 /* leave this low for testing, we can bump later if need be */
+#define SAVE_APPS_TIMEOUT_SECONDS (5 * 60)
/* With this value, an app goes from bottom to top of the
* usage list in 50 hours of use */
@@ -711,7 +714,7 @@ idle_save_application_usage (gpointer data)
out:
if (!error)
- g_output_stream_close (G_OUTPUT_STREAM(data_output), NULL, &error);
+ g_output_stream_close_async (G_OUTPUT_STREAM (data_output), 0, NULL, NULL, NULL);
g_object_unref (data_output);
if (error)
{
diff --git a/src/shell-app.c b/src/shell-app.c
index a0c56e4f9..992ad8bd8 100644
--- a/src/shell-app.c
+++ b/src/shell-app.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-app-private.h"
#include "shell-global.h"
diff --git a/src/shell-arrow.c b/src/shell-arrow.c
index 1d961f3b4..380ec44ca 100644
--- a/src/shell-arrow.c
+++ b/src/shell-arrow.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-arrow.h"
#include
diff --git a/src/shell-button-box.c b/src/shell-button-box.c
deleted file mode 100644
index bd74d732f..000000000
--- a/src/shell-button-box.c
+++ /dev/null
@@ -1,317 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-/**
- * SECTION:shell-button-box
- * @short_description: A box with properties useful for implementing buttons
- *
- * A #BigBox subclass which translates lower-level Clutter button events
- * into higher level properties which are useful for implementing "button-like"
- * actors.
- */
-
-#include "shell-button-box.h"
-
-G_DEFINE_TYPE(ShellButtonBox, shell_button_box, BIG_TYPE_BOX);
-
-struct _ShellButtonBoxPrivate {
- gboolean active;
- gboolean held;
- gboolean hover;
- gboolean pressed;
-};
-
-/* Signals */
-enum
-{
- ACTIVATE,
- LAST_SIGNAL
-};
-
-enum {
- PROP_0,
-
- PROP_ACTIVE,
- PROP_HOVER,
- PROP_PRESSED,
-};
-
-static guint shell_button_box_signals [LAST_SIGNAL] = { 0 };
-
-static void
-set_active (ShellButtonBox *box,
- gboolean active)
-{
- if (box->priv->active == active)
- return;
- box->priv->active = active;
- g_object_notify (G_OBJECT (box), "active");
-}
-
-static void
-set_hover (ShellButtonBox *box,
- gboolean hover)
-{
- if (box->priv->hover == hover)
- return;
- box->priv->hover = hover;
- g_object_notify (G_OBJECT (box), "hover");
-}
-
-static void
-set_pressed (ShellButtonBox *box,
- gboolean pressed)
-{
- if (box->priv->pressed == pressed)
- return;
- box->priv->pressed = pressed;
- g_object_notify (G_OBJECT (box), "pressed");
-}
-
-static gboolean
-shell_button_box_contains (ShellButtonBox *box,
- ClutterActor *actor)
-{
- while (actor != NULL && actor != (ClutterActor*)box)
- {
- actor = clutter_actor_get_parent (actor);
- }
- return actor != NULL;
-}
-
-static gboolean
-shell_button_box_enter_event (ClutterActor *actor,
- ClutterCrossingEvent *event)
-{
- ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
-
- if (shell_button_box_contains (box, event->related))
- return TRUE;
- if (!shell_button_box_contains (box, event->source))
- return TRUE;
-
- g_object_freeze_notify (G_OBJECT (actor));
-
- if (box->priv->held)
- set_pressed (box, TRUE);
- set_hover (box, TRUE);
-
- g_object_thaw_notify (G_OBJECT (actor));
-
- return TRUE;
-}
-
-static gboolean
-shell_button_box_leave_event (ClutterActor *actor,
- ClutterCrossingEvent *event)
-{
- ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
-
- if (shell_button_box_contains (box, event->related))
- return TRUE;
-
- set_hover (box, FALSE);
- set_pressed (box, FALSE);
-
- return TRUE;
-}
-
-static gboolean
-shell_button_box_button_press_event (ClutterActor *actor,
- ClutterButtonEvent *event)
-{
- ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
-
- if (event->button != 1 || event->click_count != 1)
- return FALSE;
-
- if (box->priv->held)
- return TRUE;
-
- if (!shell_button_box_contains (box, event->source))
- return FALSE;
-
- box->priv->held = TRUE;
- clutter_grab_pointer (CLUTTER_ACTOR (box));
-
- set_pressed (box, TRUE);
-
- return TRUE;
-}
-
-static gboolean
-shell_button_box_button_release_event (ClutterActor *actor,
- ClutterButtonEvent *event)
-{
- ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
-
- if (event->button != 1 || event->click_count != 1)
- return FALSE;
-
- if (!box->priv->held)
- return TRUE;
-
- box->priv->held = FALSE;
- clutter_ungrab_pointer ();
-
- if (!shell_button_box_contains (box, event->source))
- return FALSE;
-
- set_pressed (box, FALSE);
-
- g_signal_emit (G_OBJECT (box), shell_button_box_signals[ACTIVATE], 0, event);
-
- return TRUE;
-}
-
-/**
- * shell_button_box_fake_release:
- * @box:
- *
- * If this button box is holding a pointer grab, this function will
- * will ungrab it, and reset the pressed state. The effect is
- * similar to if the user had released the mouse button, but without
- * emitting the activate signal.
- *
- * This function is useful if for example you want to do something after the user
- * is holding the mouse button for a given period of time, breaking the
- * grab.
- */
-void
-shell_button_box_fake_release (ShellButtonBox *box)
-{
- if (!box->priv->held)
- return;
-
- box->priv->held = FALSE;
- clutter_ungrab_pointer ();
-
- set_pressed (box, FALSE);
-}
-
-static void
-shell_button_box_set_property(GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- ShellButtonBox *box = SHELL_BUTTON_BOX (object);
-
- switch (prop_id)
- {
- case PROP_ACTIVE:
- set_active (box, g_value_get_boolean (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-shell_button_box_get_property(GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- ShellButtonBox *box = SHELL_BUTTON_BOX (object);
-
- switch (prop_id)
- {
- case PROP_ACTIVE:
- g_value_set_boolean (value, box->priv->active);
- break;
- case PROP_PRESSED:
- g_value_set_boolean (value, box->priv->pressed);
- break;
- case PROP_HOVER:
- g_value_set_boolean (value, box->priv->hover);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-shell_button_box_class_init (ShellButtonBoxClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
-
- gobject_class->get_property = shell_button_box_get_property;
- gobject_class->set_property = shell_button_box_set_property;
-
- actor_class->enter_event = shell_button_box_enter_event;
- actor_class->leave_event = shell_button_box_leave_event;
- actor_class->button_press_event = shell_button_box_button_press_event;
- actor_class->button_release_event = shell_button_box_button_release_event;
-
- /**
- * ShellButtonBox::activate
- * @box: The #ShellButtonBox
- * @event: Release event which triggered the activation
- *
- * This signal is emitted when the button should take the action
- * associated with button click+release.
- */
- shell_button_box_signals[ACTIVATE] =
- g_signal_new ("activate",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 1, CLUTTER_TYPE_EVENT);
-
- /**
- * ShellButtonBox:active
- *
- * The property allows the button to be used as a "toggle button"; it's up to the
- * application to update the active property in response to the activate signal;
- * it doesn't happen automatically.
- */
- g_object_class_install_property (gobject_class,
- PROP_ACTIVE,
- g_param_spec_boolean ("active",
- "Active",
- "Whether the button persistently active",
- FALSE,
- G_PARAM_READWRITE));
-
- /**
- * ShellButtonBox:hover
- *
- * This property tracks whether the mouse is over the button; note this
- * state is independent of whether the button is pressed.
- */
- g_object_class_install_property (gobject_class,
- PROP_HOVER,
- g_param_spec_boolean ("hover",
- "Hovering state",
- "Whether the mouse is over the button",
- FALSE,
- G_PARAM_READABLE));
-
- /**
- * ShellButtonBox:pressed
- *
- * This property tracks whether the button should have a "pressed in"
- * effect.
- */
- g_object_class_install_property (gobject_class,
- PROP_PRESSED,
- g_param_spec_boolean ("pressed",
- "Pressed state",
- "Whether the button is currently pressed",
- FALSE,
- G_PARAM_READABLE));
-
- g_type_class_add_private (gobject_class, sizeof (ShellButtonBoxPrivate));
-}
-
-static void
-shell_button_box_init (ShellButtonBox *self)
-{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_BUTTON_BOX,
- ShellButtonBoxPrivate);
-}
diff --git a/src/shell-button-box.h b/src/shell-button-box.h
deleted file mode 100644
index 3ebe8d772..000000000
--- a/src/shell-button-box.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-#ifndef __SHELL_BUTTON_BOX_H__
-#define __SHELL_BUTTON_BOX_H__
-
-#include
-#include "big/box.h"
-
-#define SHELL_TYPE_BUTTON_BOX (shell_button_box_get_type ())
-#define SHELL_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_BUTTON_BOX, ShellButtonBox))
-#define SHELL_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_BUTTON_BOX, ShellButtonBoxClass))
-#define SHELL_IS_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_BUTTON_BOX))
-#define SHELL_IS_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_BUTTON_BOX))
-#define SHELL_BUTTON_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_BUTTON_BOX, ShellButtonBoxClass))
-
-typedef struct _ShellButtonBox ShellButtonBox;
-typedef struct _ShellButtonBoxClass ShellButtonBoxClass;
-
-typedef struct _ShellButtonBoxPrivate ShellButtonBoxPrivate;
-
-struct _ShellButtonBox
-{
- BigBox parent;
-
- ShellButtonBoxPrivate *priv;
-};
-
-struct _ShellButtonBoxClass
-{
- BigBoxClass parent_class;
-};
-
-GType shell_button_box_get_type (void) G_GNUC_CONST;
-
-void shell_button_box_fake_release (ShellButtonBox *box);
-
-#endif /* __SHELL_BUTTON_BOX_H__ */
diff --git a/src/shell-drawing-area.c b/src/shell-drawing-area.c
deleted file mode 100644
index 04505bb50..000000000
--- a/src/shell-drawing-area.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-/**
- * SECTION:shell-drawing-area
- * @short_description: A dynamically-sized Cairo drawing area
- *
- * #ShellDrawingArea is similar to #ClutterCairoTexture in that
- * it allows drawing via Cairo; the primary difference is that
- * it is dynamically sized. To use, connect to the @redraw
- * signal, and inside the signal handler, call
- * clutter_cairo_texture_create() to begin drawing.
- */
-
-#include "shell-drawing-area.h"
-
-#include
-#include
-#include
-
-G_DEFINE_TYPE(ShellDrawingArea, shell_drawing_area, CLUTTER_TYPE_GROUP);
-
-struct _ShellDrawingAreaPrivate {
- ClutterCairoTexture *texture;
-};
-
-/* Signals */
-enum
-{
- REDRAW,
- LAST_SIGNAL
-};
-
-static guint shell_drawing_area_signals [LAST_SIGNAL] = { 0 };
-
-static void
-shell_drawing_area_allocate (ClutterActor *self,
- const ClutterActorBox *box,
- ClutterAllocationFlags flags)
-{
- ShellDrawingArea *area = SHELL_DRAWING_AREA (self);
- int width = box->x2 - box->x1;
- int height = box->y2 - box->y1;
- ClutterActorBox child_box;
-
- /* Chain up directly to ClutterActor to set actor->allocation. We explicitly skip our parent class
- * ClutterGroup here because we want to override the allocate function. */
- (CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
-
- child_box.x1 = 0;
- child_box.x2 = width;
- child_box.y1 = 0;
- child_box.y2 = height;
-
- clutter_actor_allocate (CLUTTER_ACTOR (area->priv->texture), &child_box, flags);
- if (width > 0 && height > 0)
- {
- clutter_cairo_texture_set_surface_size (area->priv->texture,
- width, height);
- g_signal_emit (G_OBJECT (self), shell_drawing_area_signals[REDRAW], 0,
- area->priv->texture);
- }
-}
-
-static void
-shell_drawing_area_class_init (ShellDrawingAreaClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
-
- actor_class->allocate = shell_drawing_area_allocate;
-
- shell_drawing_area_signals[REDRAW] =
- g_signal_new ("redraw",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ShellDrawingAreaClass, redraw),
- NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1, G_TYPE_OBJECT);
-
- g_type_class_add_private (gobject_class, sizeof (ShellDrawingAreaPrivate));
-}
-
-static void
-shell_drawing_area_init (ShellDrawingArea *area)
-{
- area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_DRAWING_AREA,
- ShellDrawingAreaPrivate);
- area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1));
- clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture));
-}
-
-/**
- * shell_drawing_area_get_texture:
- *
- * Return Value: (transfer none):
- */
-ClutterCairoTexture *
-shell_drawing_area_get_texture (ShellDrawingArea *area)
-{
- return area->priv->texture;
-}
diff --git a/src/shell-drawing-area.h b/src/shell-drawing-area.h
deleted file mode 100644
index 9cc0f8b71..000000000
--- a/src/shell-drawing-area.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-#ifndef __SHELL_DRAWING_AREA_H__
-#define __SHELL_DRAWING_AREA_H__
-
-#include
-#include
-
-#define SHELL_TYPE_DRAWING_AREA (shell_drawing_area_get_type ())
-#define SHELL_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingArea))
-#define SHELL_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
-#define SHELL_IS_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DRAWING_AREA))
-#define SHELL_IS_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DRAWING_AREA))
-#define SHELL_DRAWING_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
-
-typedef struct _ShellDrawingArea ShellDrawingArea;
-typedef struct _ShellDrawingAreaClass ShellDrawingAreaClass;
-
-typedef struct _ShellDrawingAreaPrivate ShellDrawingAreaPrivate;
-
-struct _ShellDrawingArea
-{
- ClutterGroup parent;
-
- ShellDrawingAreaPrivate *priv;
-};
-
-struct _ShellDrawingAreaClass
-{
- ClutterGroupClass parent_class;
-
- void (*redraw) (ShellDrawingArea *area, ClutterCairoTexture *texture);
-};
-
-GType shell_drawing_area_get_type (void) G_GNUC_CONST;
-
-ClutterCairoTexture *shell_drawing_area_get_texture (ShellDrawingArea *area);
-
-#endif /* __SHELL_DRAWING_AREA_H__ */
diff --git a/src/shell-drawing.c b/src/shell-drawing.c
index 69c47b940..21f7129af 100644
--- a/src/shell-drawing.c
+++ b/src/shell-drawing.c
@@ -1,104 +1,10 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-drawing.h"
#include
-/**
- * shell_create_vertical_gradient:
- * @top: the color at the top
- * @bottom: the color at the bottom
- *
- * Creates a vertical gradient actor.
- *
- * Return value: (transfer none): a #ClutterCairoTexture actor with the
- * gradient. The texture actor is floating, hence (transfer none).
- */
-ClutterCairoTexture *
-shell_create_vertical_gradient (ClutterColor *top,
- ClutterColor *bottom)
-{
- ClutterCairoTexture *texture;
- cairo_t *cr;
- cairo_pattern_t *pattern;
-
- /* Draw the gradient on an 8x8 pixel texture. Because the gradient is drawn
- * from the uppermost to the lowermost row, after stretching 1/16 of the
- * texture height has the top color and 1/16 has the bottom color. The 8
- * pixel width is chosen for reasons related to graphics hardware internals.
- */
- texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 8));
- cr = clutter_cairo_texture_create (texture);
-
- pattern = cairo_pattern_create_linear (0, 0, 0, 8);
- cairo_pattern_add_color_stop_rgba (pattern, 0,
- top->red / 255.,
- top->green / 255.,
- top->blue / 255.,
- top->alpha / 255.);
- cairo_pattern_add_color_stop_rgba (pattern, 1,
- bottom->red / 255.,
- bottom->green / 255.,
- bottom->blue / 255.,
- bottom->alpha / 255.);
-
- cairo_set_source (cr, pattern);
- cairo_paint (cr);
-
- cairo_pattern_destroy (pattern);
- cairo_destroy (cr);
-
- return texture;
-}
-
-/**
- * shell_create_horizontal_gradient:
- * @left: the color on the left
- * @right: the color on the right
- *
- * Creates a horizontal gradient actor.
- *
- * Return value: (transfer none): a #ClutterCairoTexture actor with the
- * gradient. The texture actor is floating, hence (transfer none).
- */
-ClutterCairoTexture *
-shell_create_horizontal_gradient (ClutterColor *left,
- ClutterColor *right)
-{
- ClutterCairoTexture *texture;
- cairo_t *cr;
- cairo_pattern_t *pattern;
-
- /* Draw the gradient on an 8x1 pixel texture. Because the gradient is drawn
- * from the left to the right column, after stretching 1/16 of the
- * texture width has the left side color and 1/16 has the right side color.
- * There is no reason to use the 8 pixel height that would be similar to the
- * reason we are using the 8 pixel width for the vertical gradient, so we
- * are just using the 1 pixel height instead.
- */
- texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 1));
- cr = clutter_cairo_texture_create (texture);
-
- pattern = cairo_pattern_create_linear (0, 0, 8, 0);
- cairo_pattern_add_color_stop_rgba (pattern, 0,
- left->red / 255.,
- left->green / 255.,
- left->blue / 255.,
- left->alpha / 255.);
- cairo_pattern_add_color_stop_rgba (pattern, 1,
- right->red / 255.,
- right->green / 255.,
- right->blue / 255.,
- right->alpha / 255.);
-
- cairo_set_source (cr, pattern);
- cairo_paint (cr);
-
- cairo_pattern_destroy (pattern);
- cairo_destroy (cr);
-
- return texture;
-}
-
void
shell_draw_clock (ClutterCairoTexture *texture,
int hour,
diff --git a/src/shell-drawing.h b/src/shell-drawing.h
index eae39fc38..4b48d11c2 100644
--- a/src/shell-drawing.h
+++ b/src/shell-drawing.h
@@ -7,12 +7,6 @@
G_BEGIN_DECLS
-ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
- ClutterColor *bottom);
-
-ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
- ClutterColor *right);
-
typedef enum {
SHELL_POINTER_UP,
SHELL_POINTER_DOWN,
diff --git a/src/shell-embedded-window.c b/src/shell-embedded-window.c
index 80c64fff9..a12d7d6b4 100644
--- a/src/shell-embedded-window.c
+++ b/src/shell-embedded-window.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include
#include
diff --git a/src/shell-gconf.c b/src/shell-gconf.c
index 2d065512b..7073bbdcf 100644
--- a/src/shell-gconf.c
+++ b/src/shell-gconf.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-gconf.h"
#include
diff --git a/src/shell-generic-container.c b/src/shell-generic-container.c
index db74ecb62..68b3afde2 100644
--- a/src/shell-generic-container.c
+++ b/src/shell-generic-container.c
@@ -94,6 +94,8 @@ function runTestFixedBox() {
}
*/
+#include "config.h"
+
#include "shell-generic-container.h"
#include
diff --git a/src/shell-global.c b/src/shell-global.c
index 5bb7e69d4..a36753e9a 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-global-private.h"
#include "shell-wm.h"
@@ -19,6 +21,9 @@
#include
#include
#include
+#ifdef HAVE_SYS_RESOURCE_H
+#include
+#endif
#define SHELL_DBUS_SERVICE "org.gnome.Shell"
diff --git a/src/shell-gtk-embed.c b/src/shell-gtk-embed.c
index 0d5f2978e..35f004bc0 100644
--- a/src/shell-gtk-embed.c
+++ b/src/shell-gtk-embed.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-embedded-window-private.h"
#include
diff --git a/src/shell-menu.c b/src/shell-menu.c
index 4fce08c5f..605a7ab74 100644
--- a/src/shell-menu.c
+++ b/src/shell-menu.c
@@ -8,6 +8,8 @@
* popup-menu like actors.
*/
+#include "config.h"
+
#include "shell-menu.h"
G_DEFINE_TYPE(ShellMenu, shell_menu, BIG_TYPE_BOX);
diff --git a/src/shell-overflow-list.c b/src/shell-overflow-list.c
index a5dc8f96f..a398a854b 100644
--- a/src/shell-overflow-list.c
+++ b/src/shell-overflow-list.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-overflow-list.h"
G_DEFINE_TYPE (ShellOverflowList,
diff --git a/src/shell-process.c b/src/shell-process.c
index 443c48258..afa3abdf6 100644
--- a/src/shell-process.c
+++ b/src/shell-process.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-process.h"
#include
diff --git a/src/shell-recorder-src.c b/src/shell-recorder-src.c
index 21eebe44e..5fbdf7c7b 100644
--- a/src/shell-recorder-src.c
+++ b/src/shell-recorder-src.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include
#include "shell-recorder-src.h"
diff --git a/src/shell-recorder.c b/src/shell-recorder.c
index 70f833eb2..4c0304e22 100644
--- a/src/shell-recorder.c
+++ b/src/shell-recorder.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include
#include
#include
diff --git a/src/shell-stack.c b/src/shell-stack.c
index ea3ec2c33..f1bdf76a3 100644
--- a/src/shell-stack.c
+++ b/src/shell-stack.c
@@ -12,6 +12,8 @@
* size, even if that would overflow the size allocated to the stack.
*/
+#include "config.h"
+
#include "shell-stack.h"
G_DEFINE_TYPE (ShellStack,
diff --git a/src/shell-texture-cache.c b/src/shell-texture-cache.c
index bbbec883e..d85ca84ef 100644
--- a/src/shell-texture-cache.c
+++ b/src/shell-texture-cache.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-texture-cache.h"
#include "shell-global.h"
#include
diff --git a/src/shell-tray-manager.c b/src/shell-tray-manager.c
index 9940461a3..474c5c609 100644
--- a/src/shell-tray-manager.c
+++ b/src/shell-tray-manager.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include
#include
#include
diff --git a/src/shell-uri-util.c b/src/shell-uri-util.c
index ccb9591d6..dcbe58bb5 100644
--- a/src/shell-uri-util.c
+++ b/src/shell-uri-util.c
@@ -1,7 +1,9 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include "shell-uri-util.h"
-#include
+#include
#include
#include
diff --git a/src/shell-window-tracker.c b/src/shell-window-tracker.c
index 57226b7d5..3c9bec64c 100644
--- a/src/shell-window-tracker.c
+++ b/src/shell-window-tracker.c
@@ -1,4 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include "config.h"
+
#include
#include
diff --git a/src/shell-wm.c b/src/shell-wm.c
index 3b757cc0c..6ddd65560 100644
--- a/src/shell-wm.c
+++ b/src/shell-wm.c
@@ -1,5 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#include "config.h"
+
#include
#include "shell-wm.h"
diff --git a/src/st/st-clickable.c b/src/st/st-clickable.c
new file mode 100644
index 000000000..bc5ad4994
--- /dev/null
+++ b/src/st/st-clickable.c
@@ -0,0 +1,330 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/**
+ * SECTION:st-clickable
+ * @short_description: A bin with methods and properties useful for implementing buttons
+ *
+ * A #StBin subclass which translates lower-level Clutter button events
+ * into higher level properties which are useful for implementing "button-like"
+ * actors.
+ */
+
+#include "st-clickable.h"
+
+G_DEFINE_TYPE (StClickable, st_clickable, ST_TYPE_BIN);
+
+struct _StClickablePrivate {
+ gboolean active;
+ gboolean held;
+ gboolean hover;
+ gboolean pressed;
+
+ guint initiating_button;
+};
+
+/* Signals */
+enum
+{
+ CLICKED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+
+ PROP_ACTIVE,
+ PROP_HOVER,
+ PROP_PRESSED,
+};
+
+static guint st_clickable_signals [LAST_SIGNAL] = { 0 };
+
+static void
+sync_pseudo_class (StClickable *self)
+{
+ st_widget_set_style_pseudo_class (ST_WIDGET (self),
+ (self->priv->pressed || self->priv->active) ? "pressed" :
+ (self->priv->hover ? "hover" : NULL));
+}
+
+static void
+set_active (StClickable *self,
+ gboolean active)
+{
+ if (self->priv->active == active)
+ return;
+ self->priv->active = active;
+ sync_pseudo_class (self);
+ g_object_notify (G_OBJECT (self), "active");
+}
+
+static void
+set_hover (StClickable *self,
+ gboolean hover)
+{
+ if (self->priv->hover == hover)
+ return;
+ self->priv->hover = hover;
+ sync_pseudo_class (self);
+ g_object_notify (G_OBJECT (self), "hover");
+}
+
+static void
+set_pressed (StClickable *self,
+ gboolean pressed)
+{
+ if (self->priv->pressed == pressed)
+ return;
+ self->priv->pressed = pressed;
+ sync_pseudo_class (self);
+ g_object_notify (G_OBJECT (self), "pressed");
+}
+
+static gboolean
+st_clickable_contains (StClickable *self,
+ ClutterActor *actor)
+{
+ while (actor != NULL && actor != (ClutterActor*)self)
+ {
+ actor = clutter_actor_get_parent (actor);
+ }
+ return actor != NULL;
+}
+
+static gboolean
+st_clickable_enter_event (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StClickable *self = ST_CLICKABLE (actor);
+
+ if (st_clickable_contains (self, event->related))
+ return TRUE;
+ if (!st_clickable_contains (self, event->source))
+ return TRUE;
+
+ g_object_freeze_notify (G_OBJECT (actor));
+
+ if (self->priv->held)
+ set_pressed (self, TRUE);
+ set_hover (self, TRUE);
+
+ g_object_thaw_notify (G_OBJECT (actor));
+
+ return TRUE;
+}
+
+static gboolean
+st_clickable_leave_event (ClutterActor *actor,
+ ClutterCrossingEvent *event)
+{
+ StClickable *self = ST_CLICKABLE (actor);
+
+ if (st_clickable_contains (self, event->related))
+ return TRUE;
+
+ set_hover (self, FALSE);
+ set_pressed (self, FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+st_clickable_button_press_event (ClutterActor *actor,
+ ClutterButtonEvent *event)
+{
+ StClickable *self = ST_CLICKABLE (actor);
+
+ if (event->click_count != 1)
+ return FALSE;
+
+ if (self->priv->held)
+ return TRUE;
+
+ if (!st_clickable_contains (self, event->source))
+ return FALSE;
+
+ self->priv->held = TRUE;
+ self->priv->initiating_button = event->button;
+ clutter_grab_pointer (CLUTTER_ACTOR (self));
+
+ set_pressed (self, TRUE);
+
+ return TRUE;
+}
+
+static gboolean
+st_clickable_button_release_event (ClutterActor *actor,
+ ClutterButtonEvent *event)
+{
+ StClickable *self = ST_CLICKABLE (actor);
+
+ if (event->button != self->priv->initiating_button || event->click_count != 1)
+ return FALSE;
+
+ if (!self->priv->held)
+ return TRUE;
+
+ self->priv->held = FALSE;
+ clutter_ungrab_pointer ();
+
+ if (!st_clickable_contains (self, event->source))
+ return FALSE;
+
+ set_pressed (self, FALSE);
+
+ g_signal_emit (G_OBJECT (self), st_clickable_signals[CLICKED], 0, event);
+
+ return TRUE;
+}
+
+/**
+ * st_clickable_fake_release:
+ * @box:
+ *
+ * If this widget is holding a pointer grab, this function will
+ * will ungrab it, and reset the pressed state. The effect is
+ * similar to if the user had released the mouse button, but without
+ * emitting the clicked signal.
+ *
+ * This function is useful if for example you want to do something after the user
+ * is holding the mouse button for a given period of time, breaking the
+ * grab.
+ */
+void
+st_clickable_fake_release (StClickable *self)
+{
+ if (!self->priv->held)
+ return;
+
+ self->priv->held = FALSE;
+ clutter_ungrab_pointer ();
+
+ set_pressed (self, FALSE);
+}
+
+static void
+st_clickable_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StClickable *self = ST_CLICKABLE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ set_active (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_clickable_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StClickable *self = ST_CLICKABLE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->priv->active);
+ break;
+ case PROP_PRESSED:
+ g_value_set_boolean (value, self->priv->pressed);
+ break;
+ case PROP_HOVER:
+ g_value_set_boolean (value, self->priv->hover);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_clickable_class_init (StClickableClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ gobject_class->get_property = st_clickable_get_property;
+ gobject_class->set_property = st_clickable_set_property;
+
+ actor_class->enter_event = st_clickable_enter_event;
+ actor_class->leave_event = st_clickable_leave_event;
+ actor_class->button_press_event = st_clickable_button_press_event;
+ actor_class->button_release_event = st_clickable_button_release_event;
+
+ /**
+ * StClickable::clicked
+ * @box: The #StClickable
+ *
+ * This signal is emitted when the button should take the action
+ * associated with button click+release.
+ */
+ st_clickable_signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1, CLUTTER_TYPE_EVENT);
+
+ /**
+ * StClickable:active
+ *
+ * The property allows the button to be used as a "toggle button"; it's up to the
+ * application to update the active property in response to the activate signal;
+ * it doesn't happen automatically.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ "Active",
+ "Whether the button persistently active",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ /**
+ * StClickable:hover
+ *
+ * This property tracks whether the mouse is over the button; note this
+ * state is independent of whether the button is pressed.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_HOVER,
+ g_param_spec_boolean ("hover",
+ "Hovering state",
+ "Whether the mouse is over the button",
+ FALSE,
+ G_PARAM_READABLE));
+
+ /**
+ * StClickable:pressed
+ *
+ * This property tracks whether the button should have a "pressed in"
+ * effect.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PRESSED,
+ g_param_spec_boolean ("pressed",
+ "Pressed state",
+ "Whether the button is currently pressed",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_type_class_add_private (gobject_class, sizeof (StClickablePrivate));
+}
+
+static void
+st_clickable_init (StClickable *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ST_TYPE_CLICKABLE,
+ StClickablePrivate);
+}
diff --git a/src/st/st-clickable.h b/src/st/st-clickable.h
new file mode 100644
index 000000000..1c59447d7
--- /dev/null
+++ b/src/st/st-clickable.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_CLICKABLE_H__
+#define __ST_CLICKABLE_H__
+
+#include "st-bin.h"
+
+#define ST_TYPE_CLICKABLE (st_clickable_get_type ())
+#define ST_CLICKABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_CLICKABLE, StClickable))
+#define ST_CLICKABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_CLICKABLE, StClickableClass))
+#define ST_IS_CLICKABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_CLICKABLE))
+#define ST_IS_CLICKABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_CLICKABLE))
+#define ST_CLICKABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_CLICKABLE, StClickableClass))
+
+typedef struct _StClickable StClickable;
+typedef struct _StClickableClass StClickableClass;
+
+typedef struct _StClickablePrivate StClickablePrivate;
+
+struct _StClickable
+{
+ StBin parent;
+
+ StClickablePrivate *priv;
+};
+
+struct _StClickableClass
+{
+ StBinClass parent_class;
+};
+
+GType st_clickable_get_type (void) G_GNUC_CONST;
+
+void st_clickable_fake_release (StClickable *box);
+
+#endif /* __ST_CLICKABLE_H__ */
diff --git a/src/st/st-drawing-area.c b/src/st/st-drawing-area.c
new file mode 100644
index 000000000..4391ac13e
--- /dev/null
+++ b/src/st/st-drawing-area.c
@@ -0,0 +1,125 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/**
+ * SECTION:st-drawing-area
+ * @short_description: A dynamically-sized Cairo drawing area
+ *
+ * #StDrawingArea is similar to #ClutterCairoTexture in that
+ * it allows drawing via Cairo; the primary difference is that
+ * it is dynamically sized. To use, connect to the #StDrawingArea::redraw
+ * signal, and inside the signal handler, call
+ * clutter_cairo_texture_create() to begin drawing. The
+ * #StDrawingArea::redraw signal will be emitted by default when the area is
+ * resized or the CSS style changes; you can use the
+ * st_drawing_area_emit_redraw() as well.
+ */
+
+#include "st-drawing-area.h"
+
+#include
+
+G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_BIN);
+
+struct _StDrawingAreaPrivate {
+ ClutterCairoTexture *texture;
+};
+
+/* Signals */
+enum
+{
+ REDRAW,
+ LAST_SIGNAL
+};
+
+static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 };
+
+static void
+st_drawing_area_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags)
+{
+ StThemeNode *theme_node;
+ ClutterActorBox content_box;
+ StDrawingArea *area = ST_DRAWING_AREA (self);
+ int width = box->x2 - box->x1;
+ int height = box->y2 - box->y1;
+
+ (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class))->allocate (self, box, flags);
+
+ theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+
+ st_theme_node_get_content_box (theme_node, box, &content_box);
+
+ if (width > 0 && height > 0)
+ {
+ clutter_cairo_texture_set_surface_size (area->priv->texture,
+ content_box.x2 - content_box.x1,
+ content_box.y2 - content_box.y1);
+ g_signal_emit (G_OBJECT (self), st_drawing_area_signals[REDRAW], 0,
+ area->priv->texture);
+ }
+}
+
+static void
+st_drawing_area_style_changed (StWidget *self)
+{
+ (ST_WIDGET_CLASS (st_drawing_area_parent_class))->style_changed (self);
+
+ st_drawing_area_emit_redraw (ST_DRAWING_AREA (self));
+}
+
+static void
+st_drawing_area_class_init (StDrawingAreaClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+ StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
+
+ actor_class->allocate = st_drawing_area_allocate;
+ widget_class->style_changed = st_drawing_area_style_changed;
+
+ st_drawing_area_signals[REDRAW] =
+ g_signal_new ("redraw",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StDrawingAreaClass, redraw),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, CLUTTER_TYPE_CAIRO_TEXTURE);
+
+ g_type_class_add_private (gobject_class, sizeof (StDrawingAreaPrivate));
+}
+
+static void
+st_drawing_area_init (StDrawingArea *area)
+{
+ area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, ST_TYPE_DRAWING_AREA,
+ StDrawingAreaPrivate);
+ area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1));
+ clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture));
+}
+
+/**
+ * st_drawing_area_get_texture:
+ *
+ * Return Value: (transfer none):
+ */
+ClutterCairoTexture *
+st_drawing_area_get_texture (StDrawingArea *area)
+{
+ return area->priv->texture;
+}
+
+/**
+ * st_drawing_area_emit_redraw:
+ * @area: A #StDrawingArea
+ *
+ * Immediately emit a redraw signal. Useful if
+ * some parameters for the area being drawn other
+ * than the size or style have changed.
+ */
+void
+st_drawing_area_emit_redraw (StDrawingArea *area)
+{
+ g_signal_emit ((GObject*)area, st_drawing_area_signals[REDRAW], 0, area->priv->texture);
+}
diff --git a/src/st/st-drawing-area.h b/src/st/st-drawing-area.h
new file mode 100644
index 000000000..36083729e
--- /dev/null
+++ b/src/st/st-drawing-area.h
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_DRAWING_AREA_H__
+#define __ST_DRAWING_AREA_H__
+
+#include "st-bin.h"
+
+#define ST_TYPE_DRAWING_AREA (st_drawing_area_get_type ())
+#define ST_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_DRAWING_AREA, StDrawingArea))
+#define ST_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_DRAWING_AREA, StDrawingAreaClass))
+#define ST_IS_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_DRAWING_AREA))
+#define ST_IS_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_DRAWING_AREA))
+#define ST_DRAWING_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_DRAWING_AREA, StDrawingAreaClass))
+
+typedef struct _StDrawingArea StDrawingArea;
+typedef struct _StDrawingAreaClass StDrawingAreaClass;
+
+typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate;
+
+struct _StDrawingArea
+{
+ StBin parent;
+
+ StDrawingAreaPrivate *priv;
+};
+
+struct _StDrawingAreaClass
+{
+ StBinClass parent_class;
+
+ void (*redraw) (StDrawingArea *area, ClutterCairoTexture *texture);
+};
+
+GType st_drawing_area_get_type (void) G_GNUC_CONST;
+
+ClutterCairoTexture *st_drawing_area_get_texture (StDrawingArea *area);
+
+void st_drawing_area_emit_redraw (StDrawingArea *area);
+
+#endif /* __ST_DRAWING_AREA_H__ */
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
index cebd1afde..29acbd8e0 100644
--- a/src/st/st-theme-node.c
+++ b/src/st/st-theme-node.c
@@ -21,8 +21,13 @@ struct _StThemeNode {
PangoFontDescription *font_desc;
ClutterColor background_color;
+ /* If gradient is set, then background_color is the gradient start */
+ StGradientType background_gradient_type;
+ ClutterColor background_gradient_end;
+
ClutterColor foreground_color;
ClutterColor border_color[4];
+
double border_width[4];
double border_radius[4];
guint padding[4];
@@ -513,7 +518,7 @@ st_theme_node_get_color (StThemeNode *node,
* parent's parent, and so forth. Note that if the property has a
* value of 'inherit' it will be inherited even if %FALSE is passed
* in for @inherit; this only affects the default behavior for inheritance.
- * @value: location to store the value that was determined.
+ * @value: (out): location to store the value that was determined.
* If the property is not found, the value in this location
* will not be changed.
*
@@ -740,7 +745,7 @@ get_length_internal (StThemeNode *node,
* parent's parent, and so forth. Note that if the property has a
* value of 'inherit' it will be inherited even if %FALSE is passed
* in for @inherit; this only affects the default behavior for inheritance.
- * @length: location to store the length that was determined.
+ * @length: (out): location to store the length that was determined.
* If the property is not found, the value in this location
* will not be changed. The returned length is resolved
* to pixels.
@@ -1236,6 +1241,7 @@ ensure_background (StThemeNode *node)
node->background_computed = TRUE;
node->background_color = TRANSPARENT_COLOR;
+ node->background_gradient_type = ST_GRADIENT_NONE;
ensure_properties (node);
@@ -1331,6 +1337,31 @@ ensure_background (StThemeNode *node)
node->background_image = NULL;
}
}
+ else if (strcmp (property_name, "-gradient-direction") == 0)
+ {
+ CRTerm *term = decl->value;
+ if (strcmp (term->content.str->stryng->str, "vertical") == 0)
+ {
+ node->background_gradient_type = ST_GRADIENT_VERTICAL;
+ }
+ else if (strcmp (term->content.str->stryng->str, "horizontal") == 0)
+ {
+ node->background_gradient_type = ST_GRADIENT_HORIZONTAL;
+ }
+ else
+ {
+ g_warning ("Unrecognized background-gradient-direction \"%s\"",
+ term->content.str->stryng->str);
+ }
+ }
+ else if (strcmp (property_name, "-gradient-start") == 0)
+ {
+ get_color_from_term (node, decl->value, &node->background_color);
+ }
+ else if (strcmp (property_name, "-gradient-end") == 0)
+ {
+ get_color_from_term (node, decl->value, &node->background_gradient_end);
+ }
}
}
@@ -1393,6 +1424,34 @@ st_theme_node_get_foreground_color (StThemeNode *node,
*color = node->foreground_color;
}
+
+/**
+ * st_theme_node_get_background_gradient:
+ * @node: A #StThemeNode
+ * @type: (out): Type of gradient
+ * @start: Color at start of gradient
+ * @end: Color at end of gradient
+ *
+ * The @start and @end arguments will only be set if @type is not #ST_GRADIENT_NONE.
+ */
+void
+st_theme_node_get_background_gradient (StThemeNode *node,
+ StGradientType *type,
+ ClutterColor *start,
+ ClutterColor *end)
+{
+ g_return_if_fail (ST_IS_THEME_NODE (node));
+
+ ensure_background (node);
+
+ *type = node->background_gradient_type;
+ if (*type != ST_GRADIENT_NONE)
+ {
+ *start = node->background_color;
+ *end = node->background_gradient_end;
+ }
+}
+
void
st_theme_node_get_border_color (StThemeNode *node,
StSide side,
diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h
index abf5110a3..0f9221463 100644
--- a/src/st/st-theme-node.h
+++ b/src/st/st-theme-node.h
@@ -57,6 +57,12 @@ typedef enum {
ST_TEXT_DECORATION_BLINK = 1 << 3
} StTextDecoration;
+typedef enum {
+ ST_GRADIENT_NONE,
+ ST_GRADIENT_VERTICAL,
+ ST_GRADIENT_HORIZONTAL
+} StGradientType;
+
GType st_theme_node_get_type (void) G_GNUC_CONST;
StThemeNode *st_theme_node_new (StThemeContext *context,
@@ -103,6 +109,10 @@ void st_theme_node_get_background_color (StThemeNode *node,
ClutterColor *color);
void st_theme_node_get_foreground_color (StThemeNode *node,
ClutterColor *color);
+void st_theme_node_get_background_gradient (StThemeNode *node,
+ StGradientType *type,
+ ClutterColor *start,
+ ClutterColor *end);
const char *st_theme_node_get_background_image (StThemeNode *node);
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 482d5c04a..ba2c7a63e 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -59,6 +59,9 @@ struct _StWidgetPrivate
ClutterActor *background_image;
ClutterColor bg_color;
+ StGradientType bg_gradient_type;
+ ClutterColor bg_gradient_end;
+
gboolean is_stylable : 1;
gboolean has_tooltip : 1;
gboolean is_style_dirty : 1;
@@ -111,6 +114,7 @@ G_DEFINE_ABSTRACT_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR);
static void st_widget_recompute_style (StWidget *widget,
StThemeNode *old_theme_node);
+static void st_widget_redraw_gradient (StWidget *widget);
static void
st_widget_set_property (GObject *gobject,
@@ -258,10 +262,13 @@ st_widget_allocate (ClutterActor *actor,
ClutterAllocationFlags flags)
{
StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
+ StThemeNode *theme_node;
ClutterActorClass *klass;
ClutterGeometry area;
ClutterVertex in_v, out_v;
+ theme_node = st_widget_get_theme_node ((StWidget*) actor);
+
klass = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
klass->allocate (actor, box, flags);
@@ -298,7 +305,7 @@ st_widget_allocate (ClutterActor *actor,
flags);
}
- if (priv->background_image)
+ if (priv->background_image && priv->bg_gradient_type == ST_GRADIENT_NONE)
{
ClutterActorBox frame_box = {
0, 0, box->x2 - box->x1, box->y2 - box->y1
@@ -353,6 +360,27 @@ st_widget_allocate (ClutterActor *actor,
&frame_box,
flags);
}
+ else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
+ {
+ float width, height;
+ ClutterActorBox frame_box, content_box;
+
+ width = box->x2 - box->x1;
+ height = box->y2 - box->y1;
+ frame_box.x1 = frame_box.y1 = 0;
+ frame_box.x2 = width;
+ frame_box.y2 = height;
+
+ st_theme_node_get_content_box (theme_node, &frame_box, &content_box);
+
+ if (width > 0 && height > 0)
+ clutter_cairo_texture_set_surface_size (CLUTTER_CAIRO_TEXTURE (priv->background_image),
+ width, height);
+ st_widget_redraw_gradient ((StWidget*) actor);
+ clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
+ &content_box,
+ flags);
+ }
}
static void
@@ -526,6 +554,84 @@ st_widget_unmap (ClutterActor *actor)
clutter_actor_unmap ((ClutterActor *) priv->tooltip);
}
+static void
+draw_vertical_gradient (ClutterCairoTexture *texture,
+ ClutterColor *start,
+ ClutterColor *end)
+{
+ guint width, height;
+ cairo_t *cr;
+ cairo_pattern_t *pattern;
+
+ clutter_cairo_texture_get_surface_size (texture, &width, &height);
+ clutter_cairo_texture_clear (texture);
+ cr = clutter_cairo_texture_create (texture);
+
+ pattern = cairo_pattern_create_linear (0, 0, 0, height);
+ cairo_pattern_add_color_stop_rgba (pattern, 0,
+ start->red / 255.,
+ start->green / 255.,
+ start->blue / 255.,
+ start->alpha / 255.);
+ cairo_pattern_add_color_stop_rgba (pattern, 1,
+ end->red / 255.,
+ end->green / 255.,
+ end->blue / 255.,
+ end->alpha / 255.);
+
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+
+ cairo_pattern_destroy (pattern);
+ cairo_destroy (cr);
+}
+
+static void
+draw_horizontal_gradient (ClutterCairoTexture *texture,
+ ClutterColor *start,
+ ClutterColor *end)
+{
+ guint width, height;
+ cairo_t *cr;
+ cairo_pattern_t *pattern;
+
+ clutter_cairo_texture_get_surface_size (texture, &width, &height);
+ clutter_cairo_texture_clear (texture);
+ cr = clutter_cairo_texture_create (texture);
+
+ pattern = cairo_pattern_create_linear (0, 0, width, 0);
+ cairo_pattern_add_color_stop_rgba (pattern, 0,
+ start->red / 255.,
+ start->green / 255.,
+ start->blue / 255.,
+ start->alpha / 255.);
+ cairo_pattern_add_color_stop_rgba (pattern, 1,
+ end->red / 255.,
+ end->green / 255.,
+ end->blue / 255.,
+ end->alpha / 255.);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+
+ cairo_pattern_destroy (pattern);
+ cairo_destroy (cr);
+}
+
+static void
+st_widget_redraw_gradient (StWidget *widget)
+{
+ if (widget->priv->bg_gradient_type == ST_GRADIENT_VERTICAL)
+ draw_vertical_gradient ((ClutterCairoTexture*) widget->priv->background_image,
+ &widget->priv->bg_color,
+ &widget->priv->bg_gradient_end);
+ else if (widget->priv->bg_gradient_type == ST_GRADIENT_HORIZONTAL)
+ draw_horizontal_gradient ((ClutterCairoTexture*) widget->priv->background_image,
+ &widget->priv->bg_color,
+ &widget->priv->bg_gradient_end);
+}
+
static void notify_children_of_style_change (ClutterContainer *container);
static void
@@ -562,6 +668,8 @@ st_widget_real_style_changed (StWidget *self)
guint border_width = 0;
guint border_radius = 0;
ClutterColor border_color = { 0, };
+ StGradientType gradient;
+ ClutterColor gradient_end;
StSide side;
StCorner corner;
gboolean uniform_border_width;
@@ -572,11 +680,29 @@ st_widget_real_style_changed (StWidget *self)
theme_node = st_widget_get_theme_node (self);
- st_theme_node_get_background_color (theme_node, &color);
- if (!clutter_color_equal (&color, &priv->bg_color))
+ st_theme_node_get_background_gradient (theme_node, &gradient, &color, &gradient_end);
+
+ if (gradient == ST_GRADIENT_NONE)
{
+ if (gradient != priv->bg_gradient_type)
+ has_changed = TRUE;
+ priv->bg_gradient_type = gradient;
+ st_theme_node_get_background_color (theme_node, &color);
+ if (!clutter_color_equal (&color, &priv->bg_color))
+ {
+ priv->bg_color = color;
+ priv->draw_bg_color = color.alpha != 0;
+ has_changed = TRUE;
+ }
+ }
+ else if (gradient != priv->bg_gradient_type ||
+ !clutter_color_equal (&color, &priv->bg_color) ||
+ !clutter_color_equal (&gradient_end, &priv->bg_gradient_end))
+ {
+ priv->bg_gradient_type = gradient;
priv->bg_color = color;
- priv->draw_bg_color = color.alpha != 0;
+ priv->bg_gradient_end = gradient_end;
+ priv->draw_bg_color = TRUE;
has_changed = TRUE;
}
@@ -741,6 +867,15 @@ st_widget_real_style_changed (StWidget *self)
has_changed = TRUE;
relayout_needed = TRUE;
}
+ else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
+ {
+ texture = g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE, NULL);
+ priv->background_image = CLUTTER_ACTOR (texture);
+ clutter_actor_set_parent (priv->background_image,
+ CLUTTER_ACTOR (self));
+ has_changed = TRUE;
+ relayout_needed = TRUE;
+ }
/* If there are any properties above that need to cause a relayout thay
* should set this flag.
@@ -1248,12 +1383,22 @@ static void
st_widget_recompute_style (StWidget *widget,
StThemeNode *old_theme_node)
{
+ ClutterActorBox allocation_box;
StThemeNode *new_theme_node = st_widget_get_theme_node (widget);
+ clutter_actor_get_allocation_box ((ClutterActor *) widget, &allocation_box);
+
if (!old_theme_node ||
!st_theme_node_geometry_equal (old_theme_node, new_theme_node))
clutter_actor_queue_relayout ((ClutterActor *) widget);
+ /* Could compare gradient values here if we hit a performance issue.
+ * Also, only redraw if we've been allocated.
+ */
+ if (allocation_box.x2 - allocation_box.x1 > 0 &&
+ allocation_box.y2 - allocation_box.y1 > 0)
+ st_widget_redraw_gradient (widget);
+
g_signal_emit (widget, signals[STYLE_CHANGED], 0);
widget->priv->is_style_dirty = FALSE;
}
diff --git a/src/tray/na-tray-child.c b/src/tray/na-tray-child.c
index 61c3aea79..bfd0d88df 100644
--- a/src/tray/na-tray-child.c
+++ b/src/tray/na-tray-child.c
@@ -24,7 +24,7 @@
#include "na-tray-child.h"
-#include
+#include
#include
#include
#include
diff --git a/src/tray/na-tray-manager.c b/src/tray/na-tray-manager.c
index 1bf54f1f6..82ebc2918 100644
--- a/src/tray/na-tray-manager.c
+++ b/src/tray/na-tray-manager.c
@@ -27,7 +27,7 @@
#include "na-tray-manager.h"
#include
-#include
+#include
#if defined (GDK_WINDOWING_X11)
#include
#include