This means that the calculated font properties of the HTML canvas now
control what font is used. The size is overridden, and for monospaced
text so is the family.
I'd like to be able to also specify the monospaced font, maybe using a
CSS variable, but that looks like being quite a lot of extra complexity.
My experience when testing this was that constructing a valid "font"
string for a canvas context is prone to breakage, but broke in a way
that left the font unchanged, so we always set a simple specification
first before trying to construct one from CSS.
Instead of an align=center HTML attribute, we now centre its contents
using CSS. Also, this element contains all the important contents of
the page, so it seems appropriate to us the HTML5 <main> element for
this.
This is more compact than carefully drawing it on a canvas in
JavaScript. More importantly, the SVG resize handle scales nicely as
the page is zoomed, or on high-DPI screens.
At the moment, the nice SVG resize handle is removed by JavaScript,
but we need to wait a little while for everyone to get the new HTML
cached before changing that.
With word-wrapping disabled, long status-bar texts fall off the
right-hand end rather than wrapping onto an invisible second line.
This is less confusing because it makes the overflow more obvious.
I've also turned on "text-overflow: ellipsis" for extra obviousness.
Finally, this also the need to explicitly set the height of the status
bar, not that that was doing any harm.
They were set to "1px", which isn't a valid value since they're meant to
be integers. Since they weren't valid, they were ignored. This doesn't
seem to have caused any trouble, so they may as well be removed. In any
case, the canvas is invisible until after its size has been set by
JavaScript.
Each menu item has a -0.5px margin so that the borders of adjacent menu
items overlap, but we don't actually want the menu items to protrude
beyond the containing <ul>. Adding 0.5px of padding to the <ul>
achieves that.
Menu items that open dialogue boxes are now marked with ellipses, while
menu items that lead to submenus have pointing triangles.
The triangles are implemented as SVG files embedded in data: URLs in the
CSS, which is kind of silly but also works really well. There are
suitable characters in Unicode, but some of my test systems don't have
fonts containing them, so maybe the SVG is better.
Old browsers (like KaiOS 2.5) don't have :focus-within, but it's pretty
easy to replace the pseudo-class with a real .focus-within class
maintained by JavaScript event handlers. This is made only marginally
fiddlier by the odd fact that "focus" and "blur" events don't bubble.
Once the input focus is in the menu system (for instance by Shift+Tab
from the puzzle), you can move left and right through the menu bar and
up and down within each menu. Enter selects a menu item. The current
menu item is tracked by giving it the input focus.
Older Firefox versions don't support "-moz-appearance: none" on radio
buttons, which seems to mean that the specifies padding for them
doesn't appear. Using a space character instead works fine, so do that
everywhere. This seems to move the text slightly closer to the tick on
browsers that do support "appearance: none", but the result is quite
acceptable.
This also makes the focus outline on the ticks slightly less weird.
This is the wrong attribute, and the correct type="text/css" is
deprecated by MDN. Since it's never worked, the deprecated attribute
presumably isn't needed either.
Now that we're using flex layout, whitespace in the menu isn't scary and
we can use it to make the HTML readable.
Also finally remove the "afterseparator" class that's long obsolete.
You can always use ".separator + *" as a selector instead.
Presets are now radio buttons with labels, and menu items that take
actions are now buttons. The <li> representing each menu item is now a
thin wrapper around another element: a <label> for radio buttons, a
<button> for other buttons, and a <div> for submenu headings. All of
the things that previously applied to the <li> now apply to that inner
element instead.
This means that presets can now use the standard "checked" attribute to
indicate which one is selected, and buttons can be disabled using the
standard "disabled" attribute. It also means that we can query and set
the state of all the presets at once through their RadioNodeList.
I think this should also make the menus more accessible, and make it
easier to make them keyboard-controllable.
The CSS "px" unit isn't always a device pixel. On devices with
high-DPI displays, there can often be multiple device pixels to a CSS
px, while in particularly low-resolution displays (like feature
phones), the user might zoom out to get several CSS px to a device
pixel. And even on desktop browsers, text zooming controls can change
the ratio.
To make Puzzles' rendering look good on an arbitrary device pixel
ratio, we really want the pixels of the canvas to be device pixels,
not CSS px, so that the canvas doesn't have to be scaled by the
browser for display. To correct this, we now control the CSS size of
the puzzle canvas, via its containing <div>, to be the canvas size
divided by the device pixel ratio.
There is a significant gap, which is that this doesn't yet track
changes to the device pixel ratio. This is slightly complicated, so
I'll put it off to the next commit.
Some elements (generally those created by JavaScript) had their style
parameters set directly by JavaScript. Putting styles in CSS generally
makes them easier to understand (and fiddle with), so I've done that.
The only styles left in JavaScript are those that are calculated by
JavaScript (like the status-bar size) and the random-seed permalink
visibility because I wasn't quite sure how to handle it.
In their normal state, most of the top-level menu items are a verb and
an object, like "Undo move". This is admirably clear, but on a small
screen the menu can take a lot of space. In each case the verb alone
is sufficient to know what the button does, so use a media query to
suppress the noun is the viewport is very narrow. "Very narrow" here
is roughly where the menus would overflow onto four lines in my
browser.
In the old design, when they wrapped onto multiple lines, various bad
things happened. The lines overlapped one another, the lines got
broken within buttons but not between buttons, and if they had got
broken between buttons the left button on each line would have lacked
a left border.
I've made two major changes to fix this. First, I've switched from
flow layout to flex layout. This has much better default behaviour,
breaking lines in the right places, not overlapping lines, and even
arranging line-wrapping within a button when the viewport gets really
narrow.
Second, I've given each button a border on all four sides and then
used negative margins to overlap them. This required changing the
borders from transparent black to opaque grey to make them display
correctly when overlapping.
The result is not quite identical to the old version on a wide
viewport, but I think it's as close as I can get while keeping the new
CSS pleasant.
Ideally, the separator would vanish when it was adjacent to a line
break, but I've not worked out how to do that yet.
This is similar in concept to Minesweeper, in that each clue tells you
the number of things (in this case, just 'black squares') in the
surrounding 3x3 grid section.
But unlike Minesweeper, there's no separation between squares that can
contain clues, and squares that can contain the things you're looking
for - a clue square may or may not itself be coloured black, and if
so, its clue counts itself.
So there's also no hidden information: the clues can all be shown up
front, and the difficulty arises from the game generator choosing
which squares to provide clues for at all.
Contributed by a new author, Didi Kohen. Currently only has one
difficulty level, but harder ones would be possible to add later.
Using a stunt webserver which artificially introduces a 3s delay just
before the last line of the HTML output, I have reproduced a
uwer-reported loading/startup race bug:
Previously the wasm loading was started by the <script> element,
synchronously. If the wasm loading is fast, and finishes before the
HTML loading, the onRuntimeInitialized event may occur before
initPuzzles. But initPuzzles sets up the event handler.
Fix this bug, and introduce a new comment containing an argument for
the correctness of the new approach.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Now I've had a chance to collect some more data from browser users,
it's got a list of browsers in which we've definitely seen the WASM
puzzle working (so that if _your_ instance of one of those browsers
fails, you should check it for problems at your end, like
configuration details or overzealous web filtering software), and some
thoughts about what to report.
The result is a lot longer than the previous error message, so I've
put the bulk of it in a <details>. That way it won't look so silly
when it initially flashes up while things are loading.
The old one was totally out of date (it mentioned typed arrays and a
specific set of browser versions against which the previous Emscripten
build had been tested).
Also, a couple of users in the last day or two have reported
mysterious failures of the WASM puzzles, which I haven't been able to
reproduce in the same version of the same browser. So something odd is
going on, and this seems like a good place to put suggestions about
what diagnostic information to send.
A user mailed me today having found it less than clear from the docs
that Galaxies will only accept a solution if the set of filled-in grid
edges consists of _exactly_ the ones that separate two distinct
regions, rather than consisting of _at least_ those and perhaps others
which neither break rotational symmetry or disconnect any region.
This should make it less annoying for me to do local testing of the JS
output of a build, before I push a change. There's a new
build.out/jstest directory containing .html files suitable for loading
in a local browser, which refer to the JS files via an appropriate
relative path to the existing build.out/js directory.
Commit ef39e6e17 made a goof in which the 'New game' button had no
border on the left and an accidental extra one on the right, which I'm
really not sure how I failed to spot when I tested it yesterday.
The only user to send me a comment today on the new layout said that
that menu item in particular is annoying to have hidden behind more
clicks, so by a vote of one to nothing, it's back out in the open.
This is done by showing a dialog containing an <input type="file">
through which the user can 'upload' a save file - though, of course,
the 'upload' doesn't go to any HTTP server, but only into the mind of
the Javascript running in the same browser.
It would be even nicer to support drag-and-drop as an alternative UI
for getting the save file into the browser, but that isn't critical to
getting the first version of this feature out of the door.
This is done by getting midend_serialise to produce the complete
saved-game file as an in-memory string buffer, and then encoding that
into a data: URI which we provide to the user as a hyperlink in a
dialog box. The hyperlink has the 'download' attribute, which means
clicking on it should automatically offer to save the file, and also
lets me specify a not-too-silly default file name.
I'm about to introduce a few more options, and the button bar is
already a bit wide, so I think I should shrink it horizontally before
putting more stuff on it. So I've organised the options into something
more like the Game and Type submenus that the desktop versions use.
However, I haven't gone quite all the way, on the basis that the web
versions will be at least slightly playable on devices without much
keyboard, which makes it worth keeping the in-play actions (Undo,
Redo, and to a lesser extent Restart and Solve) accessible as
top-level buttons in a single click each.
As part of this change, I've also separated the menu bar into a
drop-down menus section and a top-level buttons section with a gap
between them, and put a signalling "..." on the end of the titles in
the former section.
This change also removes the class="left" on the game-type menu and
its submenus, which were previously there to deal with that menu being
all the way over on the far right of the menu bar. But the CSS for
those classes is still there in jspage.pl, and should still work if I
need it again in future.
The previous control buttons and dropdowns based on form elements were
always a bit ugly: partly in a purely visual sense, and partly because
of the nasty bodge I had to do with splitting the usual 'Custom' game
type menu item into two (to get round the fact that if an element of a
<select> is already selected, browsers won't send an event when it's
re-selected). Also, I'm about to want to introduce hierarchical
submenus in the Type menu, and <select> doesn't support that at all.
So here's a replacement system which does everything by CSS
properties, including the popping-up of menus when the mouse moves
over their parent menu item. (Thanks to the Internet in general for
showing me how that trick is done.)
Based on a web game I saw a few years ago, and dashed off this weekend
after I thought of a way to write a good (though not quite optimal)
heuristic solver, here's a random little thing not quite in the same
line as the most usual kind of Puzzles fare: instead of making you
scratch your head to find any move to make at all, it's easy to find
solutions in principle, and the challenge comes from having to do so
within a move limit.
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]