chiark / gitweb /
Add a draggable resize handle to the JS puzzles.
authorSimon Tatham <anakin@pobox.com>
Sun, 7 Apr 2013 10:24:37 +0000 (10:24 +0000)
committerSimon Tatham <anakin@pobox.com>
Sun, 7 Apr 2013 10:24:37 +0000 (10:24 +0000)
Rather than design an ersatz 'window frame' surrounding the puzzle
canvas, I've simply overlaid the resize handle on the corner of the
puzzle itself (canvas or status bar, depending on whether the latter
exists), trusting that all games in my collection provide a reasonable
border within their drawing area. (OS X already does this with its
resize handle, so it's not as if there's no precedent.)

Unlike the desktop versions, I control the resize behaviour completely
in this environment, so I can constrain the canvas to only ever be
sensible sizes with no dead space round the edges (and, in particular,
preserve the aspect ratio).

Right-clicking the resize handle will restore the puzzle's default
tile size. I had intended to implement a maximise-to-browser-window
button too, but was annoyingly foiled by scrollbars - if you maximise
to the current window width, and as a result the text below the puzzle
scrolls off the bottom, then a vertical scrollbar appears and eats
into the width you just maximised to. Gah.

[originally from svn r9822]

emcc.c
emcclib.js
emccpre.js
emccx.json
html/jspage.pl

diff --git a/emcc.c b/emcc.c
index baf12d32e78b01a05a1e8b2ee37c5dc4f8d67b1f..dda571645645f2623c0d7f321706d99074051dfd 100644 (file)
--- a/emcc.c
+++ b/emcc.c
  *    by using the DOM File API to ask the user to select a file and
  *    permit us to see its contents.
  *
- *  - it ought to be possible to make the puzzle canvases resizable,
- *    by superimposing some kind of draggable resize handle. Also I
- *    quite like the idea of having a few buttons for standard sizes:
- *    reset to default size, maximise to the browser window dimensions
- *    (if we can find those out), and perhaps even go full-screen.
- *
  *  - I should think about whether these webified puzzles can support
  *    touchscreen-based tablet browsers (assuming there are any that
  *    can cope with the reasonably modern JS and run it fast enough to
@@ -194,10 +188,12 @@ void timer_callback(double tplus)
 }
 
 /* ----------------------------------------------------------------------
- * Helper function to resize the canvas, and variables to remember its
- * size for other functions (e.g. trimming blitter rectangles).
+ * Helper functions to resize the canvas, and variables to remember
+ * its size for other functions (e.g. trimming blitter rectangles).
  */
 static int canvas_w, canvas_h;
+
+/* Called when we resize as a result of changing puzzle settings */
 static void resize(void)
 {
     int w, h;
@@ -208,6 +204,26 @@ static void resize(void)
     canvas_h = h;
 }
 
