From 80a9fe33960c1c17e7e95f9bb13411d4c2f5e4ec Mon Sep 17 00:00:00 2001 From: jdescottes Date: Wed, 25 Sep 2013 00:11:12 +0200 Subject: [PATCH] First layer UI. Just functional, UX far from ideal --- css/style.css | 65 +++++++++++++++++ index.html | 1 + js/app.js | 3 + js/controller/LayersController.js | 59 +++++++++++++++ js/controller/PaletteController.js | 2 - js/controller/PiskelController.js | 71 ++++++++++++++++--- js/controller/SettingsController.js | 2 +- js/controller/settings/GifExportController.js | 15 ++-- js/model/Piskel.js | 22 ++++++ piskel-script-list.js | 1 + templates/layers.html | 13 ++++ 11 files changed, 233 insertions(+), 21 deletions(-) create mode 100644 js/controller/LayersController.js create mode 100644 templates/layers.html diff --git a/css/style.css b/css/style.css index 50428da5..67954475 100644 --- a/css/style.css +++ b/css/style.css @@ -177,6 +177,71 @@ body { overflow: hidden; } +/** + * Layers container + */ +.layers-container { + border : 1px solid #444; + font-size : medium; + color: white; + text-align: left; +} + +.layers-title { + padding : 10px; + margin: 0; + font-size : 18px; +} + +.layers-list { + font-size : 12px; + border-bottom: 1px solid #444; +} + +.layer-item { + height:24px; + line-height: 24px; + padding : 0 10px; + border-top: 1px solid #444; + cursor : pointer; +} + +.layer-item:hover { + background : #222; +} + +.current-layer-item, .current-layer-item:hover { + background : #333; + font-weight: bold; +} + +.layers-button-container { + padding: 10px; +} + +.layers-button { + line-height: 24px; + padding: 0 10px; + display: inline-block; + border: none; + border-top: 1px solid #666; + border-bottom: 1px solid #222; + border-radius: 3px; + background-color: #3f3f3f; + cursor: pointer; + color: white; + font-size: 0.7em; + font-weight: bold; + text-align: center; + text-decoration: none; + text-shadow: 0px -1px 0 #000; + transition: background-color 0.2s linear; +} + +.layers-button:hover { + text-decoration: none; + background-color: #484848; +} /** * User messages */ diff --git a/index.html b/index.html index 3035a2cc..f6d4b427 100644 --- a/index.html +++ b/index.html @@ -36,6 +36,7 @@
+
diff --git a/js/app.js b/js/app.js index fb09f694..3919d508 100644 --- a/js/app.js +++ b/js/app.js @@ -29,6 +29,9 @@ this.previewsController = new pskl.controller.PreviewFilmController(this.piskelController, $('#preview-list')); this.previewsController.init(); + + this.layersController = new pskl.controller.LayersController(this.piskelController); + this.layersController.init(); this.settingsController = new pskl.controller.SettingsController(this.piskelController); this.settingsController.init(); diff --git a/js/controller/LayersController.js b/js/controller/LayersController.js new file mode 100644 index 00000000..275b674c --- /dev/null +++ b/js/controller/LayersController.js @@ -0,0 +1,59 @@ +(function () { + var ns = $.namespace('pskl.controller'); + + ns.LayersController = function (piskelController) { + this.piskelController = piskelController; + }; + + ns.LayersController.prototype.init = function () { + this.layerItemTemplate_ = pskl.utils.Template.get('layer-item-template'); + this.rootEl = document.querySelectorAll('.layers-container')[0]; + this.layersListEl = document.querySelectorAll('.layers-list')[0]; + + this.rootEl.addEventListener('click', this.onClick_.bind(this)); + + $.subscribe(Events.FRAMESHEET_RESET, this.renderLayerList_.bind(this)); + + this.renderLayerList_(); + }; + + ns.LayersController.prototype.renderLayerList_ = function () { + this.layersListEl.innerHTML = ''; + var layers = this.piskelController.getLayers(); + layers.forEach(this.addLayerItem.bind(this)); + }; + + ns.LayersController.prototype.addLayerItem = function (layer) { + var layerItemHtml = pskl.utils.Template.replace(this.layerItemTemplate_, { + layername : layer.getName() + }); + var layerItem = pskl.utils.Template.createFromHTML(layerItemHtml); + if (this.piskelController.getCurrentLayer() === layer) { + layerItem.classList.add('current-layer-item'); + } + this.layersListEl.insertBefore(layerItem, this.layersListEl.firstChild); + }; + + ns.LayersController.prototype.onClick_ = function (evt) { + var el = evt.target || evt.srcElement; + if (el.nodeName == 'BUTTON') { + this.onButtonClick_(el); + } else if (el.nodeName == 'LI') { + var layerName = el.getAttribute('data-layer-name'); + this.piskelController.selectLayerByName(layerName); + } + }; + + ns.LayersController.prototype.onButtonClick_ = function (button) { + var action = button.getAttribute('data-action'); + if (action == 'up') { + this.piskelController.moveLayerUp(); + } else if (action == 'down') { + this.piskelController.moveLayerDown(); + } else if (action == 'add') { + this.piskelController.createLayer(); + } else if (action == 'delete') { + this.piskelController.removeCurrentLayer(); + } + }; +})(); \ No newline at end of file diff --git a/js/controller/PaletteController.js b/js/controller/PaletteController.js index 813f8cb9..83e5100d 100644 --- a/js/controller/PaletteController.js +++ b/js/controller/PaletteController.js @@ -12,12 +12,10 @@ $.subscribe(Events.PRIMARY_COLOR_UPDATED, $.proxy(function(evt, color) { this.updateColorPicker_(color, $('#color-picker')); - this.addColorToPalette_(color); }, this)); $.subscribe(Events.SECONDARY_COLOR_UPDATED, $.proxy(function(evt, color) { this.updateColorPicker_(color, $('#secondary-color-picker')); - this.addColorToPalette_(color); }, this)); // Initialize colorpickers: diff --git a/js/controller/PiskelController.js b/js/controller/PiskelController.js index 6ad47e65..faa6fcbf 100644 --- a/js/controller/PiskelController.js +++ b/js/controller/PiskelController.js @@ -22,6 +22,10 @@ return this.piskel.getWidth(); }; + ns.PiskelController.prototype.getLayers = function () { + return this.piskel.getLayers(); + }; + ns.PiskelController.prototype.getCurrentLayer = function () { return this.piskel.getLayerAt(this.currentLayerIndex); }; @@ -32,7 +36,7 @@ }; ns.PiskelController.prototype.getFrameAt = function (index) { - var frames = this.piskel.getLayers().map(function (l) { + var frames = this.getLayers().map(function (l) { return l.getFrameAt(index); }); return pskl.utils.FrameUtils.merge(frames); @@ -47,7 +51,7 @@ ns.PiskelController.prototype.getMergedFrameAt; ns.PiskelController.prototype.addEmptyFrame = function () { - var layers = this.piskel.getLayers(); + var layers = this.getLayers(); layers.forEach(function (l) { l.addFrame(this.createEmptyFrame_()); }.bind(this)); @@ -59,7 +63,7 @@ }; ns.PiskelController.prototype.removeFrameAt = function (index) { - var layers = this.piskel.getLayers(); + var layers = this.getLayers(); layers.forEach(function (l) { l.removeFrameAt(index); }); @@ -72,14 +76,14 @@ }; ns.PiskelController.prototype.duplicateFrameAt = function (index) { - var layers = this.piskel.getLayers(); + var layers = this.getLayers(); layers.forEach(function (l) { l.duplicateFrameAt(index); }); }; ns.PiskelController.prototype.moveFrame = function (fromIndex, toIndex) { - var layers = this.piskel.getLayers(); + var layers = this.getLayers(); layers.forEach(function (l) { l.moveFrame(fromIndex, toIndex); }); @@ -100,13 +104,58 @@ $.publish(Events.FRAMESHEET_RESET); }; - ns.PiskelController.prototype.createLayer = function (name) { - var layer = new pskl.model.Layer(name); - for (var i = 0 ; i < this.getFrameCount() ; i++) { - layer.addFrame(this.createEmptyFrame_()); + ns.PiskelController.prototype.selectLayer = function (layer) { + var index = this.getLayers().indexOf(layer); + if (index != -1) { + this.setCurrentLayerIndex(index); + } + }; + + ns.PiskelController.prototype.selectLayerByName = function (name) { + if (this.hasLayerForName_(name)) { + var layer = this.piskel.getLayersByName(name)[0]; + this.selectLayer(layer); + } + }; + + ns.PiskelController.prototype.createLayer = function (name) { + if (!name) { + name = "Layer " + (this.getLayers().length + 1); + } + if (!this.hasLayerForName_(name)) { + var layer = new pskl.model.Layer(name); + for (var i = 0 ; i < this.getFrameCount() ; i++) { + layer.addFrame(this.createEmptyFrame_()); + } + this.piskel.addLayer(layer); + this.setCurrentLayerIndex(this.piskel.getLayers().length - 1); + } else { + throw 'Layer name should be unique'; + } + }; + + ns.PiskelController.prototype.hasLayerForName_ = function (name) { + return this.piskel.getLayersByName(name).length > 0; + }; + + ns.PiskelController.prototype.moveLayerUp = function () { + var layer = this.getCurrentLayer(); + this.piskel.moveLayerUp(layer); + this.selectLayer(layer); + }; + + ns.PiskelController.prototype.moveLayerDown = function () { + var layer = this.getCurrentLayer(); + this.piskel.moveLayerDown(layer); + this.selectLayer(layer); + }; + + ns.PiskelController.prototype.removeCurrentLayer = function () { + if (this.getLayers().length > 1) { + var layer = this.getCurrentLayer(); + this.piskel.removeLayer(layer); + this.setCurrentLayerIndex(0); } - this.piskel.addLayer(layer); - this.setCurrentLayerIndex(this.piskel.getLayers().length - 1); }; ns.PiskelController.prototype.serialize = function () { diff --git a/js/controller/SettingsController.js b/js/controller/SettingsController.js index 853e4852..611eebf2 100644 --- a/js/controller/SettingsController.js +++ b/js/controller/SettingsController.js @@ -30,7 +30,7 @@ // Expand drawer when clicking 'Settings' tab. $('[data-setting]').click(function(evt) { var el = evt.originalEvent.currentTarget; - var setting = el.dataset.setting; + var setting = el.getAttribute("data-setting"); if (this.currentSetting != setting) { this.loadSetting(setting); } else { diff --git a/js/controller/settings/GifExportController.js b/js/controller/settings/GifExportController.js index 42360c2e..8b77ad32 100644 --- a/js/controller/settings/GifExportController.js +++ b/js/controller/settings/GifExportController.js @@ -5,12 +5,14 @@ }; ns.GifExportController.prototype.init = function () { - this.initRadioElements_(); + this.radioTemplate_ = pskl.utils.Template.get("export-gif-radio-template"); this.previewContainer = document.querySelectorAll(".export-gif-preview div")[0]; - this.uploadForm = $("[name=gif-export-upload-form]"); + this.uploadForm = $("[name=gif-export-upload-form]"); this.uploadForm.submit(this.upload.bind(this)); + + this.initRadioElements_(); }; ns.GifExportController.prototype.upload = function (evt) { @@ -52,18 +54,17 @@ [10,true] //default ]; - var radioTpl = $("#export-gif-radio-template").get(0); for (var i = 0 ; i < dpis.length ; i++) { var dpi = dpis[i]; - var radio = this.createRadioForDpi_(dpi, radioTpl.innerHTML); - radioTpl.parentNode.insertBefore(radio, radioTpl); + var radio = this.createRadioForDpi_(dpi); + this.uploadForm.get(0).appendChild(radio); } }; - ns.GifExportController.prototype.createRadioForDpi_ = function (dpi, template) { + ns.GifExportController.prototype.createRadioForDpi_ = function (dpi) { var label = dpi[0]*this.piskelController.getWidth() + "x" + dpi[0]*this.piskelController.getHeight(); var value = dpi[0]; - var radioHTML = pskl.utils.Template.replace(template, {value : value, label : label}); + var radioHTML = pskl.utils.Template.replace(this.radioTemplate_, {value : value, label : label}); var radio = pskl.utils.Template.createFromHTML(radioHTML); if (dpi[1]) { diff --git a/js/model/Piskel.js b/js/model/Piskel.js index 635a6ed7..5ca4b28f 100644 --- a/js/model/Piskel.js +++ b/js/model/Piskel.js @@ -48,10 +48,32 @@ return this.layers[index]; }; + ns.Piskel.prototype.getLayersByName = function (name) { + return this.layers.filter(function (l) { + return l.getName() == name; + }); + }; + ns.Piskel.prototype.addLayer = function (layer) { this.layers.push(layer); }; + ns.Piskel.prototype.moveLayerUp = function (layer) { + var index = this.layers.indexOf(layer); + if (index > -1 && index < this.layers.length-1) { + this.layers[index] = this.layers[index+1]; + this.layers[index+1] = layer; + } + }; + + ns.Piskel.prototype.moveLayerDown = function (layer) { + var index = this.layers.indexOf(layer); + if (index > 0) { + this.layers[index] = this.layers[index-1]; + this.layers[index-1] = layer; + } + }; + ns.Piskel.prototype.removeLayer = function (layer) { var index = this.layers.indexOf(layer); if (index != -1) { diff --git a/piskel-script-list.js b/piskel-script-list.js index 35eba20a..c59f13f0 100644 --- a/piskel-script-list.js +++ b/piskel-script-list.js @@ -44,6 +44,7 @@ exports.scripts = [ "js/controller/PiskelController.js", "js/controller/DrawingController.js", "js/controller/PreviewFilmController.js", + "js/controller/LayersController.js", "js/controller/AnimatedPreviewController.js", "js/controller/ToolController.js", "js/controller/PaletteController.js", diff --git a/templates/layers.html b/templates/layers.html new file mode 100644 index 00000000..63ddf0a7 --- /dev/null +++ b/templates/layers.html @@ -0,0 +1,13 @@ +
+

Layers

+ + +
+ + + + +
+