diff --git a/example/index-mobile.html b/example/index-mobile.html
new file mode 100644
index 0000000..8abfa5c
--- /dev/null
+++ b/example/index-mobile.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ChickenPaint testbed
+
+
+
+
+
+
+
+
+ ChickenPaint testbed
+
+
+
+
\ No newline at end of file
diff --git a/example/index-mobile.js b/example/index-mobile.js
new file mode 100644
index 0000000..f2d80d8
--- /dev/null
+++ b/example/index-mobile.js
@@ -0,0 +1,14 @@
+document.addEventListener("DOMContentLoaded", function() {
+ new ChickenPaint({
+ uiElem: document.getElementById("chickenpaint-parent"),
+ //loadImageUrl: "./uploaded.png",
+ //loadChibiFileUrl: "./uploaded.chi",
+ saveUrl: "save.php",
+ postUrl: "posting.php",
+ exitUrl: "forum.php",
+ allowDownload: true,
+ resourcesRoot: "../resources/",
+ disableBootstrapAPI: true,
+ fullScreenMode: "force"
+ });
+});
diff --git a/example/index.js b/example/index.js
index a80be40..cce26da 100644
--- a/example/index.js
+++ b/example/index.js
@@ -8,6 +8,7 @@ document.addEventListener("DOMContentLoaded", function() {
exitUrl: "forum.php",
allowDownload: true,
resourcesRoot: "../resources/",
- disableBootstrapAPI: true
+ disableBootstrapAPI: true,
+ fullScreenMode: "auto"
});
});
diff --git a/js/ChickenPaint.js b/js/ChickenPaint.js
index 5480e4d..7327937 100644
--- a/js/ChickenPaint.js
+++ b/js/ChickenPaint.js
@@ -89,6 +89,10 @@ function checkBrowserSupport() {
return true;
}
+function isSmallScreen() {
+ return $(window).width() < 400 || $(window).height() < 768;
+}
+
function createDrawingTools() {
let
tools = new Array(ChickenPaint.T_MAX);
@@ -268,40 +272,45 @@ function createDrawingTools() {
/**
* @typedef {Object} ChickenPaintOptions
*
- * @property {Element} uiElem - DOM element to insert ChickenPaint into (required)
+ * @property {Element} uiElem - DOM element to insert ChickenPaint into
*
- * @property {Function} onLoaded - Callback to call when artwork loading completes
+ * @property {Function} [onLoaded] - Callback to call when artwork loading completes
*
- * @property {int} canvasWidth - Width in pixels to use when creating blank canvases (defaults to 800)
- * @property {int} canvasHeight - Height in pixels to use when creating blank canvases (defaults to 600)
- * @property {int} rotation - Integer from [0..3], number of 90 degree right rotations that should be applied to
+ * @property {int} [canvasWidth] - Width in pixels to use when creating blank canvases (defaults to 800)
+ * @property {int} [canvasHeight] - Height in pixels to use when creating blank canvases (defaults to 600)
+ * @property {int} [rotation] - Integer from [0..3], number of 90 degree right rotations that should be applied to
* the canvas after loading
*
- * @property {string} saveUrl - URL to POST the drawing to to save it
- * @property {string} postUrl - URL to navigate to after saving is successful and the user chooses to see/publish
+ * @property {string} [saveUrl] - URL to POST the drawing to to save it
+ * @property {string} [postUrl] - URL to navigate to after saving is successful and the user chooses to see/publish
* their finished product
- * @property {string} exitUrl - URL to navigate to after saving is successful and the user chooses to exit (optional)
- * @property {string} testUrl - URL that ChickenPaint can simulate a drawing upload to to test the user's
+ * @property {string} [exitUrl] - URL to navigate to after saving is successful and the user chooses to exit (optional)
+ * @property {string} [testUrl] - URL that ChickenPaint can simulate a drawing upload to to test the user's
* permissions/connection (optional)
*
- * @property {string} loadImageUrl - URL of PNG/JPEG image to load for editing (optional)
- * @property {string} loadChibiFileUrl - URL of .chi file to load for editing (optional). Used in preference to loadImage.
- * @property {string} loadSwatchesUrl - URL of an .aco palette to load (optional)
- * @property {CPArtwork} artwork - Artwork to load into ChickenPaint (if you've already created one)
+ * @property {string} [loadImageUrl] - URL of PNG/JPEG image to load for editing (optional)
+ * @property {string} [loadChibiFileUrl] - URL of .chi file to load for editing (optional). Used in preference to loadImage.
+ * @property {string} [loadSwatchesUrl] - URL of an .aco palette to load (optional)
+ * @property {CPArtwork} [artwork] - Artwork to load into ChickenPaint (if you've already created one)
*
- * @property {boolean} allowMultipleSends - Allow the drawing to be sent to the server multiple times (saving does not
+ * @property {boolean} [allowMultipleSends] - Allow the drawing to be sent to the server multiple times (saving does not
* immediately end drawing session).
- * @property {boolean} allowDownload - Allow the drawing to be saved to the user's computer
- * @property {boolean} allowFullScreen - Allow the drawing tool to enter "full screen" mode, where the rest of the page
- * contents will be hidden
+ * @property {boolean} [allowDownload] - Allow the drawing to be saved to the user's computer
*
- * @property {boolean} disableBootstrapAPI - Disable Bootstrap's data API on the root of the document. This speeds up
+ * @property {"allow"|"auto"|"force"|"disable"} [fullScreenMode] - Control the behaviour of the full screen option:
+ * allow - Don't automatically enter full screen mode, but allow it to be
+ * chosen manually (default)
+ * auto - Automatically enter full screen mode on startup on small screens
+ * force - Enter full screen mode at startup and do not provide option to leave
+ * disable - Don't allow full screen mode at all
+ *
+ * @property {boolean} [disableBootstrapAPI] - Disable Bootstrap's data API on the root of the document. This speeds up
* things considerably.
*
* @property {string} resourcesRoot - URL to the directory that contains the gfx/css etc directories (relative to the
* page that ChickenPaint is loaded on)
*
- * @property {string} language - Provide an explicit ISO language code here (e.g. "ja_JP") to override the guessed browser language
+ * @property {string} [language] - Provide an explicit ISO language code here (e.g. "ja_JP") to override the guessed browser language
* Unsupported languages will fall back to English.
* Currently only "en" and "ja" are available.
*/
@@ -349,7 +358,8 @@ export default function ChickenPaint(options) {
preTransformMode = curMode,
curGradient = [0xFF000000, 0xFFFFFFFF],
- fullScreenMode = false,
+ smallScreenMode = false,
+ isFullScreen = false,
tools = createDrawingTools(),
@@ -360,17 +370,10 @@ export default function ChickenPaint(options) {
CPFullScreen: {
action: function () {
- fullScreenMode = !fullScreenMode;
-
- $("body").toggleClass("chickenpaint-full-screen", fullScreenMode);
- $(uiElem).toggleClass("chickenpaint-full-screen", fullScreenMode);
-
- setTimeout(function () {
- mainGUI.setFullScreenMode(fullScreenMode);
- }, 200);
+ that.setFullScreen(!isFullScreen);
},
isSupported: function() {
- return options.allowFullScreen !== false;
+ return !(options.fullScreenMode === "disable" || options.fullScreenMode === "force");
},
modifies: {gui: true}
},
@@ -1231,7 +1234,31 @@ export default function ChickenPaint(options) {
// callCPEventListeners(); TODO
};
+
+ this.setSmallScreenMode = function(small) {
+ if (smallScreenMode !== small) {
+ smallScreenMode = small;
+
+ $(uiElem).toggleClass("chickenpaint-small-screen", smallScreenMode);
+ that.emitEvent("smallScreen", [smallScreenMode]);
+ }
+ };
+
+ this.getSmallScreenMode = function() {
+ return smallScreenMode;
+ };
+
+ this.setFullScreen = function(newVal) {
+ if (isFullScreen !== newVal) {
+ isFullScreen = newVal;
+
+ $("body").toggleClass("chickenpaint-full-screen", isFullScreen);
+ $(uiElem).toggleClass("chickenpaint-full-screen", isFullScreen);
+ that.emitEvent("fullScreen", [isFullScreen]);
+ }
+ };
+
function installUnsavedWarning() {
if (isEventSupported("onbeforeunload")) {
window.addEventListener("beforeunload", function(e) {
@@ -1256,11 +1283,14 @@ export default function ChickenPaint(options) {
if (!uiElem) {
return;
}
-
+
that.artwork.on("editModeChanged", onEditModeChanged);
mainGUI = new CPMainGUI(that, uiElem);
-
+
+ that.emitEvent("fullScreen", [isFullScreen]);
+ that.emitEvent("smallScreen", [smallScreenMode]);
+
setTool(ChickenPaint.T_PEN);
mainGUI.arrangePalettes();
@@ -1296,6 +1326,17 @@ export default function ChickenPaint(options) {
if (options.disableBootstrapAPI) {
$(document).off('.data-api');
}
+
+ this.setSmallScreenMode(isSmallScreen());
+
+ switch (options.fullScreenMode) {
+ case "force":
+ this.setFullScreen(true);
+ break;
+ case "auto":
+ this.setFullScreen(smallScreenMode);
+ break;
+ }
if (options.loadImageUrl || options.loadChibiFileUrl) {
let
diff --git a/js/gui/CPMainGUI.js b/js/gui/CPMainGUI.js
index b5a9fd5..8548095 100644
--- a/js/gui/CPMainGUI.js
+++ b/js/gui/CPMainGUI.js
@@ -78,10 +78,12 @@ export default function CPMainGUI(controller, uiElem) {
};
this.setFullScreenMode = function(value) {
- fullScreenMode = value;
+ if (fullScreenMode !== value) {
+ fullScreenMode = value;
- that.resize();
- that.arrangePalettes();
+ that.resize();
+ that.arrangePalettes();
+ }
};
this.resize = function() {
diff --git a/js/gui/CPPalette.js b/js/gui/CPPalette.js
index dd1e7f9..16baa95 100644
--- a/js/gui/CPPalette.js
+++ b/js/gui/CPPalette.js
@@ -24,7 +24,15 @@ import $ from "jquery";
import EventEmitter from "wolfy87-eventemitter";
import {_} from "../languages/lang";
-const isInLowResolutionMode = window.innerHeight < 820 || window.innerWidth < 400;
+const
+ DRAG_START_THRESHOLD = 5;
+
+function distanceGreaterThan(a, b, threshold) {
+ let
+ dist = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
+
+ return dist > threshold * threshold;
+}
export default function CPPalette(cpController, className, title, resizeVert, resizeHorz) {
this.title = _(title);
@@ -41,6 +49,7 @@ export default function CPPalette(cpController, className, title, resizeVert, re
vertHandle = null,
horzHandle = null,
+ dragStartPos,
dragAction,
dragOffset,
@@ -88,15 +97,29 @@ export default function CPPalette(cpController, className, title, resizeVert, re
this.setHeight(height);
};
- this.toggleBodyElementVisibility = function() {
- if (isInLowResolutionMode) {
- $(containerElement).toggleClass("collapsed");
- }
+ /**
+ * @param {boolean} [collapse]
+ */
+ this.toggleCollapse = function(collapse) {
+ $(containerElement).toggleClass("collapsed", collapse);
};
function paletteHeaderPointerMove(e) {
- if (e.buttons != 0 && dragAction == "move") {
- that.setLocation(e.pageX - dragOffset.x, e.pageY - dragOffset.y);
+ if (e.buttons != 0) {
+ let
+ newX = e.pageX - dragOffset.x,
+ newY = e.pageY - dragOffset.y
+
+ if (dragAction == "dragStart") {
+ if (distanceGreaterThan({x: newX, y: newY}, dragStartPos, DRAG_START_THRESHOLD)) {
+ // Recognise this as a drag rather than a clink
+ dragAction = "dragging";
+ }
+ }
+
+ if (dragAction == "dragging") {
+ that.setLocation(newX, newY);
+ }
}
}
@@ -108,17 +131,33 @@ export default function CPPalette(cpController, className, title, resizeVert, re
} else {
headElement.setPointerCapture(e.pointerId);
+ dragStartPos = {
+ x: parseInt(containerElement.style.left, 10),
+ y: parseInt(containerElement.style.top, 10),
+ };
dragOffset = {x: e.pageX - $(containerElement).position().left, y: e.pageY - $(containerElement).position().top};
- dragAction = "move";
+
+ if (cpController.getSmallScreenMode()) {
+ // Wait for the cursor to move a certain amount before we classify this as a drag
+ dragAction = "dragStart";
+ } else {
+ dragAction = "dragging";
+ }
}
}
}
function paletteHeaderPointerUp(e) {
- if (dragAction == "move") {
+ if (dragAction === "dragging" || dragAction === "dragStart") {
headElement.releasePointerCapture(e.pointerId);
+
+ if (dragAction === "dragStart") {
+ // We clicked the header. Cancel the drag and toggle the palette instead
+ that.setLocation(dragStartPos.x, dragStartPos.y);
+ that.toggleCollapse();
+ }
+
dragAction = false;
- that.toggleBodyElementVisibility();
}
}
@@ -197,7 +236,7 @@ export default function CPPalette(cpController, className, title, resizeVert, re
titleElem.appendChild(document.createTextNode(_(this.title)));
titleContainer.appendChild(titleElem);
- if (!isInLowResolutionMode) titleContainer.appendChild(closeButton);
+ titleContainer.appendChild(closeButton);
headElement.appendChild(titleContainer);
diff --git a/js/gui/CPPaletteManager.js b/js/gui/CPPaletteManager.js
index 4ba20c6..5384880 100644
--- a/js/gui/CPPaletteManager.js
+++ b/js/gui/CPPaletteManager.js
@@ -33,7 +33,7 @@ import CPTexturePalette from "./CPTexturePalette.js";
import CPSwatchesPalette from "./CPSwatchesPalette.js";
export default function CPPaletteManager(cpController) {
- var
+ let
palettes = {
tool: new CPToolPalette(cpController),
misc: new CPMiscPalette(cpController),
@@ -55,7 +55,7 @@ export default function CPPaletteManager(cpController) {
this.palettes = palettes;
function showPalette(palette, show) {
- var
+ let
palElement = palette.getElement();
if (show) {
@@ -70,7 +70,7 @@ export default function CPPaletteManager(cpController) {
}
this.showPaletteByName = function(paletteName, show) {
- var
+ let
palette = palettes[paletteName];
if (palette) {
@@ -85,8 +85,8 @@ export default function CPPaletteManager(cpController) {
hiddenFrames.push(this);
});
} else {
- for (var i = 0; i < hiddenFrames.length; i++) {
- var
+ for (let i = 0; i < hiddenFrames.length; i++) {
+ let
frame = hiddenFrames[i];
that.showPaletteByName(frame.getAttribute("data-paletteName"), true);
@@ -99,12 +99,12 @@ export default function CPPaletteManager(cpController) {
* Pop palettes that are currently outside the visible area back into view.
*/
this.constrainPalettes = function() {
- var
+ let
windowWidth = $(parentElem).parents(".chickenpaint-main-section").width(),
windowHeight = $(parentElem).parents(".chickenpaint-main-section").height();
- for (var i in palettes) {
- var palette = palettes[i];
+ for (let i in palettes) {
+ let palette = palettes[i];
/* Move palettes that are more than half out of the frame back into it */
if (palette.getX() + palette.getWidth() / 2 > windowWidth) {
@@ -120,7 +120,7 @@ export default function CPPaletteManager(cpController) {
//palettes.swatches.moveToFront();
//Special handling for the swatches palette being under the brush palette:
- var
+ let
widthToSpare = windowWidth - palettes.tool.getWidth() - palettes.misc.getWidth() - palettes.stroke.getWidth() - palettes.color.getWidth() - palettes.brush.getWidth() - 15 > 0;
if (palettes.swatches.getX() + palettes.swatches.getWidth() == palettes.brush.getX() + palettes.brush.getWidth() &&
@@ -138,7 +138,7 @@ export default function CPPaletteManager(cpController) {
* Rearrange the palettes from scratch into a useful arrangement.
*/
this.arrangePalettes = function() {
- var
+ let
windowWidth = $(parentElem).parents(".chickenpaint-main-section").width(),
windowHeight = $(parentElem).parents(".chickenpaint-main-section").height(),
@@ -146,7 +146,7 @@ export default function CPPaletteManager(cpController) {
palettes.brush.setLocation(windowWidth - palettes.brush.getWidth() - 15, 0);
- var
+ let
bottomOfBrush = palettes.brush.getY() + palettes.brush.getHeight(),
layersY = windowHeight - bottomOfBrush > 300 ? bottomOfBrush + 2 : bottomOfBrush;
@@ -169,13 +169,20 @@ export default function CPPaletteManager(cpController) {
palettes.textures.setLocation(palettes.color.getX() + palettes.color.getWidth() + 4, windowHeight - palettes.textures.getHeight());
palettes.color.setLocation(0, Math.max(palettes.tool.getY() + palettes.tool.getHeight(), windowHeight - palettes.color.getHeight()));
-
- for (var i in palettes) {
- var palette = palettes[i];
- palette.toggleBodyElementVisibility();
- }
};
+ cpController.on("smallScreen", function(smallScreenMode) {
+ for (let paletteName in palettes) {
+ let
+ palette = palettes[paletteName];
+
+ if (smallScreenMode) {
+ palette.show
+ }
+ palette.toggleCollapse(smallScreenMode);
+ }
+ });
+
this.getElement = function() {
return parentElem;
};
diff --git a/js/gui/CPStrokePalette.js b/js/gui/CPStrokePalette.js
index 93a1700..c023264 100644
--- a/js/gui/CPStrokePalette.js
+++ b/js/gui/CPStrokePalette.js
@@ -88,7 +88,7 @@ export default function CPStrokePalette(cpController) {
$(this).addClass("selected");
cpController.actionPerformed({action: button.command});
- that.toggleBodyElementVisibility();
+ that.toggleCollapse();
});
body.appendChild(listElem);
diff --git a/js/gui/CPSwatchesPalette.js b/js/gui/CPSwatchesPalette.js
index 3942748..26b0d9f 100644
--- a/js/gui/CPSwatchesPalette.js
+++ b/js/gui/CPSwatchesPalette.js
@@ -223,7 +223,7 @@ export default function CPSwatchesPalette(controller) {
controller.setCurColor(new CPColor(parseInt(swatch.getAttribute("data-color"), 10)));
e.stopPropagation();
e.preventDefault();
- that.toggleBodyElementVisibility();
+ that.toggleCollapse();
}
});
diff --git a/js/gui/CPToolPalette.js b/js/gui/CPToolPalette.js
index ea5afe3..088cfed 100644
--- a/js/gui/CPToolPalette.js
+++ b/js/gui/CPToolPalette.js
@@ -167,7 +167,7 @@ export default function CPToolPalette(cpController) {
button = buttons[parseInt(this.getAttribute("data-buttonIndex"), 10)];
cpController.actionPerformed({action: button.command});
- that.toggleBodyElementVisibility();
+ that.toggleCollapse();
}
}
diff --git a/resources/css/chickenpaint.scss b/resources/css/chickenpaint.scss
index 1c3aacf..f1a38a2 100644
--- a/resources/css/chickenpaint.scss
+++ b/resources/css/chickenpaint.scss
@@ -286,20 +286,6 @@
user-select: none;
}
-.chickenpaint.chickenpaint-full-screen {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- overflow: hidden;
- background-color:white;
- z-index:1000;
-}
-body.chickenpaint-full-screen {
- overflow:hidden;
-}
-
.chickenpaint-tools {
width:80px;
text-align:center;
@@ -990,6 +976,27 @@ body.chickenpaint-full-screen {
.chickenpaint .widget-nav .widget-toggler.selected {
background-color: #ffffc4;
}
+
+/* Full screen mode */
+.chickenpaint.chickenpaint-full-screen {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ overflow: hidden;
+ background-color:white;
+ z-index:1000;
+}
+body.chickenpaint-full-screen {
+ overflow:hidden;
+}
+
+/* Small screen mode (UI behaviour changes for mobiles) */
+.chickenpaint-small-screen .chickenpaint-palette-head .close {
+ display: none;
+}
+
/* More compact styles for small screens */
@media (max-height: 768px), (max-width: 400px) {
.chickenpaint .navbar {
@@ -1021,6 +1028,7 @@ body.chickenpaint-full-screen {
.chickenpaint-palette-head .close {
font-size: 18px;
+ display: none;
}
.chickenpaint select.form-control {