diff --git a/src/css/settings-resize.css b/src/css/settings-resize.css index 6adac315..5f7be984 100644 --- a/src/css/settings-resize.css +++ b/src/css/settings-resize.css @@ -1,5 +1,7 @@ .resize-section-title { + vertical-align: top; display: inline-block; + padding-top: 5px; width: 25%; } @@ -10,24 +12,76 @@ .resize-origin-container { overflow: hidden; position: relative; - width: 150px; + width: 70px; margin-top: 5px; + display: inline-block; } .resize-origin-option { float: left; box-sizing: border-box; - margin: 0 5px 5px 0; - padding-top:16px; - width: 45px; - height: 45px; - border : 1px solid #888; + position: relative; + margin: 0 1px 1px 0; + width: 20px; + height: 20px; + background : #888; + font-size: 8px; text-align: center; cursor: pointer; } .resize-origin-option.selected { - border-color: gold; - padding-top:14px; - border-width: 3px; + border : 3px solid gold; +} + +.resize-origin-option:before { + content: ''; + position: absolute; + display: block; + top: 50%; + left: 50%; + margin: -2px; +} + +.resize-origin-option.selected:before { + content: ''; + width: 4px; + height: 4px; + background: gold; +} + +.resize-origin-option[data-neighbor]:before { + width: 0; + height: 0; + border-width: 4px; + border-style: solid; + border-color: transparent; +} + +.resize-origin-option[data-neighbor="bottom"]:before { + border-top-color: gold; + margin-left: -4px; +} + +.resize-origin-option[data-neighbor="left"]:before { + border-right-color: gold; + margin-top: -4px; + margin-left: -6px; +} + +.resize-origin-option[data-neighbor="top"]:before { + border-bottom-color: gold; + margin-top: -6px; + margin-left: -4px; +} + +.resize-origin-option[data-neighbor="right"]:before { + border-left-color: gold; + margin-top: -4px; +} + +.resize-ratio-checkbox, +.resize-smooth-checkbox { + vertical-align: -2px; + margin-left: 0; } \ No newline at end of file diff --git a/src/js/controller/DrawingController.js b/src/js/controller/DrawingController.js index a167e3e9..65cf8782 100644 --- a/src/js/controller/DrawingController.js +++ b/src/js/controller/DrawingController.js @@ -63,7 +63,7 @@ $(window).resize($.proxy(this.startResizeTimer_, this)); $.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this)); - $.subscribe(Events.FRAME_SIZE_CHANGED, $.proxy(this.onFrameSizeChanged_, this)); + $.subscribe(Events.FRAME_SIZE_CHANGED, $.proxy(this.onFrameSizeChange_, this)); pskl.app.shortcutService.addShortcut('0', this.resetZoom_.bind(this)); pskl.app.shortcutService.addShortcut('+', this.increaseZoom_.bind(this, 1)); @@ -128,7 +128,7 @@ } }; - ns.DrawingController.prototype.onFrameSizeChanged_ = function () { + ns.DrawingController.prototype.onFrameSizeChange_ = function () { this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_()); this.centerColumnWrapperHorizontally_(); this.compositeRenderer.setZoom(this.calculateZoom_()); diff --git a/src/js/controller/settings/ResizeController.js b/src/js/controller/settings/ResizeController.js deleted file mode 100644 index 0353e4ba..00000000 --- a/src/js/controller/settings/ResizeController.js +++ /dev/null @@ -1,135 +0,0 @@ -(function () { - var ns = $.namespace('pskl.controller.settings'); - - var ORIGIN = { - TOPLEFT : 'TOPLEFT', - TOP : 'TOP', - TOPRIGHT : 'TOPRIGHT', - MIDDLELEFT : 'MIDDLELEFT', - MIDDLE : 'MIDDLE', - MIDDLERIGHT : 'MIDDLERIGHT', - BOTTOMLEFT : 'BOTTOMLEFT', - BOTTOM : 'BOTTOM', - BOTTOMRIGHT : 'BOTTOMRIGHT', - }; - - ns.ResizeController = function (piskelController) { - this.piskelController = piskelController; - this.origin = ORIGIN.TOPLEFT; - }; - - ns.ResizeController.prototype.init = function () { - this.resizeWidth = $('[name=resize-width]'); - this.resizeHeight = $('[name=resize-height]'); - - this.resizeWidth.val(this.piskelController.getWidth()); - this.resizeHeight.val(this.piskelController.getHeight()); - - this.cancelButton = $('.resize-cancel-button'); - this.cancelButton.click(this.onCancelButtonClicked_.bind(this)); - - this.resizeForm = $('[name=resize-form]'); - this.resizeForm.submit(this.onResizeFormSubmit_.bind(this)); - - this.resizeOrigin = document.querySelector('.resize-origin-container'); - this.resizeOrigin.addEventListener('click', this.onResizeOriginClick_.bind(this)); - this.setOrigin_(ORIGIN.TOPLEFT); - - this.resizeContentCheckbox = $(".resize-content-checkbox"); - }; - - ns.ResizeController.prototype.onResizeOriginClick_ = function (evt) { - var target = evt.target; - var origin = target.dataset.origin; - if (origin && ORIGIN[origin]) { - origin = origin.toUpperCase(); - this.setOrigin_(origin); - } - }; - - ns.ResizeController.prototype.setOrigin_ = function (origin) { - this.origin = origin; - var previous = document.querySelector('.resize-origin-option.selected'); - if (previous) { - previous.classList.remove('selected'); - } - - var selected = document.querySelector('.resize-origin-option[data-origin="' + origin + '"]'); - if (selected) { - selected.classList.add('selected'); - } - }; - - ns.ResizeController.prototype.onResizeFormSubmit_ = function (evt) { - evt.originalEvent.preventDefault(); - - var width = parseInt(this.resizeWidth.val(), 10); - var height = parseInt(this.resizeHeight.val(), 10); - - var resizeContentEnabled = this.isResizeContentEnabled_(); - var resizedLayers = this.piskelController.getLayers().map(this.resizeLayer_.bind(this)); - - var piskel = pskl.model.Piskel.fromLayers(resizedLayers, this.piskelController.getPiskel().getDescriptor()); - - pskl.app.piskelController.setPiskel(piskel, true); - $.publish(Events.CLOSE_SETTINGS_DRAWER); - }; - - ns.ResizeController.prototype.resizeLayer_ = function (layer) { - var resizedFrames = layer.getFrames().map(this.resizeFrame_.bind(this)); - return pskl.model.Layer.fromFrames(layer.getName(), resizedFrames); - }; - - ns.ResizeController.prototype.resizeFrame_ = function (frame) { - var width = parseInt(this.resizeWidth.val(), 10); - var height = parseInt(this.resizeHeight.val(), 10); - - var resizedFrame; - if (this.isResizeContentEnabled_()) { - resizedFrame = pskl.utils.FrameUtils.resize(frame, width, height, false); - } else { - resizedFrame = new pskl.model.Frame(width, height); - frame.forEachPixel(function (color, x, y) { - var translated = this.translateCoordinates_(x, y, frame, resizedFrame); - if (resizedFrame.containsPixel(translated.x, translated.y)) { - resizedFrame.setPixel(translated.x, translated.y, color); - } - }.bind(this)); - } - - return resizedFrame; - }; - - ns.ResizeController.prototype.translateCoordinates_ = function (x, y, frame, resizedFrame) { - var translatedX, translatedY; - if (this.origin.indexOf('LEFT') != -1) { - translatedX = x; - } else if (this.origin.indexOf('RIGHT') != -1) { - translatedX = x - (frame.width - resizedFrame.width); - } else { - translatedX = x - Math.round((frame.width - resizedFrame.width)/2); - } - - if (this.origin.indexOf('TOP') != -1) { - translatedY = y; - } else if (this.origin.indexOf('BOTTOM') != -1) { - translatedY = y - (frame.height - resizedFrame.height); - } else { - translatedY = y - Math.round((frame.height - resizedFrame.height)/2); - } - - return { - x : translatedX, - y : translatedY - }; - - }; - - ns.ResizeController.prototype.isResizeContentEnabled_ = function () { - return !!this.resizeContentCheckbox.prop('checked'); - }; - - ns.ResizeController.prototype.onCancelButtonClicked_ = function (evt) { - $.publish(Events.CLOSE_SETTINGS_DRAWER); - }; -})(); \ No newline at end of file diff --git a/src/js/controller/settings/SettingsController.js b/src/js/controller/settings/SettingsController.js index 7b8ba906..16babfae 100644 --- a/src/js/controller/settings/SettingsController.js +++ b/src/js/controller/settings/SettingsController.js @@ -8,11 +8,11 @@ }, 'resize' : { template : 'templates/settings/resize.html', - controller : ns.ResizeController + controller : ns.resize.ResizeController }, 'export' : { template : 'templates/settings/export.html', - controller : ns.ImageExportController + controller : ns.exportimage.ImageExportController }, 'import' : { template : 'templates/settings/import.html', diff --git a/src/js/controller/settings/GifExportController.js b/src/js/controller/settings/exportimage/GifExportController.js similarity index 98% rename from src/js/controller/settings/GifExportController.js rename to src/js/controller/settings/exportimage/GifExportController.js index f9a1b27f..2933f1fd 100644 --- a/src/js/controller/settings/GifExportController.js +++ b/src/js/controller/settings/exportimage/GifExportController.js @@ -1,5 +1,5 @@ (function () { - var ns = $.namespace("pskl.controller.settings"); + var ns = $.namespace("pskl.controller.settings.exportimage"); var URL_MAX_LENGTH = 30; var MAX_GIF_COLORS = 256; @@ -65,8 +65,6 @@ this.updatePreview_(imageData); this.previewContainerEl.classList.add("preview-upload-ongoing"); - console.log(imageData.length); - pskl.app.imageUploadService.upload(imageData, this.onImageUploadCompleted_.bind(this), this.onImageUploadFailed_.bind(this)); }; diff --git a/src/js/controller/settings/ImageExportController.js b/src/js/controller/settings/exportimage/ImageExportController.js similarity index 86% rename from src/js/controller/settings/ImageExportController.js rename to src/js/controller/settings/exportimage/ImageExportController.js index 75d8b2e9..76e4b806 100644 --- a/src/js/controller/settings/ImageExportController.js +++ b/src/js/controller/settings/exportimage/ImageExportController.js @@ -1,5 +1,5 @@ (function () { - var ns = $.namespace("pskl.controller.settings"); + var ns = $.namespace("pskl.controller.settings.exportimage"); ns.ImageExportController = function (piskelController) { this.piskelController = piskelController; diff --git a/src/js/controller/settings/PngExportController.js b/src/js/controller/settings/exportimage/PngExportController.js similarity index 98% rename from src/js/controller/settings/PngExportController.js rename to src/js/controller/settings/exportimage/PngExportController.js index db74da66..246f6a1f 100644 --- a/src/js/controller/settings/PngExportController.js +++ b/src/js/controller/settings/exportimage/PngExportController.js @@ -1,5 +1,5 @@ (function () { - var ns = $.namespace("pskl.controller.settings"); + var ns = $.namespace("pskl.controller.settings.exportimage"); var URL_MAX_LENGTH = 60; diff --git a/src/js/controller/settings/resize/AbstractResizeController.js b/src/js/controller/settings/resize/AbstractResizeController.js new file mode 100644 index 00000000..a5b77a01 --- /dev/null +++ b/src/js/controller/settings/resize/AbstractResizeController.js @@ -0,0 +1,89 @@ +(function () { + var ns = $.namespace('pskl.controller.settings.resize'); + + ns.AbstractResizeController = function (piskelController, container) { + this.piskelController = piskelController; + this.container = container; + }; + + ns.AbstractResizeController.prototype.init = function () { + this.widthInput = this.container.querySelector('[name="resize-width"]'); + this.heightInput = this.container.querySelector('[name="resize-height"]'); + + this.widthInput.value = this.piskelController.getWidth(); + this.heightInput.value = this.piskelController.getHeight(); + + this.widthInput.addEventListener('keyup', this.onSizeInputKeyUp_.bind(this)); + this.heightInput.addEventListener('keyup', this.onSizeInputKeyUp_.bind(this)); + + this.cancelButton = this.container.querySelector('.resize-cancel-button'); + this.cancelButton.addEventListener('click', this.onCancelButtonClicked_.bind(this)); + + this.resizeForm = this.container.querySelector('form'); + this.resizeForm.addEventListener('submit', this.onResizeFormSubmit_.bind(this)); + + this.maintainRatioCheckbox = this.container.querySelector('.resize-ratio-checkbox'); + this.maintainRatioCheckbox.addEventListener('change', this.onMaintainRatioChange_.bind(this)); + + this.lastInput = this.widthInput; + }; + + ns.AbstractResizeController.prototype.onResizeFormSubmit_ = function (evt) { + evt.preventDefault(); + + var resizedLayers = this.piskelController.getLayers().map(this.resizeLayer_.bind(this)); + + var piskel = pskl.model.Piskel.fromLayers(resizedLayers, this.piskelController.getPiskel().getDescriptor()); + + pskl.app.piskelController.setPiskel(piskel, true); + $.publish(Events.CLOSE_SETTINGS_DRAWER); + }; + + + ns.AbstractResizeController.prototype.resizeLayer_ = function (layer) { + var resizedFrames = layer.getFrames().map(this.resizeFrame_.bind(this)); + return pskl.model.Layer.fromFrames(layer.getName(), resizedFrames); + }; + + ns.AbstractResizeController.prototype.resizeFrame_ = Constants.ABSTRACT_FUNCTION; + + ns.AbstractResizeController.prototype.onCancelButtonClicked_ = function () { + $.publish(Events.CLOSE_SETTINGS_DRAWER); + }; + + ns.AbstractResizeController.prototype.onMaintainRatioChange_ = function (evt) { + var target = evt.target; + if (target.checked) { + this.synchronizeSizeInputs_(this.lastInput); + } + }; + + ns.AbstractResizeController.prototype.onSizeInputKeyUp_ = function (evt) { + var target = evt.target; + if (this.maintainRatioCheckbox.checked) { + this.synchronizeSizeInputs_(target); + } + this.lastInput = target; + }; + + /** + * Based on the value of the provided sizeInput (considered as emitter) + * update the value of the other sizeInput to match the current width/height ratio + * @param {HTMLElement} origin either widthInput or heightInput + */ + ns.AbstractResizeController.prototype.synchronizeSizeInputs_ = function (sizeInput) { + var value = parseInt(sizeInput.value, 10); + if (isNaN(value)) { + value = 0; + } + + var height = this.piskelController.getHeight(), + width = this.piskelController.getWidth(); + + if (sizeInput === this.widthInput) { + this.heightInput.value = Math.round(value * height/width); + } else if (sizeInput === this.heightInput) { + this.widthInput.value = Math.round(value * width/height); + } + }; +})(); \ No newline at end of file diff --git a/src/js/controller/settings/resize/ResizeCanvasController.js b/src/js/controller/settings/resize/ResizeCanvasController.js new file mode 100644 index 00000000..d474654e --- /dev/null +++ b/src/js/controller/settings/resize/ResizeCanvasController.js @@ -0,0 +1,124 @@ +(function () { + var ns = $.namespace('pskl.controller.settings.resize'); + + var ORIGIN = { + TOPLEFT : 'TOPLEFT', + TOP : 'TOP', + TOPRIGHT : 'TOPRIGHT', + MIDDLELEFT : 'MIDDLELEFT', + MIDDLE : 'MIDDLE', + MIDDLERIGHT : 'MIDDLERIGHT', + BOTTOMLEFT : 'BOTTOMLEFT', + BOTTOM : 'BOTTOM', + BOTTOMRIGHT : 'BOTTOMRIGHT' + }; + + ns.ResizeCanvasController = function (piskelController, container) { + this.superclass.constructor.call(this, piskelController, container); + this.origin = ORIGIN.TOPLEFT; + }; + + pskl.utils.inherit(ns.ResizeCanvasController, ns.AbstractResizeController); + + ns.ResizeCanvasController.prototype.init = function () { + this.superclass.init.call(this); + + this.resizeOrigin = document.querySelector('.resize-origin-container'); + this.resizeOrigin.addEventListener('click', this.onResizeOriginClick_.bind(this)); + + this.setOrigin_(ORIGIN.TOPLEFT); + }; + + /****************/ + /* RESIZE LOGIC */ + /****************/ + + ns.ResizeCanvasController.prototype.resizeFrame_ = function (frame) { + var width = parseInt(this.widthInput.value, 10); + var height = parseInt(this.heightInput.value, 10); + + var resizedFrame = new pskl.model.Frame(width, height); + frame.forEachPixel(function (color, x, y) { + var translated = this.translateCoordinates_(x, y, frame, resizedFrame); + if (resizedFrame.containsPixel(translated.x, translated.y)) { + resizedFrame.setPixel(translated.x, translated.y, color); + } + }.bind(this)); + + return resizedFrame; + }; + + ns.ResizeCanvasController.prototype.translateCoordinates_ = function (x, y, frame, resizedFrame) { + return { + x : this.translateX_(x, frame.width, resizedFrame.width), + y : this.translateY_(y, frame.height, resizedFrame.height) + }; + }; + + ns.ResizeCanvasController.prototype.translateX_ = function (x, width, resizedWidth) { + if (this.origin.indexOf('LEFT') != -1) { + return x; + } else if (this.origin.indexOf('RIGHT') != -1) { + return x - (width - resizedWidth); + } else { + return x - Math.round((width - resizedWidth)/2); + } + }; + + ns.ResizeCanvasController.prototype.translateY_ = function (y, height, resizedHeight) { + if (this.origin.indexOf('TOP') != -1) { + return y; + } else if (this.origin.indexOf('BOTTOM') != -1) { + return y - (height - resizedHeight); + } else { + return y - Math.round((height - resizedHeight)/2); + } + }; + + /*****************/ + /* ANCHOR WIDGET */ + /*****************/ + + ns.ResizeCanvasController.prototype.onResizeOriginClick_ = function (evt) { + var origin = evt.target.dataset.origin; + if (origin && ORIGIN[origin]) { + this.setOrigin_(origin); + } + }; + + ns.ResizeCanvasController.prototype.setOrigin_ = function (origin) { + this.origin = origin; + var previous = document.querySelector('.resize-origin-option.selected'); + if (previous) { + previous.classList.remove('selected'); + } + + var selected = document.querySelector('.resize-origin-option[data-origin="' + origin + '"]'); + if (selected) { + selected.classList.add('selected'); + this.refreshNeighbors_(selected); + } + }; + + ns.ResizeCanvasController.prototype.refreshNeighbors_ = function (selected) { + var options = document.querySelectorAll('.resize-origin-option'); + for (var i = 0 ; i < options.length ; i++) { + options[i].removeAttribute('data-neighbor'); + } + + var selectedIndex = Array.prototype.indexOf.call(options, selected); + + this.setNeighborhood_(options[selectedIndex - 1], 'left'); + this.setNeighborhood_(options[selectedIndex + 1], 'right'); + this.setNeighborhood_(options[selectedIndex - 3], 'top'); + this.setNeighborhood_(options[selectedIndex + 3], 'bottom'); + }; + + ns.ResizeCanvasController.prototype.setNeighborhood_ = function (el, neighborhood) { + var origin = this.origin.toLowerCase(); + var isNeighborhoodValid = origin.indexOf(neighborhood) === -1; + if (isNeighborhoodValid) { + el.setAttribute('data-neighbor', neighborhood); + } + }; +})(); \ No newline at end of file diff --git a/src/js/controller/settings/resize/ResizeContentController.js b/src/js/controller/settings/resize/ResizeContentController.js new file mode 100644 index 00000000..4c192ced --- /dev/null +++ b/src/js/controller/settings/resize/ResizeContentController.js @@ -0,0 +1,19 @@ +(function () { + var ns = $.namespace('pskl.controller.settings.resize'); + + ns.ResizeContentController = function (piskelController, container) { + this.superclass.constructor.call(this, piskelController, container); + }; + + pskl.utils.inherit(ns.ResizeContentController, ns.AbstractResizeController); + + ns.ResizeContentController.prototype.init = function () { + this.superclass.init.call(this); + }; + + ns.ResizeContentController.prototype.resizeFrame_ = function (frame) { + var width = parseInt(this.widthInput.value, 10); + var height = parseInt(this.heightInput.value, 10); + return pskl.utils.FrameUtils.resize(frame, width, height, false); + }; +})(); \ No newline at end of file diff --git a/src/js/controller/settings/resize/ResizeController.js b/src/js/controller/settings/resize/ResizeController.js new file mode 100644 index 00000000..1d509f2c --- /dev/null +++ b/src/js/controller/settings/resize/ResizeController.js @@ -0,0 +1,16 @@ +(function () { + var ns = $.namespace('pskl.controller.settings.resize'); + + ns.ResizeController = function (piskelController) { + var resizeCanvasContainer = document.querySelector('.resize-canvas'); + this.resizeCanvasController = new ns.ResizeCanvasController(piskelController, resizeCanvasContainer); + + var resizeContentContainer = document.querySelector('.resize-content'); + this.resizeContentController = new ns.ResizeContentController(piskelController, resizeContentContainer); + }; + + ns.ResizeController.prototype.init = function () { + this.resizeCanvasController.init(); + this.resizeContentController.init(); + }; +})(); \ No newline at end of file diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index e1c2e2bb..6e0f40ce 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -96,10 +96,13 @@ // Settings sub-controllers "js/controller/settings/ApplicationSettingsController.js", - "js/controller/settings/ResizeController.js", - "js/controller/settings/ImageExportController.js", - "js/controller/settings/GifExportController.js", - "js/controller/settings/PngExportController.js", + "js/controller/settings/exportimage/ImageExportController.js", + "js/controller/settings/exportimage/GifExportController.js", + "js/controller/settings/exportimage/PngExportController.js", + "js/controller/settings/resize/ResizeController.js", + "js/controller/settings/resize/AbstractResizeController.js", + "js/controller/settings/resize/ResizeCanvasController.js", + "js/controller/settings/resize/ResizeContentController.js", "js/controller/settings/SaveController.js", "js/controller/settings/ImportController.js", diff --git a/src/templates/settings/resize.html b/src/templates/settings/resize.html index 47398939..12a43032 100644 --- a/src/templates/settings/resize.html +++ b/src/templates/settings/resize.html @@ -1,9 +1,10 @@
+
- Resize drawing area + Canvas size
-
-
+
+
Width @@ -15,22 +16,51 @@ px
- Origin -
-
TOP-L
-
TOP
-
TOP-R
-
MID-L
-
MID
-
MID-R
-
BOT-L
-
BOT
-
BOT-R
-
+
- - Resize content + Anchor +
+
+
+
+
+
+
+
+
+
+
+
+ + + +
+ + +
+ Image size +
+
+
+
+ Width + + px +
+
+ Height + + px +
+
+