skia-canvas icon indicating copy to clipboard operation
skia-canvas copied to clipboard

Add support for GUI windows, animation, and interactivity

Open samizdatco opened this issue 2 years ago • 0 comments

This PR adds a new WIndow class and the App global variable to coordinate starting and stopping the GUI environment. Each Window has an associated Canvas object, whose dimensions can be set independently of the size of the window. Resizing the window will scale the canvas to fit it using similar options as the css object-fit property.

Window attributes can be assigned via properties (after the object has been instantiated) or passed as part of an options object passed to the constructor. Its configurable attributes include:

  • context selection: page
  • bounds: left / top / width / height
  • css: cursor / fit
  • flags: fullscreen / visible
  • color: background
  • label: title

Multiple Window objects can be instantiated and will be displayed as soon as the current event loop completes (or once the blocking App.launch() method is called manually). This transfers control of the global event loop away from Node and lets the underlying OS take over moment-to-moment operations. As a result, you will not be able to use any of Node's asynchronous behaviors: in particular, setTimeout and setInterval callbacks will not fire while the App is running.

Instead, you must use event handlers attached to the Window objects. These allow you to respond to user interface events (e.g., mouse and keyboard input, the window being dragged or resized, changes in which window is currently active, etc.). Each window is an EventEmitter object and supports the standard .on(), .off(), and .once() methods for setting event handlers. The full set of supported event types is:

  • mousedown / mouseup / mousemove / wheel
  • keydown / keyup / input
  • move / resize
  • blur / focus
  • setup / frame / draw

The frame event is similar to the requestAnimationFrame timer in browsers in that it will trigger your callback at the proper time to update the screen at the current frame rate (by default 60 fps, though this can be set via the App.fps property). The draw event operates the same way but also has the side effect of clearing the canvas before triggering your event handler. This makes it ideal for ‘canned’ animations that don't require user interaction and can be driven solely by the current 'frame number' integer (which is passed as the sole argument to your event handler with each call). The setup event fires once, just before the initial frame is rendered, allowing you to perform last-minute preparation that requires access to the ‘live’ environment.

The run will end once you close all of the open windows or call the App.quit() method, returning control to the Node event loop (but leaving the App in a state where it cannot be re-launched).

On Linux and Windows the display layer uses Vulkan (with the rendering pipeline coordinated by the Rafx framework). On macOS, the system's Metal backend is used instead. To build the rust components locally, you will need to specify the platform-appropriate features to use:

  • macOS: npm run build -- --release --features metal,window
  • Linux/Windows: npm run build -- --release --features vulkan,window

samizdatco avatar Aug 01 '22 00:08 samizdatco