From 70bc94946f17607019c15d462e36c5af96277e46 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 20 Dec 2017 13:08:50 +0100 Subject: [PATCH] keyboard: add a special KeyContainer actor to contain keyboard layers Instead of manually resizing each key everytime the keyboard needs to relayout, have a special grid container that will preserve aspect when resized. This actor works in two stages though, first the keys need to be added and then layoutButtons() need to be called for the actors to be reparented to the container with the right attachment options. --- js/ui/keyboard.js | 189 ++++++++++++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 63 deletions(-) diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js index 4067fe260..964f41cc7 100644 --- a/js/ui/keyboard.js +++ b/js/ui/keyboard.js @@ -23,6 +23,9 @@ var KEY_LONG_PRESS_TIME = 250; const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; const SHOW_KEYBOARD = 'screen-keyboard-enabled'; +/* KeyContainer puts keys in a grid where a 1:1 key takes this size */ +const KEY_SIZE = 2; + const defaultKeysPre = [ [ [], [], [{ label: '⇧', width: 1.5, level: 1 }], [{ label: '?123', width: 1.5, level: 2 }] ], [ [], [], [{ label: '⇪', width: 1.5, level: 0 }], [{ label: '?123', width: 1.5, level: 2 }] ], @@ -49,6 +52,106 @@ const defaultKeysPost = [ [{ label: '🌐', width: 1.5 }, { label: '⌨', width: 1.5, action: 'hide' }] ], ]; +var KeyContainer = new Lang.Class({ + Name: 'KeyContainer', + Extends: St.Widget, + + _init: function() { + let gridLayout = new Clutter.GridLayout({ orientation: Clutter.Orientation.HORIZONTAL, + column_homogeneous: true, + row_homogeneous: true }); + this.parent({ layout_manager: gridLayout }); + this._gridLayout = gridLayout; + this._currentRow = 0; + this._currentCol = 0; + this._maxCols = 0; + + this._currentRow = null; + this._rows = []; + }, + + appendRow: function(length) { + this._currentRow++; + this._currentCol = 0; + + let row = new Object(); + row.keys = []; + row.width = 0; + this._rows.push(row); + }, + + appendKey: function(key, width = 1, height = 1) { + let keyInfo = { + key, + left: this._currentCol, + top: this._currentRow, + width, + height + }; + + let row = this._rows[this._rows.length - 1]; + row.keys.push(keyInfo); + row.width += width; + + this._currentCol += width; + this._maxCols = Math.max(this._currentCol, this._maxCols); + }, + + vfunc_allocate: function(box, flags) { + if (box.get_width() > 0 && box.get_height() > 0 && this._maxCols > 0) { + let keyboardRatio = this._maxCols / this._rows.length; + let sizeRatio = box.get_width() / box.get_height(); + + if (sizeRatio >= keyboardRatio) { + /* Restrict horizontally */ + let width = box.get_height() * keyboardRatio; + let diff = box.get_width() - width; + + box.x1 += Math.floor(diff / 2); + box.x2 -= Math.ceil(diff / 2); + } else { + /* Restrict vertically */ + let height = box.get_width() / keyboardRatio; + let diff = box.get_height() - height; + + box.y1 += Math.floor(diff / 2); + box.y2 -= Math.floor(diff / 2); + } + } + + this.parent (box, flags); + }, + + layoutButtons: function() { + let nCol = 0, nRow = 0; + + for (let i = 0; i < this._rows.length; i++) { + let row = this._rows[i]; + + /* When starting a new row, see if we need some padding */ + if (nCol == 0) { + let diff = this._maxCols - row.width; + if (diff >= 1) + nCol = diff * KEY_SIZE / 2; + else + nCol = diff * KEY_SIZE; + } + + for (let j = 0; j < row.keys.length; j++) { + let keyInfo = row.keys[j]; + let width = keyInfo.width * KEY_SIZE; + let height = keyInfo.height * KEY_SIZE; + + this._gridLayout.attach(keyInfo.key, nCol, nRow, width, height); + nCol += width; + } + + nRow += KEY_SIZE; + nCol = 0; + } + } +}); + var Suggestions = new Lang.Class({ Name: 'Suggestions', @@ -297,8 +400,7 @@ var Keyboard = new Lang.Class({ this._keyboardRequested = false; this._keyboardRestingId = 0; - Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw)); - this._redraw(); + Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout)); }, get visible() { @@ -466,6 +568,8 @@ var Keyboard = new Lang.Class({ this._keyboardGroupsChangedId = this._keyboardController.connect('groups-changed', Lang.bind(this, this._onKeyboardGroupsChanged)); this._keyboardStateId = this._keyboardController.connect('panel-state', Lang.bind(this, this._onKeyboardStateChanged)); this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged)); + + this._relayout(); }, _onKeyFocusChanged: function () { @@ -505,11 +609,12 @@ var Keyboard = new Lang.Class({ * second level. */ let level = (i >= 1 && levels.length == 3) ? i + 1 : i; - let layout = new St.BoxLayout({ style_class: 'keyboard-layout', - vertical: true }); + + let layout = new KeyContainer(); this._loadRows(currentLevel, level, levels.length, layout); layers[level] = layout; - this.actor.add(layout, { x_fill: false }); + this.actor.add(layout, { expand: true }); + layout.layoutButtons(); layout.hide(); } @@ -539,7 +644,7 @@ var Keyboard = new Lang.Class({ return Clutter.EVENT_STOP; }, - _addRowKeys : function (keys, keyboardRow) { + _addRowKeys : function (keys, layout) { for (let i = 0; i < keys.length; ++i) { let key = keys[i]; let button = new Key(key.shift(), key); @@ -579,11 +684,11 @@ var Keyboard = new Lang.Class({ } })); - keyboardRow.add(button.container, { expand: true, x_fill: false, x_align: St.Align.END }); + layout.appendKey(button.container, button.actor.keyWidth); } }, - _loadDefaultKeys: function(keys, rowActor, numLevels, numKeys) { + _loadDefaultKeys: function(keys, layout, numLevels, numKeys) { let extraButton; for (let i = 0; i < keys.length; i++) { let key = keys[i]; @@ -592,7 +697,6 @@ var Keyboard = new Lang.Class({ let action = key.action; extraButton = new Key(key.label, []); - rowActor.add(extraButton.container); extraButton.actor.add_style_class_name('default-key'); if (key.extraClassName != null) @@ -627,6 +731,8 @@ var Keyboard = new Lang.Class({ } else if (key.label == '⏎' && numKeys > 9) { extraButton.setWidth(1.5); } + + layout.appendKey(extraButton.container, extraButton.actor.keyWidth); } }, @@ -648,27 +754,22 @@ var Keyboard = new Lang.Class({ } }, - _mergeRowKeys: function (keyboardRow, pre, row, post, numLevels) { + _mergeRowKeys: function (layout, pre, row, post, numLevels) { if (pre != null) - this._loadDefaultKeys(pre, keyboardRow, numLevels, row.length); + this._loadDefaultKeys(pre, layout, numLevels, row.length); - this._addRowKeys(row, keyboardRow); + this._addRowKeys(row, layout); if (post != null) - this._loadDefaultKeys(post, keyboardRow, numLevels, row.length); + this._loadDefaultKeys(post, layout, numLevels, row.length); }, _loadRows : function (model, level, numLevels, layout) { let rows = model.rows; for (let i = 0; i < rows.length; ++i) { - let row = rows[i]; - let keyboardRow = new St.BoxLayout({ style_class: 'keyboard-row', - x_expand: false, - x_align: Clutter.ActorAlign.END }); - layout.add(keyboardRow); - + layout.appendRow(); let [pre, post] = this._getDefaultKeysForRow(i, rows.length, level); - this._mergeRowKeys (keyboardRow, pre, row, post, numLevels); + this._mergeRowKeys (layout, pre, rows[i], post, numLevels); } }, @@ -687,50 +788,13 @@ var Keyboard = new Lang.Class({ return [numOfHorizSlots, numOfVertSlots]; }, - _redraw: function () { - if (!this._enabled || !this._current_page) + _relayout: function () { + if (this.actor == null) return; - let monitor = Main.layoutManager.keyboardMonitor; let maxHeight = monitor.height / 3; this.actor.width = monitor.width; - - let layout = this._current_page; - let verticalSpacing = layout.get_theme_node().get_length('spacing'); - let padding = layout.get_theme_node().get_length('padding'); - - let [numOfHorizSlots, numOfVertSlots] = this._getGridSlots (); - - let box = layout.get_children()[0].get_children()[0]; - let horizontalSpacing = box.get_theme_node().get_length('spacing'); - let allHorizontalSpacing = (numOfHorizSlots - 1) * horizontalSpacing; - let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / numOfHorizSlots); - - let allVerticalSpacing = (numOfVertSlots - 1) * verticalSpacing; - let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / numOfVertSlots); - - let keySize = Math.min(keyWidth, keyHeight); - layout.height = keySize * numOfVertSlots + allVerticalSpacing + 2 * padding; - - let rows = this._current_page.get_children(); - for (let i = 0; i < rows.length; ++i) { - let keyboard_row = rows[i]; - let keys = keyboard_row.get_children(); - for (let j = 0; j < keys.length; ++j) { - let child = keys[j]; - let keyActor = child.get_children()[0]; - child.width = keySize * keyActor.keyWidth; - child.height = keySize; - if (keyActor._extended_keys) { - let extended_keys = keyActor._extended_keys.get_children(); - for (let n = 0; n < extended_keys.length; ++n) { - let extended_key = extended_keys[n]; - extended_key.width = keySize; - extended_key.height = keySize; - } - } - } - } + this.actor.height = maxHeight; }, _onLevelChanged: function (level) { @@ -772,7 +836,6 @@ var Keyboard = new Lang.Class({ } this._current_page = layers[activeLevel]; - this._redraw(); this._current_page.show(); }, @@ -799,7 +862,7 @@ var Keyboard = new Lang.Class({ if (this._keyboardVisible) { if (monitor != Main.layoutManager.keyboardIndex) { Main.layoutManager.keyboardIndex = monitor; - this._redraw(); + this._relayout(); } return; } @@ -820,7 +883,7 @@ var Keyboard = new Lang.Class({ return; Main.layoutManager.keyboardIndex = monitor; - this._redraw(); + this._relayout(); Main.layoutManager.showKeyboard(); },