+/* Called from JS when the user uses the resize handle */
+void resize_puzzle(int w, int h)
+{
+    midend_size(me, &w, &h, TRUE);
+    if (canvas_w != w || canvas_h != h) { 
+        js_canvas_set_size(w, h);
+        canvas_w = w;
+        canvas_h = h;
+        midend_force_redraw(me);
+    }
+}
+
+/* Called from JS when the user uses the restore button */
+void restore_puzzle_size(int w, int h)
+{
+    midend_reset_tilesize(me);
+    resize();
+    midend_force_redraw(me);
+}
+
 /*
  * HTML doesn't give us a default frontend colour of its own, so we
  * just make up a lightish grey ourselves.
index e0c4bea8251128a9f014118975e3f5fbbe199ad4..7d001c3f852c4de5bbf0c5c098aab192885701b4 100644 (file)
@@ -567,6 +567,7 @@ mergeInto(LibraryManager.library, {
             statusbar.style.width = (w - 4) + "px";
             document.getElementById("statusbarholder").style.width = w + "px";
         }
+        resizable_div.style.width = w + "px";
 
         onscreen_canvas.height = h;
         offscreen_canvas.height = h;
index 5c1ad4c7dade55dbeaab2487a5a3ce582ccda252..5231d04e6dbd857a54b4555775b810d3f92d0234 100644 (file)
@@ -103,6 +103,10 @@ var permalink_seed, permalink_desc;
 // The undo and redo buttons. Used by js_enable_undo_redo().
 var undo_button, redo_button;
 
+// A div element enclosing both the puzzle and its status bar, used
+// for positioning the resize handle.
+var resizable_div;
+
 // Helper function to find the absolute position of a given DOM
 // element on a page, by iterating upwards through the DOM finding
 // each element's offset from its parent, and thus calculating the
@@ -268,6 +272,83 @@ function initPuzzle() {
     // Default to giving keyboard focus to the puzzle.
     onscreen_canvas.focus();
 
+    // Create the resize handle.
+    var resize_handle = document.createElement("canvas");
+    resize_handle.width = 10;
+    resize_handle.height = 10;
+    {
+        var ctx = resize_handle.getContext("2d");
+        ctx.beginPath();
+        for (var i = 1; i <= 7; i += 3) {
+            ctx.moveTo(8.5, i + 0.5);
+            ctx.lineTo(i + 0.5, 8.5);
+        }
+        ctx.lineWidth = '1px';
+        ctx.lineCap = 'round';
+        ctx.lineJoin = 'round';
+        ctx.strokeStyle = '#000000';
+        ctx.stroke();
+    }
+    resizable_div = document.getElementById("resizable");
+    resizable_div.appendChild(resize_handle);
+    resize_handle.style.position = 'absolute';
+    resize_handle.style.zIndex = 98;
+    resize_handle.style.bottom = "0";
+    resize_handle.style.right = "0";
+    resize_handle.style.cursor = "se-resize";
+    resize_handle.title = "Drag to resize the puzzle. Right-click to restore the default size.";
+    var resize_xbase = null, resize_ybase = null, restore_pending = false;
+    var resize_xoffset = null, resize_yoffset = null;
+    var resize_puzzle = Module.cwrap('resize_puzzle',
+                                     'void', ['number', 'number']);
+    var restore_puzzle_size = Module.cwrap('restore_puzzle_size', 'void', []);
+    resize_handle.oncontextmenu = function(event) { return false; }
+    resize_handle.onmousedown = function(event) {
+        if (event.button == 0) {
+            var xy = element_coords(onscreen_canvas);
+            resize_xbase = xy.x + onscreen_canvas.width / 2;
+            resize_ybase = xy.y;
+            resize_xoffset = xy.x + onscreen_canvas.width - event.pageX;
+            resize_yoffset = xy.y + onscreen_canvas.height - event.pageY;
+        } else {
+            restore_pending = true;
+        }
+        resize_handle.setCapture(true);
+        event.preventDefault();
+    };
+    window.addEventListener("mousemove", function(event) {
+        if (resize_xbase !== null && resize_ybase !== null) {
+            resize_puzzle((event.pageX + resize_xoffset - resize_xbase) * 2,
+                          (event.pageY + resize_yoffset - resize_ybase));
+            event.preventDefault();
+            // Chrome insists on selecting text during a resize drag
+            // no matter what I do
+            if (window.getSelection)
+                window.getSelection().removeAllRanges();
+            else
+                document.selection.empty();        }
+    });
+    window.addEventListener("mouseup", function(event) {
+        if (resize_xbase !== null && resize_ybase !== null) {
+            resize_xbase = null;
+            resize_ybase = null;
+            onscreen_canvas.focus(); // return focus to the puzzle
+        } else if (restore_pending) {
+            // If you have the puzzle at larger than normal size and
+            // then right-click to restore, I haven't found any way to
+            // stop Chrome and IE popping up a context menu on the
+            // revealed piece of document when you release the button
+            // except by putting the actual restore into a setTimeout.
+            // Gah.
+            setTimeout(function() {
+                restore_pending = false;
+                restore_puzzle_size();
+                onscreen_canvas.focus();
+            }, 20);
+        }
+        event.preventDefault();
+    });
+
     // Run the C setup function, passing argv[1] as the fragment
     // identifier (so that permalinks of the form puzzle.html#game-id
     // can launch the specified id).
index 31d723476443166f4e73a52ae7459312c7ca9cad..e03f7e25c7564fcbe670dab687b31fcda3dd2f1b 100644 (file)
@@ -21,6 +21,9 @@
     // Callbacks to return values from dialog boxes
     '_dlg_return_sval',
     '_dlg_return_ival',
+    // Callbacks when the resizing controls are used
+    '_resize_puzzle',
+    '_restore_puzzle_size',
     // Main program, run at initialisation time
     '_main'
 ]
index d6e41677515433b566c3d7c058cb86dd4c09f8e4..19868bd9483b8a04ab89b5982ef70856a2760b24 100755 (executable)
@@ -84,10 +84,12 @@ ${unfinishedpara}
   <select id="gametype"></select>
 </p>
 <div align=center>
+  <div id="resizable" style="position:relative; left:0; top:0">
   <canvas style="display: block" id="puzzlecanvas" width="1px" height="1px" tabindex="1">
   </canvas>
   <div id="statusbarholder" style="display: block">
   </div>
+  </div>
   <p>
     Link to this puzzle:
     <a id="permalink-desc">by game ID</a>