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 @@ - - -image/svg+xml - - \ 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 @@ + + +image/svg+xml + + \ 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