loleaflet/Makefile.am | 1 loleaflet/build/deps.js | 1 loleaflet/css/toolbar.css | 107 +++++++ loleaflet/src/control/ColorPicker.js | 330 +++++++++++++++++++++++ loleaflet/src/control/Control.JSDialogBuilder.js | 121 ++++++-- loleaflet/src/core/Util.js | 5 6 files changed, 534 insertions(+), 31 deletions(-)
New commits: commit d0e6692c1bb07966f74304df401b434149d1609d Author: Marco Cecchetti <marco.cecche...@collabora.com> AuthorDate: Mon Oct 14 16:51:15 2019 +0200 Commit: Marco Cecchetti <marco.cecche...@collabora.com> CommitDate: Wed Oct 16 12:18:43 2019 +0200 loleaflet: color picker control based on JSDialogBuilder This patch provides an implementation of a color picker control living in its own submenu. Change-Id: I65c24c73d322f1d4d0d8819dc5c9ad80ee846261 diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am index 160e36ee8..23f6676bb 100644 --- a/loleaflet/Makefile.am +++ b/loleaflet/Makefile.am @@ -330,6 +330,7 @@ pot: src/control/Control.ContextMenu.js \ src/control/Control.DocumentRepair.js \ src/control/Control.DownloadProgress.js \ + src/control/ColorPicker.js \ src/control/Control.JSDialogBuilder.js \ src/control/Control.Menubar.js \ src/control/Control.MobileWizard.js \ diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js index 28eb45cf9..f003f06fe 100644 --- a/loleaflet/build/deps.js +++ b/loleaflet/build/deps.js @@ -387,6 +387,7 @@ var deps = { 'control/Control.LokDialog.js', 'control/Control.AlertDialog.js', 'control/Control.Infobar.js', + 'control/ColorPicker.js', 'control/Control.JSDialogBuilder.js', 'control/Control.MobileWizard.js'], heading: 'Controls', diff --git a/loleaflet/css/toolbar.css b/loleaflet/css/toolbar.css index d95f85a5f..518e03791 100644 --- a/loleaflet/css/toolbar.css +++ b/loleaflet/css/toolbar.css @@ -1066,6 +1066,7 @@ tr.useritem > td > img { } .ui-header.mobile-wizard { + width: 96%; height: 56px !important; font-size: 11pt !important; margin: 0px; @@ -1230,3 +1231,109 @@ menu-entry-with-icon.padding-left + menu-entry-icon.width */ vertical-align: middle; padding-left: 8px; } + +.color-sample-selected { + height: 32px !important; + width: 32px !important; + border: 1px solid #8A8A8A; + border-radius: 100px; +} + +.no-color-selected { + text-align: center; + font-size: 30pt; + line-height: 32px; + font-weight: 300; + color: #ff0000 !important; + background-color: #fff !important; +} + +.color-sample-small { + border-left: 3px solid #fff; + border-right: 3px solid #fff; + border-bottom: 3px solid #fff; + width: 28px; + height: 28px; + color: #f0f8ff; + text-align: center; + vertical-align: bottom; + display: table-cell; +} + +.color-sample-big { + border: 5px solid #f2f8f0; + padding: 5px; + width: 118px; + height: 42px; + color: #f0f8ff; + text-align: right; + vertical-align: bottom; + display: table-cell; +} + +.colors-container { + display: table; +} + +.colors-container-no-color-row { + display: flex; + justify-content: space-between; + height: 50px; +} + +.colors-container-basic-colors-row { + padding-bottom: 20px; +} + +.colors-container-tints { + padding-bottom: 0px; +} + +.no-color-control-label { + display:table-cell; + padding-left: 30px; + vertical-align: middle; +} + +.no-color-control-mark { + color: #0b87e7 !important; + font-size: larger; + line-height: 0; +} + +.no-color-control-icon { + display: table-cell; + height: 28px !important; + width: 28px !important; + border: 1px solid #8A8A8A; + border-radius: 100px; + text-align: center; + font-size: 26pt; + line-height: 28px; + font-weight: 300; + color: #ff0000 !important; + background-color: #fff; +} + +.colors-container-basic-color-mark { + width: 10px; + height: 10px; + font-size: x-large; + color: darkslategray !important; + background-color: transparent !important; + margin-bottom: 2px; + margin-left: 7px; + vertical-align: bottom; +} + +.colors-container-tint-mark { + width: 20px; + height: 20px; + font-size: medium; + color: #000 !important; + background-color: whitesmoke !important; + text-align: center; + line-height: 21px; + margin-left: auto; + border-radius: 100px; +} \ No newline at end of file diff --git a/loleaflet/src/control/ColorPicker.js b/loleaflet/src/control/ColorPicker.js new file mode 100644 index 000000000..71a0e4224 --- /dev/null +++ b/loleaflet/src/control/ColorPicker.js @@ -0,0 +1,330 @@ +/* -*- js-indent-level: 8 -*- */ +/* + * L.ColorPicker is used for building a native HTML color picker + * panel to be used by the properties mobile wizard. + */ + +/* global _ */ + +L.ColorPicker = L.Class.extend({ + options: { + selectedColor: '#CC0814', + noColorControl: true + }, + + statics: { + ID: 0, + ID_TAG: 'color-picker-', + + // color types + BASIC_COLOR: 0, + TINT: 1, + + BASIC_COLORS: ['#000000', '#FF011B', '#FF9838', '#FFFD59', '#01FD55', + '#00FFFE', '#006CE7', '#9B24F4', '#FF21F5'], + TINTS: { + '#000000': ['#000000', '#434343', '#666666', '#888888', + '#BBBBBB', '#DDDDDD', '#EEEEEE', '#FFFFFF'], + '#FF011B': ['#99050C', '#CC0814', '#E06666', '#FF011B', + '#EA9899', '#F4CCCC', '#FFEAEA', '#FFFFFF'], + '#FF9838': ['#783f04', '#b45f06', '#e69138', '#ff9900', + '#f6b26b', '#f9cb9c', '#fce5cd', '#FFFFFF'], + '#FFFD59': ['#7f6000', '#bf9000', '#f1c232', '#ffff00', + '#ffd966', '#ffe599', '#fff2cc', '#FFFFFF'] + } + }, + + _selectedBasicColorIndex: 0, + _selectedTintIndex: 0, + + initialize: function (selectedColorSample, options) { + L.setOptions(this, options); + this._id = L.ColorPicker.ID++; + this._basicColorSampleIdTag = L.ColorPicker.ID_TAG + this._id + '-basic-color-'; + this._tintSampleIdTag = L.ColorPicker.ID_TAG + this._id + '-tint-'; + this._noColorControlId = L.ColorPicker.ID_TAG + this._id + '-no-color'; + this._createBasicColorSelectionMark(); + this._createTintSelectionMark(); + this._selectedColorElement = selectedColorSample; + this._selectedColor = this.options.selectedColor; + this._initIndexes(); + this._container = this._createControl(); + }, + + getId: function () { + return this._id; + }, + + getSelectedColor: function () { + return this._selectedColor; + }, + + getContainer: function () { + return this._container; + }, + + _initIndexes: function () { + for (var i = 0; i < this._getBasicColorCount(); ++i) { + var tintSet = this._getTintSet(i); + for (var j = 0; j < tintSet.length; ++j) { + var tint = tintSet[j]; + if (tint === this._selectedColor) { + this._selectedBasicColorIndex = i; + this._selectedTintIndex = j; + return; + } + } + } + }, + + _createControl: function () { + var children = []; + if (this.options.noColorControl) + children.push(this._createNoColorControl()); + children.push(this._createBasicColorSamples()); + children.push(this._createTintSamples()); + return {type: 'divcontainer', style: 'colors-container', children: children}; + }, + + _createNoColorControl: function () { + var icon = { + type: 'fixedtext', + text: '\\', + style: 'no-color-control-icon' + }; + var label = {type: 'fixedtext', style: 'no-color-control-label', text: _('No color')}; + var description = {type: 'divcontainer', children: [icon, label]}; + var checked = {type:'fixedtext', id: this._noColorControlId, style: 'no-color-control-mark', text: ''}; + var container = { + type: 'divcontainer', + style: 'colors-container-no-color-row', + handlers: [{event: 'click', handler: L.bind(this.onClickNoColor, this)}], + children: [description, checked] + }; + return container; + }, + + _createBasicColorSamples: function () { + var colorEntries = []; + for (var k = 0; k < this._getBasicColorCount(); ++k) { + var selected = k === this._selectedBasicColorIndex; + var entry = { + type: 'colorsample', + id: this._basicColorSampleIdTag + k, + selected: selected, + color: this._getBasicColor(k), + size: 'small', + handlers: [{event: 'click', handler: L.bind(this.onClickBasicColorSample, this)}] + }; + if (selected) { + entry.mark = this._basicColorSelectionMark; + } + colorEntries.push(entry); + } + return {type: 'divcontainer', style: 'colors-container-basic-colors-row', children: colorEntries}; + }, + + _createTintSamples: function () { + var tints = this._getTintSet(this._selectedBasicColorIndex); + var k, selected, entry, tintRowLength = tints.length / 2; + + // first tints row + var tintsEntries1 = []; + for (k = 0; k < tintRowLength; ++k) { + selected = tints[k] === this._selectedColor; + entry = { + type: 'colorsample', + id: this._tintSampleIdTag + k, + selected: selected, + color: tints[k], + size: 'big', + handlers: [{event: 'click', handler: L.bind(this.onClickTintSample, this)}] + }; + if (selected) { + entry.mark = this._tintSelectionMark; + } + tintsEntries1.push(entry); + } + var tintsRow1 = {type: 'divcontainer', style: 'colors-container-tints', children: tintsEntries1}; + + // second tints row + var tintsEntries2 = []; + for (k = tintRowLength; k < tints.length; ++k) { + selected = tints[k] === this._selectedColor; + entry = { + type: 'colorsample', + id: this._tintSampleIdTag + k, + color: tints[k], + size: 'big', + handlers: [{event: 'click', handler: L.bind(this.onClickTintSample, this)}] + }; + if (selected) { + entry.mark = this._tintSelectionMark + } + tintsEntries2.push(entry); + } + var tintsRow2 = {type: 'divcontainer', style: 'colors-container-tints', children: tintsEntries2}; + + return {type: 'divcontainer', children: [tintsRow1, tintsRow2]}; + }, + + _createBasicColorSelectionMark: function () { + this._basicColorSelectionMark = L.DomUtil.create('div', 'colors-container-basic-color-mark', null); + this._basicColorSelectionMark.innerHTML = '●'; + }, + + _createTintSelectionMark: function () { + this._tintSelectionMark = L.DomUtil.create('div', 'colors-container-tint-mark', null); + this._tintSelectionMark.innerHTML = '✔'; + }, + + + _getSelectionMark: function (colorType) { + if (colorType === L.ColorPicker.BASIC_COLOR) + return this._basicColorSelectionMark; + else if (colorType === L.ColorPicker.TINT) + return this._tintSelectionMark; + else + return null; + }, + + _getBasicColorCount: function () { + return L.ColorPicker.BASIC_COLORS.length; + }, + + _getBasicColor: function (index) { + if (!(index >= 0 && index < L.ColorPicker.BASIC_COLORS.length)) + return ''; + return L.ColorPicker.BASIC_COLORS[index]; + }, + + _getTintSet: function (basicColorIndex) { + var basicColor = this._getBasicColor(basicColorIndex); + return L.ColorPicker.TINTS[basicColor]; + }, + + _extractBasicColorIndex: function (sampleId) { + if (!sampleId.startsWith(this._basicColorSampleIdTag)) + return -1; + var index = parseInt(sampleId.substring(this._basicColorSampleIdTag.length)); + if (index < 0 || index >= this._getBasicColorCount()) + return -1; + return index; + }, + + _extractTintIndex: function (sampleId) { + if (!sampleId.startsWith(this._tintSampleIdTag)) + return -1; + var index = parseInt(sampleId.substring(this._tintSampleIdTag.length)); + if (index < 0 || index >= this._getTintSet(this._selectedBasicColorIndex).length) + return -1; + return index; + }, + + _getColorCode: function (colorIndex, colorType) { + var sampleElem = this._getSampleElement(colorIndex, colorType); + return sampleElem.name; + }, + + onClickNoColor: function () { + this._selectedColor = '#'; + this._unselectSample(this._selectedTintIndex, L.ColorPicker.TINT); + this._updateNoColorControl(true); + }, + + onClickBasicColorSample: function (e) { + var basicColorIndex = this._extractBasicColorIndex(e.id); + if (basicColorIndex < 0) + return; + this._selectedBasicColorIndex = this._updateSelectedSample(basicColorIndex, this._selectedBasicColorIndex, L.ColorPicker.BASIC_COLOR); + this._updateTintsView(basicColorIndex); + }, + + onClickTintSample: function (e) { + var tintIndex = this._extractTintIndex(e.id); + if (tintIndex < 0) + return; + this._selectedTintIndex = this._updateSelectedSample(tintIndex, this._selectedTintIndex, L.ColorPicker.TINT); + this._selectedColor = '#' + this._getColorCode(this._selectedTintIndex, L.ColorPicker.TINT); + this._updateNoColorControl(false); + this._updateSelectedColorElement(); + }, + + _updateSelectedSample: function (colorIndex, selectedColorIndex, colorType) { + this._unselectSample(selectedColorIndex, colorType); + this._selectSample(colorIndex, colorType); + return colorIndex; + }, + + _unselectSample: function (colorIndex, colorType) { + var sampleElem = this._getSampleElement(colorIndex, colorType); + if (sampleElem && sampleElem.firstChild) { + sampleElem.removeChild(sampleElem.firstChild); + } + }, + + _selectSample: function (colorIndex, colorType) { + var sampleElem = this._getSampleElement(colorIndex, colorType); + if (sampleElem) { + sampleElem.appendChild(this._getSelectionMark(colorType)); + } + }, + + _updateTintsView: function () { + var tintSet = this._getTintSet(this._selectedBasicColorIndex); + if (!tintSet) + return; + for (var i = 0; i < tintSet.length; ++i) { + var tint = tintSet[i]; + var sampleElem = this._getSampleElement(i, L.ColorPicker.TINT); + if (sampleElem) { + sampleElem.style.backgroundColor = tint; + sampleElem.name = tint.substring(1); + if (tint === this._selectedColor) { + sampleElem.appendChild(this._getSelectionMark(L.ColorPicker.TINT)); + } else if (sampleElem.firstChild) { + sampleElem.removeChild(sampleElem.firstChild); + } + } + } + }, + + _updateNoColorControl: function (checked) { + var noColorElem = L.DomUtil.get(this._noColorControlId); + if (noColorElem) { + if (noColorElem.checked !== checked) { + noColorElem.checked = checked; + if (this._selectedColorElement) { + if (checked) { + noColorElem.innerHTML = '✔'; + // update value for the related menu entry + L.DomUtil.addClass(this._selectedColorElement, 'no-color-selected'); + this._selectedColorElement.innerHTML = '\\'; + } else { + noColorElem.innerHTML = ''; + // update value for the related menu entry + L.DomUtil.removeClass(this._selectedColorElement, 'no-color-selected'); + this._selectedColorElement.innerHTML = ''; + } + } + } + } + }, + + _getSampleElement: function (index, type) { + var sampleId; + if (type === L.ColorPicker.BASIC_COLOR) { + sampleId = this._basicColorSampleIdTag + index; + } else if (type === L.ColorPicker.TINT) { + sampleId = this._tintSampleIdTag + index; + } + return L.DomUtil.get(sampleId); + }, + + _updateSelectedColorElement: function () { + if (this._selectedColorElement) { + this._selectedColorElement.style.backgroundColor = this._selectedColor; + } + } + +}); diff --git a/loleaflet/src/control/Control.JSDialogBuilder.js b/loleaflet/src/control/Control.JSDialogBuilder.js index 311d0c64d..db4991df6 100644 --- a/loleaflet/src/control/Control.JSDialogBuilder.js +++ b/loleaflet/src/control/Control.JSDialogBuilder.js @@ -4,7 +4,8 @@ * from the JSON description provided by the server. */ -/* global $ w2ui _ */ +/* global $ _ */ + L.Control.JSDialogBuilder = L.Control.extend({ /* Handler is a function which takes three parameters: @@ -46,6 +47,8 @@ L.Control.JSDialogBuilder = L.Control.extend({ this._controlHandlers['scrollbar'] = this._ignoreHandler; this._controlHandlers['toolbox'] = this._containerHandler; this._controlHandlers['toolitem'] = this._toolitemHandler; + this._controlHandlers['colorsample'] = this._colorSampleControl; + this._controlHandlers['divcontainer'] = this._divContainerHandler; this._controlHandlers['chartTypeSelector'] = this._chartTypeControl; @@ -67,11 +70,8 @@ L.Control.JSDialogBuilder = L.Control.extend({ }, _clearColorPickers: function() { - while (this._colorPickers.length) { - var id = this._colorPickers.pop(); - w2ui[id].remove(); - delete w2ui[id]; - } + this._colorPickers = []; + L.ColorPicker.ID = 0; }, _toolitemHandler: function(parentContainer, data, builder) { @@ -100,6 +100,28 @@ L.Control.JSDialogBuilder = L.Control.extend({ } }, + _setupHandlers: function (controlElement, handlers) { + if (handlers) { + for (var i = 0; i < handlers.length; ++i) { + var event = handlers[i].event; + var handler = handlers[i].handler; + if (!L.isEmpty(event) && handler) { + if (event === 'click') { + var eventData = { + id: controlElement.id + }; + $(controlElement).click( + // avoid to access mutable variable (that is `i` dependent) in closure + (function (lhandler, leventData) { + return function() { lhandler(leventData) }; + })(handler, eventData) + ); + } + } + } + } + }, + _cleanText: function(text) { if (!text) return ''; @@ -458,7 +480,7 @@ L.Control.JSDialogBuilder = L.Control.extend({ var entries = []; for (var index in data.entries) { - var entry = { type: 'fixedtext', text: data.entries[index], isComboboxItem: true }; + var entry = { type: 'fixedtext', text: data.entries[index], style: 'ui-combobox-text' }; entries.push(entry); } @@ -470,13 +492,13 @@ L.Control.JSDialogBuilder = L.Control.extend({ }, _fixedtextControl: function(parentContainer, data, builder) { - var fixedtext = L.DomUtil.create('p', 'mobile-wizard ui-text', parentContainer); + var fixedtext = L.DomUtil.create('p', 'mobile-wizard', parentContainer); fixedtext.innerHTML = builder._cleanText(data.text); fixedtext.id = data.id; - - if (data.isComboboxItem) { - $(fixedtext).removeClass('ui-text'); - $(fixedtext).addClass('ui-combobox-text'); + if (data.style && data.style.length) { + L.DomUtil.addClass(fixedtext, data.style); + } else { + L.DomUtil.addClass(fixedtext, 'ui-text'); } if (data.hidden) @@ -530,30 +552,67 @@ L.Control.JSDialogBuilder = L.Control.extend({ return false; }, + _divContainerHandler: function (parentContainer, data, builder) { + if (!(data.children && data.children.length)) + return false; + + var divElem = L.DomUtil.create('div', 'mobile-wizard', parentContainer); + if (data.style && data.style.length) + L.DomUtil.addClass(divElem, data.style); + for (var i = 0; i < data.children.length; ++i) { + var entry = data.children[i]; + var handle = builder._controlHandlers[entry.type]; + if (handle) { + handle(divElem, entry, builder); + } + } + builder._setupHandlers(divElem, data.handlers); + return false; + }, + + _colorSampleControl: function (parentContainer, data, builder) { + var sampleSizeClass = 'color-sample-small'; + if (data.size === 'big') + sampleSizeClass = 'color-sample-big'; + var colorSample = L.DomUtil.create('div', 'mobile-wizard ' + sampleSizeClass, parentContainer); + colorSample.id = data.id; + colorSample.style.backgroundColor = data.color; + colorSample.name = data.color.substring(1); + + if (data.selected && data.mark) { + colorSample.appendChild(data.mark); + } + + builder._setupHandlers(colorSample, data.handlers); + + return false; + }, + _colorControl: function(parentContainer, data, builder) { - var colorContainer = L.DomUtil.create('div', 'colorcontainer', parentContainer); - colorContainer.id = data.command.substr('.uno:'.length); + var title = data.text; + title = builder._cleanText(title); - if (data.enabled == 'false') - $(colorContainer).attr('disabled', 'disabled'); + var selectedColor = L.ColorPicker.BASIC_COLORS[1]; + var valueNode = L.DomUtil.create('div', 'color-sample-selected', null); + valueNode.style.backgroundColor = selectedColor; - var imageContainer = L.DomUtil.create('div', 'colorimagecontainer', colorContainer); - var image = L.DomUtil.create('img', 'colorimage', imageContainer); - var icon = builder._createIconPath(data.command); - image.src = icon; + var iconPath = builder._createIconPath(data.command); + var noColorControl = data.command !== '.uno:FontColor'; - var toolbarContainer = L.DomUtil.create('div', 'colorspan', colorContainer); - var toolbar = $(toolbarContainer); - var id = 'colorselector-' + builder._colorPickers.length; - var items = [{type: 'color', id: 'color'}]; - toolbar.w2toolbar({ - name: id, - tooltip: 'bottom', - items: items - }); - w2ui[id].set('color', {color: '#ff0033'}); - builder._colorPickers.push(id); + var colorPickerControl = new L.ColorPicker( + valueNode, + { + selectedColor: selectedColor, + noColorControl: noColorControl + }); + builder._colorPickers.push(colorPickerControl); + // color control panel + var colorsContainer = colorPickerControl.getContainer(); + + var contentNode = {type: 'container', children: [colorsContainer]}; + + builder._explorableEntry(parentContainer, title, contentNode, builder, valueNode, iconPath); return false; }, diff --git a/loleaflet/src/core/Util.js b/loleaflet/src/core/Util.js index 5d25f5ec5..04b3638f9 100644 --- a/loleaflet/src/core/Util.js +++ b/loleaflet/src/core/Util.js @@ -191,6 +191,10 @@ L.Util = { } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } + }, + + isEmpty: function(o) { + return !(o && o.length); } }; @@ -240,3 +244,4 @@ L.setOptions = L.Util.setOptions; L.round = L.Util.round; L.getDpiScaleFactor = L.Util.getDpiScaleFactor; L.toggleFullScreen = L.Util.toggleFullScreen; +L.isEmpty = L.Util.isEmpty; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits