I expect Escape to exit the menu, and SoftRight should do that as well
for KaiOS. Backspace goes up one level through the menus, again because
that's conventional on KaiOS and not too confusing elsewhere.
In the bubbling phase it managed to catch the "Enter" keypress that
opened a dialogue box from the menu and use it to close the dialogue
box again. I think it's probably reasonable to have it run earlier and
just permanently steal any keypresses it wants.
When we disable a button, it loses focus but doesn't generate a "blur"
event. This means our "focus-within" class goes wrong. Instead of
relying on "blur" events to remove the class, remove it from any
inappropriate elements in the "focus" handler. This requires attaching
the handler to the root element of the document, but I've got plans that
need that anyway.
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.
I'm generally in favour of putting HTML in HTML rather the constructing
it in JavaScript, and this will allow for simplifying the code
eventually. This only changes the JavaScript to make sure that's in
people's caches before I change the HTML itself.
The previous expression for WIDTH defined it, curiously, as (1 +
(TILESIZE >= 16) + (TILESIZE >= 32) + (TILESIZE >= 64)) which is
roughly logarithmic in tile size, but bounded above by a maximum of 4
pixels. On high-DPI displays this isn't really good enough any more.
Now I've set the line thickness to a constant fraction of the tile
size (but still bounded below by 1), so it's much easier to see the
lines when the puzzle is expanded to extra large size.
When there's no drag in progress, cancelling the drag has no effect.
Returning NULL lets the front-end know this, which in particular means
the Backspace key can leave the app in KaiOS.
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.
Games with neither presets nor configuration (which may only be the Null
Game) have been slightly broken since the introduction of hierarchical
preset menus, in that the code to remove the "Type..." menu stopped
being called then. My switch to using radio buttons in menus then broke
them utterly because it's not possible to set the value of an empty
radio group, causing a crash at startup.
Fix this by detected when there's no preset menu, removing the item from
the menu bar, and setting the variable that's meant to indicate this has
been done.
The solve button problem was more subtle, in that only the <button> was
being hidden and not the <li> containing it, which led to the right border of the menu bar being two pixels thick. Switch to fully removing
the <li> from the DOM, like we now do with the presets menu, since that
also makes my keyboard handler (in another branch) simpler.
The C code in the Emscripten front-end already keeps a timer_active
variable to ensure that the timer can be activated only when it's
inactive, and deactivated only when it's active. Adjusting the
JavaScript side to rely on this makes the code much simpler. The only
oddity is that it now requests a new animation frame before calling the
callback so that it's ready to be cancelled if the callback decides to
deactivate the timer.
I think this has been broken since a752e73, when the canvas changed to
being hidden, and hence unable to receive keyboard focus, when the page
loaded. I've now moved the focus() call to after the canvas gets
displayed.
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.
They can now be specified by sticking some JSON in a <script> element in
the Web page:
<script id="environment" type="application/json">
{ "LOOPY_DEFAULT": "20x10t11dh" }
</script>
This isn't brilliantly useful, but it does allow for changing settings
without recompiling.
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.
... and then decide there was no excuse for renaming the variable, so
now it has the same name it had before I started using
Window.requestAnimationFrame().
This is an API specifically designed for the purposes of timing
animations. Unlike setInterval, it tries to synchronise with the screen
refresh rate. It naturally passes us timing information, saving the
need to construct a Date object every frame. It has the nice feature
that browsers (at least Firefox 91) will call it less frequently when
the puzzle page isn't visible, which saves CPU time in puzzles that run
a timer continuously.
This adds a new bool * argument, which can be NULL if front ends don't
care whether the keypress was handled. Currently they all do that.
Currently, "undo" and "redo" keys are treated as not handled if there's
no move to undo or redo. This may be a little too strict.
This way, the front end can intercept one of SoftLeft and SoftRight as a
menu key and leave the other one for the puzzle. And while we don't
have a working menu, I can use whichever is more convenient.
This is the left soft key on KaiOS phones. The centre soft key
already sends "Enter", which eventually becomes CURSOR_SELECT. The
right soft key I'm planning to use to open the menu.
The device pixel ratio indicates how many physical pixels there are in
the platonic ideal of a pixel, at least approximately. In Web browsers,
the device pixel ratio is used to represent "retina" displays with
particularly high pixel densities, and also to reflect user-driven
zooming of the page to different text sizes.
The mid-end uses the device pixel ratio to adjust the tile size at
startup, and can also respond to changes in device pixel ratio by
adjusting the time size later. This is accomplished through a new
argument to midend_size() which can simply be passed as 1.0 in any front
end that doesn't care about this.
With very small tile sizes, js_canvas_find_font_midpoint() can throw an
exception. When it was called from update_pixel_ratio(), this prevented
the new MediaQueryList from being created, which meant that the puzzle
stopped noticing changes of device pixel ratio.
Now update_pixel_ratio() establishes a new MediaQueryList before calling
rescale_puzzle(), so the exception can't break it. Catching the
exception properly would be even better, of course.
It would have helped in the previous commit if I'd tried actually
_playing_ the game, not just admiring it in its initial state. When I
did, I found that lines weren't being fully overdrawn, which turned
out to be because the clip rectangle was being set too narrow.
This is now important due to Ben's changes in the web frontend. On
high-DPI displays, the canvas is the same overall size as before, but
it's scaled up by increasing the game's tilesize rather than the
browser scaling the image after the game redraws.
Loopy ought to have been scaling its line thicknesses all along, but
forgot. Easily fixed.