diff --git a/misc/icons/SVG/flip.svg b/misc/icons/SVG/flip.svg
new file mode 100644
index 00000000..429112be
--- /dev/null
+++ b/misc/icons/SVG/flip.svg
@@ -0,0 +1,88 @@
+
+
+
+
diff --git a/misc/icons/noun-project/sheep/icon_8389.png b/misc/icons/noun-project/sheep/icon_8389.png
new file mode 100644
index 00000000..2e3dbf76
Binary files /dev/null and b/misc/icons/noun-project/sheep/icon_8389.png differ
diff --git a/misc/icons/noun-project/sheep/icon_8389.svg b/misc/icons/noun-project/sheep/icon_8389.svg
new file mode 100644
index 00000000..673f4f70
--- /dev/null
+++ b/misc/icons/noun-project/sheep/icon_8389.svg
@@ -0,0 +1,41 @@
+
+
+
+
diff --git a/misc/icons/noun-project/sheep/license.txt b/misc/icons/noun-project/sheep/license.txt
new file mode 100644
index 00000000..31ca6c35
--- /dev/null
+++ b/misc/icons/noun-project/sheep/license.txt
@@ -0,0 +1,8 @@
+Thank you for using The Noun Project. This icon is licensed under Creative
+Commons Attribution and must be attributed as:
+
+ Sheep by Unrecognized MJ from The Noun Project
+
+If you have a Premium Account or have purchased a license for this icon, you
+don't need to worry about attribution! We will share the profits from your
+purchase with this icon's designer.
diff --git a/misc/icons/noun-project/undo-kyle_levi_fox/icon_10033.svg b/misc/icons/noun-project/undo-kyle_levi_fox/icon_10033.svg
new file mode 100644
index 00000000..a65a77e1
--- /dev/null
+++ b/misc/icons/noun-project/undo-kyle_levi_fox/icon_10033.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/misc/icons/noun-project/undo-kyle_levi_fox/license.txt b/misc/icons/noun-project/undo-kyle_levi_fox/license.txt
new file mode 100644
index 00000000..90fdf374
--- /dev/null
+++ b/misc/icons/noun-project/undo-kyle_levi_fox/license.txt
@@ -0,0 +1,8 @@
+Thank you for using The Noun Project. This icon is licensed under Creative
+Commons Attribution and must be attributed as:
+
+ Undo by Kyle Levi Fox from The Noun Project
+
+If you have a Premium Account or have purchased a license for this icon, you
+don't need to worry about attribution! We will share the profits from your
+purchase with this icon's designer.
diff --git a/src/css/layout.css b/src/css/layout.css
new file mode 100644
index 00000000..37645a45
--- /dev/null
+++ b/src/css/layout.css
@@ -0,0 +1,154 @@
+/**
+ * Application layout
+ */
+
+.main-wrapper {
+ position: absolute;
+ top: 5px;
+ right: 0;
+ bottom: 5px;
+ left: 0;
+}
+
+.column-wrapper {
+ text-align: center;
+ font-size: 0;
+ position: absolute;
+ left: 100px; /* Reserve room for tools on the left edge of the screen. */
+ top: 0;
+ right: 50px; /* Reserve room for actions on the right edge of the screen. */
+ bottom: 0;
+}
+
+.column {
+ display: inline-block;
+}
+
+.left-column {
+ vertical-align: top;
+ height: 100%;
+ margin-right: 7px;
+}
+
+.main-column {
+ height: 100%;
+ position: relative;
+}
+
+.right-column {
+ vertical-align: top;
+ margin-left: 10px;
+ height: 100%;
+ position: relative;
+}
+
+.drawing-canvas-container {
+ font-size: 0;
+}
+
+.sticky-section {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ z-index: 1000;
+}
+
+.sticky-section .sticky-section-wrap {
+ display: table;
+ height: 100%;
+}
+
+.sticky-section .vertical-centerer {
+ display: table-cell;
+ vertical-align: middle;
+}
+
+.left-sticky-section.sticky-section {
+ left: 0;
+ max-width: 100px;
+}
+
+.left-sticky-section .tool-icon {
+ float: left;
+}
+
+.cursor-coordinates {
+ color:#888;
+ font-size:12px;
+ font-weight:bold;
+ font-family:Courier;
+}
+
+/**
+ * Canvases layout
+ */
+
+.canvas {
+ position: relative;
+ z-index: 1;
+}
+
+.canvas-container {
+ position: relative;
+ display: block;
+}
+
+.canvas-container .canvas-background {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.light-picker-background,
+.light-canvas-background .canvas-background {
+ background: url(../img/canvas_background/light_canvas_background.png) repeat;
+}
+
+.medium-picker-background,
+.medium-canvas-background .canvas-background {
+ background: url(../img/canvas_background/medium_canvas_background.png) repeat;
+}
+
+.lowcont-medium-picker-background,
+.lowcont-medium-canvas-background .canvas-background {
+ background: url(../img/canvas_background/lowcont_medium_canvas_background.png) repeat;
+}
+
+.lowcont-dark-picker-background,
+.lowcont-dark-canvas-background .canvas-background {
+ background: url(../img/canvas_background/lowcont_dark_canvas_background.png) repeat;
+}
+
+.layers-canvas,
+.canvas.onion-skin-canvas {
+ opacity: 0.2;
+}
+
+.canvas.canvas-overlay,
+.canvas.layers-canvas,
+.canvas.onion-skin-canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.tools-wrapper,
+.options-wrapper,
+.palette-wrapper {
+ float : left;
+}
+
+/**
+ * Z-indexes should match the drawing area canvas superposition order :
+ * - 1 : draw layers below current layer
+ * - 2 : draw current layer
+ * - 3 : draw layers above current layer
+ * - 4 : draw the tools overlay
+ */
+.canvas.layers-below-canvas {z-index: 7;}
+.canvas.drawing-canvas {z-index: 8;}
+.canvas.canvas-overlay {z-index: 9;}
+.canvas.onion-skin-canvas {z-index: 10;}
+.canvas.layers-above-canvas {z-index: 11;}
\ No newline at end of file
diff --git a/src/css/style.css b/src/css/style.css
index deaa44a7..d1aa309d 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -20,148 +20,10 @@ body {
user-select: initial;
}
-/**
- * Application layout
- */
-
-.main-wrapper {
- position: absolute;
- top: 5px;
- right: 0;
- bottom: 5px;
- left: 0;
+.no-overflow {
+ overflow : hidden;
}
-.column-wrapper {
- text-align: center;
- font-size: 0;
- position: absolute;
- left: 100px; /* Reserve room for tools on the left edge of the screen. */
- top: 0;
- right: 50px; /* Reserve room for actions on the right edge of the screen. */
- bottom: 0;
-}
-
-.column {
- display: inline-block;
-}
-
-.left-column {
- vertical-align: top;
- height: 100%;
- margin-right: 7px;
-}
-
-.main-column {
- height: 100%;
- position: relative;
-}
-
-.right-column {
- vertical-align: top;
- margin-left: 10px;
- height: 100%;
- position: relative;
-}
-
-.drawing-canvas-container {
- font-size: 0;
-}
-
-.sticky-section {
- position: fixed;
- top: 0;
- bottom: 0;
- z-index: 1000;
-}
-
-.sticky-section .sticky-section-wrap {
- display: table;
- height: 100%;
-}
-
-.sticky-section .vertical-centerer {
- display: table-cell;
- vertical-align: middle;
-}
-
-.left-sticky-section.sticky-section {
- left: 0;
- max-width: 100px;
-}
-
-.left-sticky-section .tool-icon {
- float: left;
-}
-
-/**
- * Canvases layout
- */
-
-.canvas {
- position: relative;
- z-index: 1;
-}
-
-.canvas-container {
- position: relative;
- display: block;
-}
-
-.canvas-container .canvas-background {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
-}
-
-.light-picker-background,
-.light-canvas-background .canvas-background {
- background: url(../img/canvas_background/light_canvas_background.png) repeat;
-}
-
-.medium-picker-background,
-.medium-canvas-background .canvas-background {
- background: url(../img/canvas_background/medium_canvas_background.png) repeat;
-}
-
-.lowcont-medium-picker-background,
-.lowcont-medium-canvas-background .canvas-background {
- background: url(../img/canvas_background/lowcont_medium_canvas_background.png) repeat;
-}
-
-.lowcont-dark-picker-background,
-.lowcont-dark-canvas-background .canvas-background {
- background: url(../img/canvas_background/lowcont_dark_canvas_background.png) repeat;
-}
-
-.layers-canvas,
-.canvas.onion-skin-canvas {
- opacity: 0.2;
-}
-
-.canvas.canvas-overlay,
-.canvas.layers-canvas,
-.canvas.onion-skin-canvas {
- position: absolute;
- top: 0;
- left: 0;
-}
-
-/**
- * Z-indexes should match the drawing area canvas superposition order :
- * - 1 : draw layers below current layer
- * - 2 : draw current layer
- * - 3 : draw layers above current layer
- * - 4 : draw the tools overlay
- */
-.canvas.layers-below-canvas {z-index: 7;}
-.canvas.drawing-canvas {z-index: 8;}
-.canvas.canvas-overlay {z-index: 9;}
-.canvas.onion-skin-canvas {z-index: 10;}
-.canvas.layers-above-canvas {z-index: 11;}
-
.image-link {
color : gold;
}
@@ -188,10 +50,3 @@ body {
.pull-left {
left:0;
}
-
-.cursor-coordinates {
- color:#888;
- font-size:12px;
- font-weight:bold;
- font-family:Courier;
-}
\ No newline at end of file
diff --git a/src/css/tools.css b/src/css/tools.css
index c27716a5..89a2e907 100644
--- a/src/css/tools.css
+++ b/src/css/tools.css
@@ -1,10 +1,3 @@
-
-.tools-wrapper,
-.options-wrapper,
-.palette-wrapper {
- float: left;
-}
-
.tool-icon {
position : relative;
cursor : pointer;
@@ -88,8 +81,8 @@
.tool-icon.tool-lighten {
background-image: url(../img/tools/lighten.png);
- background-size: 30px 30px;
background-position: 8px 8px;
+ background-size: 30px 30px;
}
.tool-icon.tool-colorpicker {
@@ -103,6 +96,24 @@
background-size: 36px 36px;
}
+.tool-icon.tool-flip {
+ background-image: url(../img/tools/flip.png);
+ background-position: 7px 11px;
+ background-size: 32px;
+}
+
+.tool-icon.tool-rotate {
+ background-image: url(../img/tools/rotate.png);
+ background-position: 10px 9px;
+ background-size: 26px;
+}
+
+.tool-icon.tool-clone {
+ background-image: url(../img/tools/clone.png);
+ background-position: 9px 15px;
+ background-size: 30px;
+}
+
/*
* Tool cursors:
*/
diff --git a/src/css/transformations.css b/src/css/transformations.css
new file mode 100644
index 00000000..13c7c8da
--- /dev/null
+++ b/src/css/transformations.css
@@ -0,0 +1,3 @@
+.transformations-container .tool-icon {
+ float:left;
+}
\ No newline at end of file
diff --git a/src/img/tools/clone.png b/src/img/tools/clone.png
new file mode 100644
index 00000000..ca02ff71
Binary files /dev/null and b/src/img/tools/clone.png differ
diff --git a/src/img/tools/flip.png b/src/img/tools/flip.png
new file mode 100644
index 00000000..c2fbc8c8
Binary files /dev/null and b/src/img/tools/flip.png differ
diff --git a/src/img/tools/rotate.png b/src/img/tools/rotate.png
new file mode 100644
index 00000000..cea4c5cb
Binary files /dev/null and b/src/img/tools/rotate.png differ
diff --git a/src/index.html b/src/index.html
index 99b1e0e7..03672105 100644
--- a/src/index.html
+++ b/src/index.html
@@ -44,6 +44,7 @@
diff --git a/src/js/app.js b/src/js/app.js
index fe8eb764..8d626e02 100644
--- a/src/js/app.js
+++ b/src/js/app.js
@@ -87,6 +87,9 @@
this.notificationController = new pskl.controller.NotificationController();
this.notificationController.init();
+ this.transformationsController = new pskl.controller.TransformationsController();
+ this.transformationsController.init();
+
this.progressBarController = new pskl.controller.ProgressBarController();
this.progressBarController.init();
diff --git a/src/js/controller/ToolController.js b/src/js/controller/ToolController.js
index 270718c8..d21e4c10 100644
--- a/src/js/controller/ToolController.js
+++ b/src/js/controller/ToolController.js
@@ -7,23 +7,22 @@
};
this.tools = [
- toDescriptor('simplePen', 'P', new pskl.drawingtools.SimplePen()),
- toDescriptor('verticalMirrorPen', 'V', new pskl.drawingtools.VerticalMirrorPen()),
- toDescriptor('paintBucket', 'B', new pskl.drawingtools.PaintBucket()),
- toDescriptor('colorSwap', 'A', new pskl.drawingtools.ColorSwap()),
- toDescriptor('eraser', 'E', new pskl.drawingtools.Eraser()),
- toDescriptor('stroke', 'L', new pskl.drawingtools.Stroke()),
- toDescriptor('rectangle', 'R', new pskl.drawingtools.Rectangle()),
- toDescriptor('circle', 'C', new pskl.drawingtools.Circle()),
- toDescriptor('move', 'M', new pskl.drawingtools.Move()),
- toDescriptor('rectangleSelect', 'S', new pskl.drawingtools.RectangleSelect()),
- toDescriptor('shapeSelect', 'Z', new pskl.drawingtools.ShapeSelect()),
- toDescriptor('lighten', 'U', new pskl.drawingtools.Lighten()),
- toDescriptor('colorPicker', 'O', new pskl.drawingtools.ColorPicker())
+ toDescriptor('simplePen', 'P', new pskl.tools.drawing.SimplePen()),
+ toDescriptor('verticalMirrorPen', 'V', new pskl.tools.drawing.VerticalMirrorPen()),
+ toDescriptor('paintBucket', 'B', new pskl.tools.drawing.PaintBucket()),
+ toDescriptor('colorSwap', 'A', new pskl.tools.drawing.ColorSwap()),
+ toDescriptor('eraser', 'E', new pskl.tools.drawing.Eraser()),
+ toDescriptor('stroke', 'L', new pskl.tools.drawing.Stroke()),
+ toDescriptor('rectangle', 'R', new pskl.tools.drawing.Rectangle()),
+ toDescriptor('circle', 'C', new pskl.tools.drawing.Circle()),
+ toDescriptor('move', 'M', new pskl.tools.drawing.Move()),
+ toDescriptor('rectangleSelect', 'S', new pskl.tools.drawing.RectangleSelect()),
+ toDescriptor('shapeSelect', 'Z', new pskl.tools.drawing.ShapeSelect()),
+ toDescriptor('lighten', 'U', new pskl.tools.drawing.Lighten()),
+ toDescriptor('colorPicker', 'O', new pskl.tools.drawing.ColorPicker())
];
- this.currentSelectedTool = this.tools[0];
- this.previousSelectedTool = this.tools[0];
+ this.toolIconRenderer = new pskl.tools.IconMarkupRenderer();
};
/**
@@ -50,7 +49,7 @@
var previousSelectedToolClass = stage.data("selected-tool-class");
if(previousSelectedToolClass) {
stage.removeClass(previousSelectedToolClass);
- stage.removeClass(pskl.drawingtools.Move.TOOL_ID);
+ stage.removeClass(pskl.tools.drawing.Move.TOOL_ID);
}
stage.addClass(tool.instance.toolId);
stage.data("selected-tool-class", tool.instance.toolId);
@@ -105,43 +104,21 @@
};
ns.ToolController.prototype.getToolById_ = function (toolId) {
- for(var i = 0 ; i < this.tools.length ; i++) {
- var tool = this.tools[i];
- if (tool.instance.toolId == toolId) {
- return tool;
- }
- }
- return null;
+ return pskl.utils.Array.find(this.tools, function (tool) {
+ return tool.instance.toolId == toolId;
+ });
};
/**
* @private
*/
ns.ToolController.prototype.createToolsDom_ = function() {
- var toolMarkup = '';
+ var html = '';
for(var i = 0 ; i < this.tools.length ; i++) {
- toolMarkup += this.getToolMarkup_(this.tools[i]);
+ var tool = this.tools[i];
+ html += this.toolIconRenderer.render(tool.instance, tool.shortcut);
}
- $('#tools-container').html(toolMarkup);
- };
-
- /**
- * @private
- */
- ns.ToolController.prototype.getToolMarkup_ = function(tool) {
- var toolId = tool.instance.toolId;
-
- var classList = ['tool-icon', toolId];
- if (this.currentSelectedTool == tool) {
- classList.push('selected');
- }
-
- var tpl = pskl.utils.Template.get('drawing-tool-item-template');
- return pskl.utils.Template.replace(tpl, {
- cssclass : classList.join(' '),
- toolid : toolId,
- title : tool.instance.getTooltipText(tool.shortcut)
- });
+ $('#tools-container').html(html);
};
ns.ToolController.prototype.addKeyboardShortcuts_ = function () {
diff --git a/src/js/controller/TransformationsController.js b/src/js/controller/TransformationsController.js
new file mode 100644
index 00000000..d1e3a71d
--- /dev/null
+++ b/src/js/controller/TransformationsController.js
@@ -0,0 +1,42 @@
+(function () {
+ var ns = $.namespace('pskl.controller');
+
+ ns.TransformationsController = function () {
+
+ var toDescriptor = function (id, shortcut, instance) {
+ return {id:id, shortcut:shortcut, instance:instance};
+ };
+
+ this.tools = [
+ toDescriptor('flip', '', new pskl.tools.transform.Flip()),
+ toDescriptor('rotate', '', new pskl.tools.transform.Rotate()),
+ toDescriptor('clone', '', new pskl.tools.transform.Clone())
+ ];
+
+ this.toolIconRenderer = new pskl.tools.IconMarkupRenderer();
+ };
+
+ ns.TransformationsController.prototype.init = function () {
+ var container = document.querySelector('.transformations-container');
+ this.toolsContainer = container.querySelector('.tools-wrapper');
+ container.addEventListener('click', this.onTransformationClick.bind(this));
+ this.createToolsDom_();
+ };
+
+
+ ns.TransformationsController.prototype.onTransformationClick = function (evt) {
+ var toolId = evt.target.dataset.toolId;
+ this.tools.forEach(function (tool) {
+ if (tool.instance.toolId === toolId) {
+ tool.instance.apply(evt);
+ }
+ }.bind(this));
+ };
+
+ ns.TransformationsController.prototype.createToolsDom_ = function() {
+ var html = this.tools.reduce(function (p, tool) {
+ return p + this.toolIconRenderer.render(tool.instance, tool.shortcut, 'left');
+ }.bind(this), '');
+ this.toolsContainer.innerHTML = html;
+ };
+})();
\ No newline at end of file
diff --git a/src/js/controller/dialogs/CreatePaletteController.js b/src/js/controller/dialogs/CreatePaletteController.js
index fa777fcc..ba729897 100644
--- a/src/js/controller/dialogs/CreatePaletteController.js
+++ b/src/js/controller/dialogs/CreatePaletteController.js
@@ -27,7 +27,7 @@
importFileButton.addEventListener('click', this.onImportFileButtonClick_.bind(this));
var colorsListContainer = document.querySelector('.colors-container');
- this.colorsListWidget = new pskl.controller.widgets.ColorsList(colorsListContainer);
+ this.colorsListWidget = new pskl.widgets.ColorsList(colorsListContainer);
var palette;
var isCurrentColorsPalette = paletteId == Constants.CURRENT_COLORS_PALETTE_ID;
diff --git a/src/js/selection/SelectionManager.js b/src/js/selection/SelectionManager.js
index 2088b36a..e6a90214 100644
--- a/src/js/selection/SelectionManager.js
+++ b/src/js/selection/SelectionManager.js
@@ -41,7 +41,7 @@
* @private
*/
ns.SelectionManager.prototype.onToolSelected_ = function(evt, tool) {
- var isSelectionTool = tool instanceof pskl.drawingtools.BaseSelect;
+ var isSelectionTool = tool instanceof pskl.tools.drawing.BaseSelect;
if(!isSelectionTool) {
this.cleanSelection_();
}
diff --git a/src/js/tools/IconMarkupRenderer.js b/src/js/tools/IconMarkupRenderer.js
new file mode 100644
index 00000000..e4979283
--- /dev/null
+++ b/src/js/tools/IconMarkupRenderer.js
@@ -0,0 +1,49 @@
+(function () {
+ var ns = $.namespace('pskl.tools');
+
+ ns.IconMarkupRenderer = function () {};
+
+ ns.IconMarkupRenderer.prototype.render = function (tool, shortcut, tooltipPosition) {
+ tooltipPosition = tooltipPosition || 'right';
+ shortcut = shortcut ? '(' + shortcut + ')' : '';
+ var tpl = pskl.utils.Template.get('drawingTool-item-template');
+ return pskl.utils.Template.replace(tpl, {
+ cssclass : ['tool-icon', tool.toolId].join(' '),
+ toolid : tool.toolId,
+ title : this.getTooltipText(tool, shortcut),
+ tooltipposition : tooltipPosition
+ });
+ };
+
+ ns.IconMarkupRenderer.prototype.getTooltipText = function(tool, shortcut) {
+ var descriptors = tool.tooltipDescriptors;
+ var tpl = pskl.utils.Template.get('drawingTool-tooltipContainer-template');
+ return pskl.utils.Template.replace(tpl, {
+ helptext : tool.getHelpText(),
+ shortcut : shortcut,
+ descriptors : this.formatToolDescriptors_(descriptors)
+ });
+ };
+
+
+ ns.IconMarkupRenderer.prototype.formatToolDescriptors_ = function(descriptors) {
+ descriptors = descriptors || [];
+ return descriptors.reduce(function (p, descriptor) {
+ return p += this.formatToolDescriptor_(descriptor);
+ }.bind(this), '');
+ };
+
+ ns.IconMarkupRenderer.prototype.formatToolDescriptor_ = function(descriptor) {
+ var tpl;
+ if (descriptor.key) {
+ tpl = pskl.utils.Template.get('drawingTool-tooltipDescriptor-template');
+ descriptor.key = descriptor.key.toUpperCase();
+ if (pskl.utils.UserAgent.isMac) {
+ descriptor.key = descriptor.key.replace('CTRL', 'CMD');
+ }
+ } else {
+ tpl = pskl.utils.Template.get('drawingTool-simpleTooltipDescriptor-template');
+ }
+ return pskl.utils.Template.replace(tpl, descriptor);
+ };
+})();
\ No newline at end of file
diff --git a/src/js/tools/Tool.js b/src/js/tools/Tool.js
new file mode 100644
index 00000000..a0a476fe
--- /dev/null
+++ b/src/js/tools/Tool.js
@@ -0,0 +1,17 @@
+(function () {
+ var ns = $.namespace('pskl.tools');
+
+ ns.Tool = function () {
+ this.toolId = "tool";
+ this.helpText = "Abstract tool";
+ this.tooltipDescriptors = [];
+ };
+
+ ns.Tool.prototype.getHelpText = function() {
+ return this.helpText;
+ };
+
+ ns.Tool.prototype.getId = function() {
+ return this.toolId;
+ };
+})();
\ No newline at end of file
diff --git a/src/js/drawingtools/BaseTool.js b/src/js/tools/drawing/BaseTool.js
similarity index 68%
rename from src/js/drawingtools/BaseTool.js
rename to src/js/tools/drawing/BaseTool.js
index 2a364c2f..ea6480bb 100644
--- a/src/js/drawingtools/BaseTool.js
+++ b/src/js/tools/drawing/BaseTool.js
@@ -1,15 +1,18 @@
/**
- * @provide pskl.drawingtools.BaseTool
+ * @provide pskl.tools.drawing.BaseTool
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.BaseTool = function() {
+ pskl.tool.Tool.call(this);
this.toolId = "tool-base";
};
+ pskl.utils.inherit(ns.BaseTool, pskl.tools.Tool);
+
ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {};
ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {};
@@ -58,41 +61,6 @@
});
};
- ns.BaseTool.prototype.getHelpText = function() {
- return this.shortHelpText || this.helpText;
- };
-
- ns.BaseTool.prototype.getTooltipText = function(shortcut) {
- var tpl = pskl.utils.Template.get('drawing-tool-tooltip-container-template');
-
- var descriptors = "";
- if (Array.isArray(this.tooltipDescriptors)) {
- this.tooltipDescriptors.forEach(function (descriptor) {
- descriptors += this.getTooltipDescription(descriptor);
- }.bind(this));
- }
-
- return pskl.utils.Template.replace(tpl, {
- helptext : this.getHelpText(),
- shortcut : shortcut,
- descriptors : descriptors
- });
- };
-
- ns.BaseTool.prototype.getTooltipDescription = function(descriptor) {
- var tpl;
- if (descriptor.key) {
- tpl = pskl.utils.Template.get('drawing-tool-tooltip-descriptor-template');
- descriptor.key = descriptor.key.toUpperCase();
- if (pskl.utils.UserAgent.isMac) {
- descriptor.key = descriptor.key.replace('CTRL', 'CMD');
- }
- } else {
- tpl = pskl.utils.Template.get('drawing-tool-tooltip-descriptor-simple-template');
- }
- return pskl.utils.Template.replace(tpl, descriptor);
- };
-
ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {};
/**
diff --git a/src/js/drawingtools/Circle.js b/src/js/tools/drawing/Circle.js
similarity index 91%
rename from src/js/drawingtools/Circle.js
rename to src/js/tools/drawing/Circle.js
index 3b2f7da2..53bcdb2d 100644
--- a/src/js/drawingtools/Circle.js
+++ b/src/js/tools/drawing/Circle.js
@@ -1,52 +1,52 @@
-/**
- * @provide pskl.drawingtools.Circle
- *
- * @require pskl.utils
- */
-(function() {
- var ns = $.namespace("pskl.drawingtools");
-
- ns.Circle = function() {
- ns.ShapeTool.call(this);
-
- this.toolId = "tool-circle";
-
- this.helpText = "Circle tool";
- };
-
- pskl.utils.inherit(ns.Circle, ns.ShapeTool);
-
- ns.Circle.prototype.draw_ = function (col, row, color, targetFrame) {
- var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row);
- for(var i = 0; i< circlePoints.length; i++) {
- // Change model:
- targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color);
- }
- };
-
- ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) {
- var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1);
- var xC = (coords.x0 + coords.x1)/2;
- var yC = (coords.y0 + coords.y1)/2;
-
- var rX = coords.x1 - xC;
- var rY = coords.y1 - yC;
-
- var pixels = [];
- var x, y, angle;
- for (x = coords.x0 ; x < coords.x1 ; x++) {
- angle = Math.acos((x - xC)/rX);
- y = Math.round(rY * Math.sin(angle) + yC);
- pixels.push({"col": x, "row": y});
- pixels.push({"col": 2*xC - x, "row": 2*yC - y});
- }
-
- for (y = coords.y0 ; y < coords.y1 ; y++) {
- angle = Math.asin((y - yC)/rY);
- x = Math.round(rX * Math.cos(angle) + xC);
- pixels.push({"col": x, "row": y});
- pixels.push({"col": 2*xC - x, "row": 2*yC - y});
- }
- return pixels;
- };
-})();
+/**
+ * @provide pskl.tools.drawing.Circle
+ *
+ * @require pskl.utils
+ */
+(function() {
+ var ns = $.namespace("pskl.tools.drawing");
+
+ ns.Circle = function() {
+ ns.ShapeTool.call(this);
+
+ this.toolId = "tool-circle";
+
+ this.helpText = "Circle tool";
+ };
+
+ pskl.utils.inherit(ns.Circle, ns.ShapeTool);
+
+ ns.Circle.prototype.draw_ = function (col, row, color, targetFrame) {
+ var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row);
+ for(var i = 0; i< circlePoints.length; i++) {
+ // Change model:
+ targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color);
+ }
+ };
+
+ ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) {
+ var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1);
+ var xC = (coords.x0 + coords.x1)/2;
+ var yC = (coords.y0 + coords.y1)/2;
+
+ var rX = coords.x1 - xC;
+ var rY = coords.y1 - yC;
+
+ var pixels = [];
+ var x, y, angle;
+ for (x = coords.x0 ; x < coords.x1 ; x++) {
+ angle = Math.acos((x - xC)/rX);
+ y = Math.round(rY * Math.sin(angle) + yC);
+ pixels.push({"col": x, "row": y});
+ pixels.push({"col": 2*xC - x, "row": 2*yC - y});
+ }
+
+ for (y = coords.y0 ; y < coords.y1 ; y++) {
+ angle = Math.asin((y - yC)/rY);
+ x = Math.round(rX * Math.cos(angle) + xC);
+ pixels.push({"col": x, "row": y});
+ pixels.push({"col": 2*xC - x, "row": 2*yC - y});
+ }
+ return pixels;
+ };
+})();
diff --git a/src/js/drawingtools/ColorPicker.js b/src/js/tools/drawing/ColorPicker.js
similarity index 88%
rename from src/js/drawingtools/ColorPicker.js
rename to src/js/tools/drawing/ColorPicker.js
index 5a0de4ee..b30a4d69 100644
--- a/src/js/drawingtools/ColorPicker.js
+++ b/src/js/tools/drawing/ColorPicker.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.ColorPicker
+ * @provide pskl.tools.drawing.ColorPicker
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.ColorPicker = function() {
this.toolId = "tool-colorpicker";
diff --git a/src/js/drawingtools/ColorSwap.js b/src/js/tools/drawing/ColorSwap.js
similarity index 94%
rename from src/js/drawingtools/ColorSwap.js
rename to src/js/tools/drawing/ColorSwap.js
index 47cc8e26..54de9813 100644
--- a/src/js/drawingtools/ColorSwap.js
+++ b/src/js/tools/drawing/ColorSwap.js
@@ -1,9 +1,9 @@
/**
- * @provide pskl.drawingtools.ColorSwap
+ * @provide pskl.tools.drawing.ColorSwap
*
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.ColorSwap = function() {
this.toolId = "tool-colorswap";
diff --git a/src/js/drawingtools/Eraser.js b/src/js/tools/drawing/Eraser.js
similarity index 89%
rename from src/js/drawingtools/Eraser.js
rename to src/js/tools/drawing/Eraser.js
index 27243f7e..6a40b674 100644
--- a/src/js/drawingtools/Eraser.js
+++ b/src/js/tools/drawing/Eraser.js
@@ -1,11 +1,11 @@
/**
- * @provide pskl.drawingtools.Eraser
+ * @provide pskl.tools.drawing.Eraser
*
* @require Constants
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.Eraser = function() {
this.superclass.constructor.call(this);
diff --git a/src/js/drawingtools/Lighten.js b/src/js/tools/drawing/Lighten.js
similarity index 95%
rename from src/js/drawingtools/Lighten.js
rename to src/js/tools/drawing/Lighten.js
index 4695169f..f6bba044 100644
--- a/src/js/drawingtools/Lighten.js
+++ b/src/js/tools/drawing/Lighten.js
@@ -1,11 +1,11 @@
/**
- * @provide pskl.drawingtools.Eraser
+ * @provide pskl.tools.drawing.Eraser
*
* @require Constants
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
var DEFAULT_STEP = 3;
ns.Lighten = function() {
diff --git a/src/js/drawingtools/Move.js b/src/js/tools/drawing/Move.js
similarity index 95%
rename from src/js/drawingtools/Move.js
rename to src/js/tools/drawing/Move.js
index a40735fa..51479b54 100644
--- a/src/js/drawingtools/Move.js
+++ b/src/js/tools/drawing/Move.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.Move
+ * @provide pskl.tools.drawing.Move
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.Move = function() {
this.toolId = ns.Move.TOOL_ID;
diff --git a/src/js/drawingtools/PaintBucket.js b/src/js/tools/drawing/PaintBucket.js
similarity index 88%
rename from src/js/drawingtools/PaintBucket.js
rename to src/js/tools/drawing/PaintBucket.js
index 357fb7cb..0e00663d 100644
--- a/src/js/drawingtools/PaintBucket.js
+++ b/src/js/tools/drawing/PaintBucket.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.PaintBucket
+ * @provide pskl.tools.drawing.PaintBucket
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.PaintBucket = function() {
this.toolId = "tool-paint-bucket";
diff --git a/src/js/drawingtools/Rectangle.js b/src/js/tools/drawing/Rectangle.js
similarity index 81%
rename from src/js/drawingtools/Rectangle.js
rename to src/js/tools/drawing/Rectangle.js
index 250da4b7..def6cace 100644
--- a/src/js/drawingtools/Rectangle.js
+++ b/src/js/tools/drawing/Rectangle.js
@@ -1,17 +1,17 @@
/**
- * @provide pskl.drawingtools.Rectangle
+ * @provide pskl.tools.drawing.Rectangle
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.Rectangle = function() {
ns.ShapeTool.call(this);
this.toolId = "tool-rectangle";
- this.shortHelpText = "Rectangle tool";
+ this.helpText = "Rectangle tool";
};
pskl.utils.inherit(ns.Rectangle, ns.ShapeTool);
diff --git a/src/js/drawingtools/ShapeTool.js b/src/js/tools/drawing/ShapeTool.js
similarity index 98%
rename from src/js/drawingtools/ShapeTool.js
rename to src/js/tools/drawing/ShapeTool.js
index e0916171..771a70e6 100644
--- a/src/js/drawingtools/ShapeTool.js
+++ b/src/js/tools/drawing/ShapeTool.js
@@ -1,5 +1,5 @@
(function () {
- var ns = $.namespace('pskl.drawingtools');
+ var ns = $.namespace('pskl.tools.drawing');
/**
* Abstract shape tool class, parent to all shape tools (rectangle, circle).
* Shape tools should override only the draw_ method
diff --git a/src/js/drawingtools/SimplePen.js b/src/js/tools/drawing/SimplePen.js
similarity index 96%
rename from src/js/drawingtools/SimplePen.js
rename to src/js/tools/drawing/SimplePen.js
index 993ca445..134ad231 100644
--- a/src/js/drawingtools/SimplePen.js
+++ b/src/js/tools/drawing/SimplePen.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.SimplePen
+ * @provide pskl.tools.drawing.SimplePen
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.SimplePen = function() {
this.toolId = "tool-pen";
diff --git a/src/js/drawingtools/Stroke.js b/src/js/tools/drawing/Stroke.js
similarity index 97%
rename from src/js/drawingtools/Stroke.js
rename to src/js/tools/drawing/Stroke.js
index 27c115ae..088a9aa5 100644
--- a/src/js/drawingtools/Stroke.js
+++ b/src/js/tools/drawing/Stroke.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.Stroke
+ * @provide pskl.tools.drawing.Stroke
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.Stroke = function() {
this.toolId = "tool-stroke";
diff --git a/src/js/drawingtools/VerticalMirrorPen.js b/src/js/tools/drawing/VerticalMirrorPen.js
similarity index 97%
rename from src/js/drawingtools/VerticalMirrorPen.js
rename to src/js/tools/drawing/VerticalMirrorPen.js
index e2d851a9..24d5a4a5 100644
--- a/src/js/drawingtools/VerticalMirrorPen.js
+++ b/src/js/tools/drawing/VerticalMirrorPen.js
@@ -1,5 +1,5 @@
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.VerticalMirrorPen = function() {
this.superclass.constructor.call(this);
diff --git a/src/js/drawingtools/selectiontools/BaseSelect.js b/src/js/tools/drawing/selection/BaseSelect.js
similarity index 97%
rename from src/js/drawingtools/selectiontools/BaseSelect.js
rename to src/js/tools/drawing/selection/BaseSelect.js
index ae33c364..2646ddf8 100644
--- a/src/js/drawingtools/selectiontools/BaseSelect.js
+++ b/src/js/tools/drawing/selection/BaseSelect.js
@@ -1,13 +1,13 @@
/**
- * @provide pskl.drawingtools.BaseSelect
+ * @provide pskl.tools.drawing.BaseSelect
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.BaseSelect = function() {
- this.secondaryToolId = pskl.drawingtools.Move.TOOL_ID;
+ this.secondaryToolId = pskl.tools.drawing.Move.TOOL_ID;
this.BodyRoot = $('body');
// Select's first point coordinates (set in applyToolAt)
diff --git a/src/js/drawingtools/selectiontools/RectangleSelect.js b/src/js/tools/drawing/selection/RectangleSelect.js
similarity index 94%
rename from src/js/drawingtools/selectiontools/RectangleSelect.js
rename to src/js/tools/drawing/selection/RectangleSelect.js
index 3abb4ba8..1f4843f6 100644
--- a/src/js/drawingtools/selectiontools/RectangleSelect.js
+++ b/src/js/tools/drawing/selection/RectangleSelect.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.RectangleSelect
+ * @provide pskl.tools.drawing.RectangleSelect
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.RectangleSelect = function() {
this.toolId = "tool-rectangle-select";
diff --git a/src/js/drawingtools/selectiontools/ShapeSelect.js b/src/js/tools/drawing/selection/ShapeSelect.js
similarity index 91%
rename from src/js/drawingtools/selectiontools/ShapeSelect.js
rename to src/js/tools/drawing/selection/ShapeSelect.js
index 4c829001..887d88ac 100644
--- a/src/js/drawingtools/selectiontools/ShapeSelect.js
+++ b/src/js/tools/drawing/selection/ShapeSelect.js
@@ -1,10 +1,10 @@
/**
- * @provide pskl.drawingtools.ShapeSelect
+ * @provide pskl.tools.drawing.ShapeSelect
*
* @require pskl.utils
*/
(function() {
- var ns = $.namespace("pskl.drawingtools");
+ var ns = $.namespace("pskl.tools.drawing");
ns.ShapeSelect = function() {
this.toolId = "tool-shape-select";
diff --git a/src/js/tools/transform/Clone.js b/src/js/tools/transform/Clone.js
new file mode 100644
index 00000000..e648adb7
--- /dev/null
+++ b/src/js/tools/transform/Clone.js
@@ -0,0 +1,25 @@
+(function () {
+ var ns = $.namespace('pskl.tools.transform');
+
+ ns.Clone = function () {
+ this.toolId = "tool-clone";
+ this.helpText = "Clone current layer to all frames";
+ this.tooltipDescriptors = [];
+ };
+
+ pskl.utils.inherit(ns.Clone, ns.Transform);
+
+ ns.Clone.prototype.apply = function (evt) {
+ var ref = pskl.app.piskelController.getCurrentFrame();
+ var layer = pskl.app.piskelController.getCurrentLayer();
+ layer.getFrames().forEach(function (frame) {
+ if (frame !== ref) {
+ frame.setPixels(ref.getPixels());
+ }
+ });
+ $.publish(Events.PISKEL_RESET);
+ $.publish(Events.PISKEL_SAVE_STATE, {
+ type : pskl.service.HistoryService.SNAPSHOT
+ });
+ };
+})();
\ No newline at end of file
diff --git a/src/js/tools/transform/Flip.js b/src/js/tools/transform/Flip.js
new file mode 100644
index 00000000..95d2ec6c
--- /dev/null
+++ b/src/js/tools/transform/Flip.js
@@ -0,0 +1,28 @@
+(function () {
+ var ns = $.namespace('pskl.tools.transform');
+
+ ns.Flip = function () {
+ this.toolId = "tool-flip";
+ this.helpText = "Flip vertically";
+ this.tooltipDescriptors = [
+ {key : 'alt', description : 'Flip horizontally'},
+ {key : 'ctrl', description : 'Apply to all layers'},
+ {key : 'shift', description : 'Apply to all frames'}
+ ];
+ };
+
+ pskl.utils.inherit(ns.Flip, ns.Transform);
+
+ ns.Flip.prototype.applyToolOnFrame_ = function (frame, altKey) {
+ var axis;
+
+ if (altKey) {
+ axis = pskl.utils.FrameTransform.HORIZONTAL;
+ } else {
+ axis = pskl.utils.FrameTransform.VERTICAL;
+ }
+
+ pskl.utils.FrameTransform.flip(frame, axis);
+ };
+
+})();
\ No newline at end of file
diff --git a/src/js/tools/transform/Rotate.js b/src/js/tools/transform/Rotate.js
new file mode 100644
index 00000000..082fdd27
--- /dev/null
+++ b/src/js/tools/transform/Rotate.js
@@ -0,0 +1,27 @@
+(function () {
+ var ns = $.namespace('pskl.tools.transform');
+
+ ns.Rotate = function () {
+ this.toolId = "tool-rotate";
+ this.helpText = "Counter-clockwise rotation";
+ this.tooltipDescriptors = [
+ {key : 'alt', description : 'Clockwise rotation'},
+ {key : 'ctrl', description : 'Apply to all layers'},
+ {key : 'shift', description : 'Apply to all frames'}];
+ };
+
+ pskl.utils.inherit(ns.Rotate, ns.Transform);
+
+ ns.Rotate.prototype.applyToolOnFrame_ = function (frame, altKey) {
+ var direction;
+
+ if (altKey) {
+ direction = pskl.utils.FrameTransform.CLOCKWISE;
+ } else {
+ direction = pskl.utils.FrameTransform.COUNTERCLOCKWISE;
+ }
+
+ pskl.utils.FrameTransform.rotate(frame, direction);
+ };
+
+})();
\ No newline at end of file
diff --git a/src/js/tools/transform/Transform.js b/src/js/tools/transform/Transform.js
new file mode 100644
index 00000000..7cd19f3d
--- /dev/null
+++ b/src/js/tools/transform/Transform.js
@@ -0,0 +1,33 @@
+(function () {
+ var ns = $.namespace('pskl.tools.transform');
+
+ ns.Transform = function () {
+ this.toolId = "tool-transform";
+ this.helpText = "Transform tool";
+ this.tooltipDescriptors = [];
+ };
+
+ pskl.utils.inherit(ns.Transform, pskl.tools.Tool);
+
+ ns.Transform.prototype.apply = function (evt) {
+ var allFrames = evt.shiftKey;
+ var allLayers = evt.ctrlKey;
+ this.applyTool_(evt.altKey, allFrames, allLayers);
+ };
+
+ ns.Transform.prototype.applyTool_ = function (altKey, allFrames, allLayers) {
+ var currentFrameIndex = pskl.app.piskelController.getCurrentFrameIndex();
+ var layers = allLayers ? pskl.app.piskelController.getLayers(): [pskl.app.piskelController.getCurrentLayer()];
+ layers.forEach(function (layer) {
+ var frames = allFrames ? layer.getFrames(): [layer.getFrameAt(currentFrameIndex)];
+ frames.forEach(function (frame) {
+ this.applyToolOnFrame_(frame, altKey);
+ }.bind(this));
+ }.bind(this));
+ $.publish(Events.PISKEL_RESET);
+ $.publish(Events.PISKEL_SAVE_STATE, {
+ type : pskl.service.HistoryService.SNAPSHOT
+ });
+ };
+
+})();
\ No newline at end of file
diff --git a/src/js/utils/Array.js b/src/js/utils/Array.js
new file mode 100644
index 00000000..efe69c1b
--- /dev/null
+++ b/src/js/utils/Array.js
@@ -0,0 +1,16 @@
+(function () {
+ var ns = $.namespace('pskl.utils');
+
+ ns.Array = {
+ find : function (array, filterFn) {
+ var match = null;
+ array = Array.isArray(array) ? array : [];
+ var filtered = array.filter(filterFn);
+ if (filtered.length) {
+ match = filtered[0];
+ }
+ return match;
+ }
+ };
+
+})();
\ No newline at end of file
diff --git a/src/js/utils/FrameTransform.js b/src/js/utils/FrameTransform.js
new file mode 100644
index 00000000..864cb7ed
--- /dev/null
+++ b/src/js/utils/FrameTransform.js
@@ -0,0 +1,65 @@
+(function () {
+ var ns = $.namespace('pskl.utils');
+
+ ns.FrameTransform = {
+ VERTICAL : 'vertical',
+ HORIZONTAL : 'HORIZONTAL',
+ flip : function (frame, axis) {
+ var clone = frame.clone();
+ var w = frame.getWidth();
+ var h = frame.getHeight();
+
+ clone.forEachPixel(function (color, x, y) {
+ if (axis === ns.FrameTransform.VERTICAL) {
+ x = w-x-1;
+ } else if (axis === ns.FrameTransform.HORIZONTAL) {
+ y = h-y-1;
+ }
+ frame.pixels[x][y] = color;
+ });
+ frame.version++;
+ return frame;
+ },
+
+ CLOCKWISE : 'clockwise',
+ COUNTERCLOCKWISE : 'counterclockwise',
+ rotate : function (frame, direction) {
+ var clone = frame.clone();
+ var w = frame.getWidth();
+ var h = frame.getHeight();
+
+ var max = Math.max(w, h);
+ var xDelta = Math.ceil((max - w)/2);
+ var yDelta = Math.ceil((max - h)/2);
+
+ frame.forEachPixel(function (color, x, y) {
+ var _x = x, _y = y;
+
+ // Convert to square coords
+ x = x + xDelta;
+ y = y + yDelta;
+
+ // Perform the rotation
+ var tmpX = x, tmpY = y;
+ if (direction === ns.FrameTransform.CLOCKWISE) {
+ x = tmpY;
+ y = max - 1 - tmpX;
+ } else if (direction === ns.FrameTransform.COUNTERCLOCKWISE) {
+ y = tmpX;
+ x = max - 1 - tmpY;
+ }
+
+ // Convert the coordinates back to the rectangular grid
+ x = x - xDelta;
+ y = y - yDelta;
+ if (clone.containsPixel(x, y)) {
+ frame.pixels[_x][_y] = clone.getPixel(x, y);
+ } else {
+ frame.pixels[_x][_y] = Constants.TRANSPARENT_COLOR;
+ }
+ });
+ frame.version++;
+ return frame;
+ }
+ };
+})();
\ No newline at end of file
diff --git a/src/js/utils/FrameUtils.js b/src/js/utils/FrameUtils.js
index a2094d43..4fa36252 100644
--- a/src/js/utils/FrameUtils.js
+++ b/src/js/utils/FrameUtils.js
@@ -2,11 +2,17 @@
var ns = $.namespace('pskl.utils');
var colorCache = {};
ns.FrameUtils = {
- toImage : function (frame, zoom, bgColor) {
+ /**
+ * Render a Frame object as an image.
+ * Can optionally scale it (zoom)
+ * @param {Frame} frame
+ * @param {Number} zoom
+ * @return {Image}
+ */
+ toImage : function (frame, zoom) {
zoom = zoom || 1;
- bgColor = bgColor || Constants.TRANSPARENT_COLOR;
var canvasRenderer = new pskl.rendering.CanvasRenderer(frame, zoom);
- canvasRenderer.drawTransparentAs(bgColor);
+ canvasRenderer.drawTransparentAs(Constants.TRANSPARENT_COLOR);
return canvasRenderer.render();
},
@@ -23,9 +29,9 @@
},
mergeFrames_ : function (frameA, frameB) {
- frameB.forEachPixel(function (p, col, row) {
- if (p != Constants.TRANSPARENT_COLOR) {
- frameA.setPixel(col, row, p);
+ frameB.forEachPixel(function (color, col, row) {
+ if (color != Constants.TRANSPARENT_COLOR) {
+ frameA.setPixel(col, row, color);
}
});
},
diff --git a/src/js/utils/TooltipFormatter.js b/src/js/utils/TooltipFormatter.js
new file mode 100644
index 00000000..e70ba7d7
--- /dev/null
+++ b/src/js/utils/TooltipFormatter.js
@@ -0,0 +1,37 @@
+(function () {
+ var ns = $.namespace('pskl.utils');
+
+ ns.TooltipFormatter = {};
+
+
+ ns.TooltipFormatter.formatForTool = function(shortcut, descriptors, helpText) {
+ var tpl = pskl.utils.Template.get('drawingTool-tooltipContainer-template');
+ return pskl.utils.Template.replace(tpl, {
+ helptext : helpText,
+ shortcut : shortcut,
+ descriptors : this.formatToolDescriptors_(descriptors)
+ });
+ };
+
+
+ ns.TooltipFormatter.formatToolDescriptors_ = function(descriptors) {
+ descriptors = descriptors || [];
+ return descriptors.reduce(function (p, descriptor) {
+ return p += this.formatToolDescriptor_(descriptor);
+ }.bind(this), '');
+ };
+
+ ns.TooltipFormatter.formatToolDescriptor_ = function(descriptor) {
+ var tpl;
+ if (descriptor.key) {
+ tpl = pskl.utils.Template.get('drawingTool-tooltipDescriptor-template');
+ descriptor.key = descriptor.key.toUpperCase();
+ if (pskl.utils.UserAgent.isMac) {
+ descriptor.key = descriptor.key.replace('CTRL', 'CMD');
+ }
+ } else {
+ tpl = pskl.utils.Template.get('drawingTool-simpleTooltipDescriptor-template');
+ }
+ return pskl.utils.Template.replace(tpl, descriptor);
+ };
+})();
\ No newline at end of file
diff --git a/src/js/controller/widgets/ColorsList.js b/src/js/widgets/ColorsList.js
similarity index 96%
rename from src/js/controller/widgets/ColorsList.js
rename to src/js/widgets/ColorsList.js
index 1c137b5c..b4e7b795 100644
--- a/src/js/controller/widgets/ColorsList.js
+++ b/src/js/widgets/ColorsList.js
@@ -1,5 +1,5 @@
(function () {
- var ns = $.namespace('pskl.controller.widgets');
+ var ns = $.namespace('pskl.widgets');
var DEFAULT_COLOR = '#000000';
@@ -20,7 +20,7 @@
this.colorsList.addEventListener('click', this.onColorContainerClick_.bind(this));
var colorPickerContainer = container.querySelector('.color-picker-container');
- this.hslRgbColorPicker = new pskl.controller.widgets.HslRgbColorPicker(colorPickerContainer, this.onColorUpdated_.bind(this));
+ this.hslRgbColorPicker = new pskl.widgets.HslRgbColorPicker(colorPickerContainer, this.onColorUpdated_.bind(this));
this.hslRgbColorPicker.init();
};
diff --git a/src/js/controller/widgets/HslRgbColorPicker.js b/src/js/widgets/HslRgbColorPicker.js
similarity index 99%
rename from src/js/controller/widgets/HslRgbColorPicker.js
rename to src/js/widgets/HslRgbColorPicker.js
index f7e539d5..744eb7fe 100644
--- a/src/js/controller/widgets/HslRgbColorPicker.js
+++ b/src/js/widgets/HslRgbColorPicker.js
@@ -1,5 +1,5 @@
(function () {
- var ns = $.namespace('pskl.controller.widgets');
+ var ns = $.namespace('pskl.widgets');
ns.HslRgbColorPicker = function (container, colorUpdatedCallback) {
this.container = container;
diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js
index cbfedc78..e1c2e2bb 100644
--- a/src/piskel-script-list.js
+++ b/src/piskel-script-list.js
@@ -11,6 +11,7 @@
// Libraries
"js/utils/core.js",
"js/utils/UserAgent.js",
+ "js/utils/Array.js",
"js/utils/Base64.js",
"js/utils/BlobUtils.js",
"js/utils/CanvasUtils.js",
@@ -18,12 +19,14 @@
"js/utils/Dom.js",
"js/utils/Math.js",
"js/utils/FileUtils.js",
+ "js/utils/FrameTransform.js",
"js/utils/FrameUtils.js",
"js/utils/LayerUtils.js",
"js/utils/ImageResizer.js",
"js/utils/PixelUtils.js",
"js/utils/PiskelFileUtils.js",
"js/utils/Template.js",
+ "js/utils/TooltipFormatter.js",
"js/utils/UserSettings.js",
"js/utils/Uuid.js",
"js/utils/WorkerUtils.js",
@@ -88,6 +91,7 @@
"js/controller/PalettesListController.js",
"js/controller/ProgressBarController.js",
"js/controller/NotificationController.js",
+ "js/controller/TransformationsController.js",
"js/controller/CanvasBackgroundController.js",
// Settings sub-controllers
@@ -112,9 +116,9 @@
// Dialogs controller
"js/controller/dialogs/DialogsController.js",
- // Widget controller
- "js/controller/widgets/ColorsList.js",
- "js/controller/widgets/HslRgbColorPicker.js",
+ // Widgets
+ "js/widgets/ColorsList.js",
+ "js/widgets/HslRgbColorPicker.js",
// Services
"js/service/LocalStorageService.js",
@@ -142,22 +146,28 @@
"js/service/FileDropperService.js",
// Tools
- "js/drawingtools/BaseTool.js",
- "js/drawingtools/ShapeTool.js",
- "js/drawingtools/SimplePen.js",
- "js/drawingtools/Lighten.js",
- "js/drawingtools/VerticalMirrorPen.js",
- "js/drawingtools/Eraser.js",
- "js/drawingtools/Stroke.js",
- "js/drawingtools/PaintBucket.js",
- "js/drawingtools/Rectangle.js",
- "js/drawingtools/Circle.js",
- "js/drawingtools/Move.js",
- "js/drawingtools/selectiontools/BaseSelect.js",
- "js/drawingtools/selectiontools/RectangleSelect.js",
- "js/drawingtools/selectiontools/ShapeSelect.js",
- "js/drawingtools/ColorPicker.js",
- "js/drawingtools/ColorSwap.js",
+ "js/tools/Tool.js",
+ "js/tools/IconMarkupRenderer.js",
+ "js/tools/drawing/BaseTool.js",
+ "js/tools/drawing/ShapeTool.js",
+ "js/tools/drawing/SimplePen.js",
+ "js/tools/drawing/Lighten.js",
+ "js/tools/drawing/VerticalMirrorPen.js",
+ "js/tools/drawing/Eraser.js",
+ "js/tools/drawing/Stroke.js",
+ "js/tools/drawing/PaintBucket.js",
+ "js/tools/drawing/Rectangle.js",
+ "js/tools/drawing/Circle.js",
+ "js/tools/drawing/Move.js",
+ "js/tools/drawing/selection/BaseSelect.js",
+ "js/tools/drawing/selection/RectangleSelect.js",
+ "js/tools/drawing/selection/ShapeSelect.js",
+ "js/tools/drawing/ColorPicker.js",
+ "js/tools/drawing/ColorSwap.js",
+ "js/tools/transform/Transform.js",
+ "js/tools/transform/Clone.js",
+ "js/tools/transform/Flip.js",
+ "js/tools/transform/Rotate.js",
// Devtools
"js/devtools/DrawingTestPlayer.js",
diff --git a/src/piskel-style-list.js b/src/piskel-style-list.js
index fdb8e0c9..70a6a84f 100644
--- a/src/piskel-style-list.js
+++ b/src/piskel-style-list.js
@@ -3,6 +3,7 @@
(typeof exports != "undefined" ? exports : pskl_exports).styles = [
"css/reset.css",
"css/style.css",
+ "css/layout.css",
"css/font-icon.css",
"css/forms.css",
"css/settings.css",
@@ -21,6 +22,7 @@
"css/toolbox-layers-list.css",
"css/toolbox-palettes-list.css",
"css/toolbox-animated-preview.css",
+ "css/transformations.css",
"css/spectrum/spectrum.css",
"css/spectrum/spectrum-overrides.css",
"css/bootstrap/bootstrap.css",
diff --git a/src/templates/drawing-tools.html b/src/templates/drawing-tools.html
index 623e7141..20fb8290 100644
--- a/src/templates/drawing-tools.html
+++ b/src/templates/drawing-tools.html
@@ -34,20 +34,20 @@
-
-
-
-