From 2d3988c5fc030e4e791a28c5e69b1de1372581f1 Mon Sep 17 00:00:00 2001 From: Marina Date: Tue, 10 Feb 2009 17:38:06 -0500 Subject: [PATCH] Bug 570579: Redo the layout of overlay components Divide the screen into a grid and use it to determine the layout of the overlay components in a more consistent manner. Remove the 'Add workspace' control and slide the workspaces display to the side without scaling it when switching to the 'More' mode. --- js/ui/appDisplay.js | 8 +-- js/ui/docDisplay.js | 8 +-- js/ui/genericDisplay.js | 33 ++++++------ js/ui/overlay.js | 117 +++++++++++++++++++++++++++++++--------- js/ui/workspaces.js | 92 +++++++++++-------------------- 5 files changed, 149 insertions(+), 109 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 97d533566..c8befb809 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -110,15 +110,15 @@ AppDisplayItem.prototype = { * width - width available for the display * height - height available for the display */ -function AppDisplay(width, height) { - this._init(width, height); +function AppDisplay(width, height, numberOfColumns, columnGap) { + this._init(width, height, numberOfColumns, columnGap); } AppDisplay.prototype = { __proto__: GenericDisplay.GenericDisplay.prototype, - _init : function(width, height) { - GenericDisplay.GenericDisplay.prototype._init.call(this, width, height); + _init : function(width, height, numberOfColumns, columnGap) { + GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap); // map this._categories = {}; diff --git a/js/ui/docDisplay.js b/js/ui/docDisplay.js index c23d91c44..ade5d0550 100644 --- a/js/ui/docDisplay.js +++ b/js/ui/docDisplay.js @@ -91,15 +91,15 @@ DocDisplayItem.prototype = { * width - width available for the display * height - height available for the display */ -function DocDisplay(width, height) { - this._init(width, height); +function DocDisplay(width, height, numberOfColumns, columnGap) { + this._init(width, height, numberOfColumns, columnGap); } DocDisplay.prototype = { __proto__: GenericDisplay.GenericDisplay.prototype, - _init : function(width, height) { - GenericDisplay.GenericDisplay.prototype._init.call(this, width, height); + _init : function(width, height, numberOfColumns, columnGap) { + GenericDisplay.GenericDisplay.prototype._init.call(this, width, height, numberOfColumns, columnGap); let me = this; this._recentManager = Gtk.RecentManager.get_default(); this._docsStale = true; diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js index 2a298672f..1f4064f9b 100644 --- a/js/ui/genericDisplay.js +++ b/js/ui/genericDisplay.js @@ -22,10 +22,9 @@ const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color(); ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x00ff0055); const ITEM_DISPLAY_HEIGHT = 50; -const MIN_ITEM_DISPLAY_WIDTH = 230; const ITEM_DISPLAY_ICON_SIZE = 48; const ITEM_DISPLAY_PADDING = 1; -const COLUMN_GAP = 6; +const DEFAULT_COLUMN_GAP = 6; /* 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 @@ -165,22 +164,27 @@ Signals.addSignalMethods(GenericDisplayItem.prototype); * width - width available for the display * height - height available for the display */ -function GenericDisplay(width, height) { - this._init(width, height); +function GenericDisplay(width, height, numberOfColumns, columnGap) { + this._init(width, height, numberOfColumns, columnGap); } GenericDisplay.prototype = { - _init : function(width, height) { + _init : function(width, height, numberOfColumns, columnGap) { this._search = ''; this._width = null; this._height = null; this._columnWidth = null; - this._maxColumns = null; + + this._numberOfColumns = numberOfColumns; + this._columnGap = columnGap; + if (this._columnGap == null) + this._columnGap = DEFAULT_COLUMN_GAP; + this._maxItems = null; this._setDimensionsAndMaxItems(width, height); this._grid = new Tidy.Grid({width: this._width, height: this._height}); this._grid.column_major = true; - this._grid.column_gap = COLUMN_GAP; + this._grid.column_gap = this._columnGap; // map where Object represents the item info this._allItems = {}; // map @@ -282,7 +286,8 @@ GenericDisplay.prototype = { }, // Readjusts display layout and the items displayed based on the new dimensions. - updateDimensions: function(width, height) { + updateDimensions: function(width, height, numberOfColumns) { + this._numberOfColumns = numberOfColumns; this._setDimensionsAndMaxItems(width, height); this._grid.width = this._width; this._grid.height = this._height; @@ -405,17 +410,13 @@ GenericDisplay.prototype = { //// Private methods //// - // Sets this._width, this._height, this._maxColumns, this._columnWidth, and - // this._maxItems based on the space available for the display and the number - // of items it can fit. + // Sets this._width, this._height, this._columnWidth, and this._maxItems based on the + // space available for the display, number of columns, and the number of items it can fit. _setDimensionsAndMaxItems: function(width, height) { this._width = width; + this._columnWidth = (this._width - this._columnGap * (this._numberOfColumns - 1)) / this._numberOfColumns; let maxItemsInColumn = Math.floor(height / ITEM_DISPLAY_HEIGHT); - // we always want to try to display at lease one column, even if its - // width is less then MIN_ITEM_DISPLAY_WIDTH - this._maxColumns = Math.max(Math.floor(width / (MIN_ITEM_DISPLAY_WIDTH + COLUMN_GAP)), 1); - this._columnWidth = (width - COLUMN_GAP * (this._maxColumns - 1)) / this._maxColumns; - this._maxItems = maxItemsInColumn * this._maxColumns; + this._maxItems = maxItemsInColumn * this._numberOfColumns; this._height = maxItemsInColumn * ITEM_DISPLAY_HEIGHT; }, diff --git a/js/ui/overlay.js b/js/ui/overlay.js index fffbed6fc..088f065b5 100644 --- a/js/ui/overlay.js +++ b/js/ui/overlay.js @@ -20,11 +20,14 @@ const OVERLAY_BACKGROUND_COLOR = new Clutter.Color(); OVERLAY_BACKGROUND_COLOR.from_pixel(0x000000ff); const LABEL_HEIGHT = 16; +// We use SIDESHOW_PAD for the padding on the left side of the sideshow and as a gap +// between sideshow columns. const SIDESHOW_PAD = 6; -const SIDESHOW_PAD_BOTTOM = 40; const SIDESHOW_MIN_WIDTH = 250; const SIDESHOW_SECTION_MARGIN = 10; const SIDESHOW_SECTION_LABEL_MARGIN_BOTTOM = 6; +const SIDESHOW_COLUMNS = 1; +const EXPANDED_SIDESHOW_COLUMNS = 2; const SIDESHOW_SEARCH_BG_COLOR = new Clutter.Color(); SIDESHOW_SEARCH_BG_COLOR.from_pixel(0xffffffff); const SIDESHOW_TEXT_COLOR = new Clutter.Color(); @@ -33,15 +36,40 @@ SIDESHOW_TEXT_COLOR.from_pixel(0xffffffff); // Time for initial animation going into overlay mode const ANIMATION_TIME = 0.5; -// How much of the screen the workspace grid takes up -const WORKSPACE_GRID_SCALE = 0.75; +// We divide the screen into a grid of rows and columns, which we use +// to help us position the overlay components, such as the side panel +// that lists applications and documents, the workspaces display, and +// the button for adding additional workspaces. +// In the regular mode, the side panel takes up one column on the left, +// and the workspaces display takes up the remaining columns. +// In the expanded side panel display mode, the side panel takes up two +// columns, and the workspaces display slides all the way to the right, +// being visible only in the last quarter of the right-most column. +// In the future, this mode will have more components, such as a display +// of documents which were recently opened with a given application, which +// will take up the remaining sections of the display. -// How much of the screen the workspace grid takes up when it is moved aside to provide space -// for an expanded 'More' view -const WORKSPACE_GRID_ASIDE_SCALE = 0.1; +const WIDE_SCREEN_CUT_OFF_RATIO = 1.4; + +const COLUMNS_REGULAR_SCREEN = 4; +const ROWS_REGULAR_SCREEN = 8; +const COLUMNS_WIDE_SCREEN = 5; +const ROWS_WIDE_SCREEN = 10; // Padding around workspace grid / Spacing between Sideshow and Workspaces -const WORKSPACE_GRID_PADDING = 10; +const WORKSPACE_GRID_PADDING = 12; + +const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3; +const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6; +const WORKSPACES_X_FACTOR_ASIDE_MODE_REGULAR_SCREEN = 4 - 0.25; + +const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4; +const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8; +const WORKSPACES_X_FACTOR_ASIDE_MODE_WIDE_SCREEN = 5 - 0.25; + +let wideScreen = false; +let displayGridColumnWidth = null; +let displayGridRowHeight = null; function Sideshow(parent, width) { this._init(parent, width); @@ -53,7 +81,7 @@ Sideshow.prototype = { this._moreMode = false; - this._width = width; + this._width = width - SIDESHOW_PAD; let global = Shell.Global.get(); this.actor = new Clutter.Group(); @@ -63,7 +91,7 @@ Sideshow.prototype = { corner_radius: 4, x: SIDESHOW_PAD, y: Panel.PANEL_HEIGHT + SIDESHOW_PAD, - width: width, + width: this._width, height: 24}); this.actor.add_actor(rect); @@ -152,9 +180,11 @@ Sideshow.prototype = { let sectionLabelHeight = LABEL_HEIGHT + SIDESHOW_SECTION_LABEL_MARGIN_BOTTOM; let menuY = this._appsText.y + sectionLabelHeight; + let bottomHeight = displayGridRowHeight / 2; + // extra LABEL_HEIGHT is for the More link - this._itemDisplayHeight = global.screen_height - menuY - SIDESHOW_SECTION_MARGIN - sectionLabelHeight - SIDESHOW_PAD_BOTTOM - LABEL_HEIGHT; - this._appDisplay = new AppDisplay.AppDisplay(width, this._itemDisplayHeight / 2); + this._itemDisplayHeight = global.screen_height - menuY - SIDESHOW_SECTION_MARGIN - sectionLabelHeight - bottomHeight - LABEL_HEIGHT; + this._appDisplay = new AppDisplay.AppDisplay(this._width, this._itemDisplayHeight / 2, SIDESHOW_COLUMNS, SIDESHOW_PAD); this._appDisplay.actor.x = SIDESHOW_PAD; this._appDisplay.actor.y = menuY; this.actor.add_actor(this._appDisplay.actor); @@ -167,7 +197,7 @@ Sideshow.prototype = { reactive: true}); // This sets right-alignment manually. - this._moreAppsText.x = width - this._moreAppsText.width + SIDESHOW_PAD; + this._moreAppsText.x = this._width - this._moreAppsText.width + SIDESHOW_PAD; this.actor.add_actor(this._moreAppsText); this._docsText = new Clutter.Label({ color: SIDESHOW_TEXT_COLOR, @@ -178,7 +208,7 @@ Sideshow.prototype = { height: LABEL_HEIGHT}); this.actor.add_actor(this._docsText); - this._docDisplay = new DocDisplay.DocDisplay(width, this._itemDisplayHeight - this._appDisplay.actor.height); + this._docDisplay = new DocDisplay.DocDisplay(this._width, this._itemDisplayHeight - this._appDisplay.actor.height, SIDESHOW_COLUMNS, SIDESHOW_PAD); this._docDisplay.actor.x = SIDESHOW_PAD; this._docDisplay.actor.y = this._docsText.y + sectionLabelHeight; this.actor.add_actor(this._docDisplay.actor); @@ -347,15 +377,17 @@ Sideshow.prototype = { // depending on the current mode. This function must only be called once after the 'More' mode has been // changed, which is ensured by _setMoreMode() and _unsetMoreMode() functions. _updateAppsSection: function() { - let additionalWidth = this._width + GenericDisplay.COLUMN_GAP; + let additionalWidth = ((this._width + SIDESHOW_PAD) / SIDESHOW_COLUMNS) * + (EXPANDED_SIDESHOW_COLUMNS - SIDESHOW_COLUMNS); if (this._moreMode) { this._appDisplay.updateDimensions(this._width + additionalWidth, - this._itemDisplayHeight + LABEL_HEIGHT * 2 + SIDESHOW_SECTION_LABEL_MARGIN_BOTTOM); + this._itemDisplayHeight + LABEL_HEIGHT * 2 + SIDESHOW_SECTION_LABEL_MARGIN_BOTTOM, + EXPANDED_SIDESHOW_COLUMNS); this._moreAppsText.x = this._moreAppsText.x + additionalWidth; this._moreAppsText.y = this._appDisplay.actor.y + this._appDisplay.actor.height; this._moreAppsText.text = "Less..."; } else { - this._appDisplay.updateDimensions(this._width, this._itemDisplayHeight / 2); + this._appDisplay.updateDimensions(this._width, this._itemDisplayHeight / 2, SIDESHOW_COLUMNS); this._moreAppsText.x = this._moreAppsText.x - additionalWidth; this._moreAppsText.y = this._appDisplay.actor.y + this._appDisplay.actor.height; this._moreAppsText.text = "More..."; @@ -375,6 +407,18 @@ Overlay.prototype = { let global = Shell.Global.get(); + wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO); + + // We divide the screen into an imaginary grid which helps us determine the layout of + // different visual components. + if (wideScreen) { + displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN; + displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN; + } else { + displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN; + displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN; + } + this._group = new Clutter.Group(); this.visible = false; @@ -390,9 +434,8 @@ Overlay.prototype = { global.overlay_group.add_actor(this._group); // TODO - recalculate everything when desktop size changes - let screenWidth = global.screen_width; - let sideshowWidth = screenWidth - (screenWidth * WORKSPACE_GRID_SCALE) - - 2 * WORKSPACE_GRID_PADDING; + let sideshowWidth = displayGridColumnWidth; + this._sideshow = new Sideshow(this._group, sideshowWidth); this._workspaces = null; this._sideshow.connect('activated', function(sideshow) { @@ -402,12 +445,20 @@ Overlay.prototype = { me._deactivate(); }); this._sideshow.connect('more-activated', function(sideshow) { - if (me._workspaces != null) - me._workspaces.moveAside(); + if (me._workspaces != null) { + let asideXFactor = wideScreen ? WORKSPACES_X_FACTOR_ASIDE_MODE_WIDE_SCREEN : WORKSPACES_X_FACTOR_ASIDE_MODE_REGULAR_SCREEN; + + let workspacesX = displayGridColumnWidth * asideXFactor + WORKSPACE_GRID_PADDING; + me._workspaces.addButton.hide(); + me._workspaces.updatePosition(workspacesX, null); + } }); this._sideshow.connect('less-activated', function(sideshow) { - if (me._workspaces != null) - me._workspaces.moveToCenter(); + if (me._workspaces != null) { + let workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING; + me._workspaces.addButton.show(); + me._workspaces.updatePosition(workspacesX, null); + } }); }, @@ -418,10 +469,28 @@ Overlay.prototype = { this.visible = true; let global = Shell.Global.get(); + let screenWidth = global.screen_width; + let screenHeight = global.screen_height; this._sideshow.show(); + + let columnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN; + let rowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN; + + let workspacesWidth = displayGridColumnWidth * columnsUsed - WORKSPACE_GRID_PADDING * 2; + // We scale the vertical padding by (screenHeight / screenWidth) so that the workspace preserves its aspect ratio. + let workspacesHeight = displayGridRowHeight * rowsUsed - WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2; - this._workspaces = new Workspaces.Workspaces(); + let workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING; + let workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth); + + // place the 'Add Workspace' button in the bottom row of the grid + let addButtonSize = Math.floor(displayGridRowHeight * 3/5); + let addButtonX = workspacesX + workspacesWidth - addButtonSize; + let addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5); + + this._workspaces = new Workspaces.Workspaces(workspacesWidth, workspacesHeight, workspacesX, workspacesY, + addButtonSize, addButtonX, addButtonY); this._group.add_actor(this._workspaces.actor); this._workspaces.actor.raise_top(); diff --git a/js/ui/workspaces.js b/js/ui/workspaces.js index dde2ebe9e..ce9632990 100644 --- a/js/ui/workspaces.js +++ b/js/ui/workspaces.js @@ -49,6 +49,8 @@ const POSITIONS = { const GRID_SPACING = 15; const FRAME_SIZE = GRID_SPACING / 3; +let buttonSize = false; + function WindowClone(realWindow) { this._init(realWindow); } @@ -326,8 +328,8 @@ Workspace.prototype = { if (this._removeButton) return; - this._removeButton = new Clutter.Texture({ width: Workspaces.buttonSize, - height: Workspaces.buttonSize, + this._removeButton = new Clutter.Texture({ width: buttonSize, + height: buttonSize, reactive: true }); this._removeButton.set_from_file(global.imagedir + "remove-workspace.svg"); @@ -760,27 +762,22 @@ Workspace.prototype = { Signals.addSignalMethods(Workspace.prototype); -function Workspaces() { - this._init(); +function Workspaces(width, height, x, y, addButtonSize, addButtonX, addButtonY) { + this._init(width, height, x, y, addButtonSize, addButtonX, addButtonY); } Workspaces.prototype = { - _init : function() { + _init : function(width, height, x, y, addButtonSize, addButtonX, addButtonY) { let global = Shell.Global.get(); this.actor = new Clutter.Group(); let screenHeight = global.screen_height; - - this._width = null; - this._height = null; - this._x = null; - this._y = null; - this._bottomHeight = null; - - this._setDimensions(true); - - this._bottomHeight = screenHeight - this._height - this._y; + + this._width = width; + this._height = height; + this._x = x; + this._y = y; this._workspaces = []; @@ -818,23 +815,18 @@ Workspaces.prototype = { transition: "easeOutQuad" }); - // Create (+) and (-) buttons - // FIXME: figure out a better way to communicate buttonSize - // to the Workspace - Workspaces.buttonSize = Math.floor(this._bottomHeight * 3/5); - this._plusX = this._x + this._width - Workspaces.buttonSize; - this._plusY = screenHeight - Math.floor(this._bottomHeight * 4/5); - - let plus = new Clutter.Texture({ x: this._plusX, - y: this._plusY, - width: Workspaces.buttonSize, - height: Workspaces.buttonSize, - reactive: true - }); - plus.set_from_file(global.imagedir + "add-workspace.svg"); - plus.connect('button-release-event', this._appendNewWorkspace); - this.actor.add_actor(plus); - plus.lower_bottom(); + // Create (+) button + buttonSize = addButtonSize; + this.addButton = new Clutter.Texture({ x: addButtonX, + y: addButtonY, + width: buttonSize, + height: buttonSize, + reactive: true + }); + this.addButton.set_from_file(global.imagedir + "add-workspace.svg"); + this.addButton.connect('button-release-event', this._appendNewWorkspace); + this.actor.add_actor(this.addButton); + this.addButton.lower_bottom(); let lastWorkspace = this._workspaces[this._workspaces.length - 1]; lastWorkspace.updateRemovable(true); @@ -852,15 +844,14 @@ Workspaces.prototype = { Lang.bind(this, this._activeWorkspaceChanged)); }, - // Moves the workspaces display to the side. - moveAside : function() { - this._setDimensions(false); - this._updateInOverlay(); - }, + // Updates position of the workspaces display based on the new coordinates. + // Preserves the old value for the coordinate, if the passed value is null. + updatePosition : function(x, y) { + if (x != null) + this._x = x; + if (y != null) + this._y = y; - // Moves the workspaces display to the center. - moveToCenter : function() { - this._setDimensions(true); this._updateInOverlay(); }, @@ -900,27 +891,6 @@ Workspaces.prototype = { global.window_manager.disconnect(this._switchWorkspaceNotifyId); }, - // Sets dimensions and position of the workspaces display depending on the mode - // in which it will be presented (in the center of the overlay mode or on the side). - // - // centerMode - a boolean flag indicating if the workspaces will be displayed in the center of the overlay - // - _setDimensions : function(centerMode) { - let global = Shell.Global.get(); - let screenWidth = global.screen_width; - let screenHeight = global.screen_height; - - let scalingFactor = centerMode ? Overlay.WORKSPACE_GRID_SCALE : Overlay.WORKSPACE_GRID_ASIDE_SCALE; - - this._width = screenWidth * scalingFactor - 2 * Overlay.WORKSPACE_GRID_PADDING; - this._height = screenHeight * scalingFactor; - this._x = screenWidth - this._width - Overlay.WORKSPACE_GRID_PADDING; - if (centerMode) - this._y = Panel.PANEL_HEIGHT + (screenHeight - this._height - Panel.PANEL_HEIGHT) / 2; - else - this._y = screenHeight - this._height - this._bottomHeight; - }, - // Updates the workspaces display based on the current dimensions and position. _updateInOverlay : function() { let global = Shell.Global.get();