Canvas & Code Card Components - TJohn2017/Laputa GitHub Wiki

Overall architecture

All of the Canvas interaction is held in the CanvasView.

The CanvasView contains a CodeCardView for each code card, and a PencilKitDrawingView for pencil interactions.

To allow the view to adapt to iPad orientation changes, you should pass in height and width to CanvasView from a geometry reader.

The CodeCardView is implemented using a simplified extension of SwiftTerm's TerminalView which is designed to handle static content only. This allows code cards to parse and display content in the same encoding it uses in the terminal as well as providing an identical visual experience to the terminal view used for live connections. These static terminals have been extended to handle resizing.

User interaction

Drawing mode vs. manipulation mode

The canvas has two interaction modes, to facilitate the combination of drawing and manipulating code cards. Our initial goal was to allow the user to always draw with their Apple pencil, and always be able to navigate and manipulate code cards with their finger. However, we struggled to integrate UIKit and SwiftUI in order to do this, so ultimately we settled on toggling between two modes.

In drawing mode, the user can draw with the Apple pencil. They can't interact with code cards.

In manipulation mode, the user can move, resize, and delete code cards. They can't draw.

In both modes, the user can navigate around the canvas by panning and zooming. To switch between modes, the user can press the mode toggle button or double tap with their finger.

Touch gesture interaction

Canvas interactions

  • drag to pan around
  • pinch to zoom in and out
  • double tap to switch to drawing mode

Code card interactions

  • drag to move and arrange cards on the canvas
  • drag the resize icon to resize the card
  • long press to bring up the option to delete the card

SwiftUI gestures and gestureStates are used for touch interactions. Whenever a gesture is completed, its respective value (magnification, x or y location, offset size) is saved in CoreData in the attributes described in the "Saving canvas and code card data" section.

Notably for code cards, the embedded terminal won't resize until the gesture is completed. This is due to limitations of passing GestureState as a binding to embedded UIKit components.

Apple pencil interaction

An embedded PKCanvasView allows the user to draw with the Apple pencil in drawing mode. All drawing tools such as pen, pencil, eraser, highlighter, cut, and color picker are implemented through PencilKit.

An UndoManager allows the user to undo and redo pencil drawings.

Saving canvas and code card data

When the user interacts with the Canvas component, information is saved in the Canvas and CodeCard entities through CoreData. The Canvas entity represents the canvas itself, and is linked to CodeCard entities, which represent each draggable card created from the terminal component.

Both Canvas and CodeCard entities have custom NSManagedObject subclasses, so that we have more control over required attributes and their defaults, and one-to-many relationships such as Canvases to CodeCards. The custom relationship-based attributes are noted below.

Canvas

General Attributes

Attribute Type Description
id UUID a unique identifier, hidden from the user
dateCreated Date date when canvas was created
title String a user-inputted name which will represent the canvas on the home page
drawingData Data (binary data) binary data version of any Apple pencil drawings made on the canvas
locX Double x location of the canvas (used for saving the user’s last position when panning around the canvas)
locY Double y location of the canvas (used for saving the user’s last position when panning around the canvas)
magnification Double how zoomed in the user last was when using the canvas

Custom relationship-related attributes

Attribute Type Description
cardArray array of CodeCard an array of code cards that exist on the canvas, sorted by descending zIndex

CodeCard

General Attributes:

Attribute Type Description
id UUID a unique identifier, hidden from the user
dateCreated Date date when card was created
locX Double x location of the card relative to the canvas
locY Double y location of the card relative to the canvas
zIndex Double stack order index for a card; higher zIndex cards will always show up on top of lower zIndex cards
text String the text output from a terminal that is displayed on the card
width Float width of the card
height Float height of the card

Custom relationship-related attributes

Attribute Type Functionality
origin Canvas the canvas that this card exists on