Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,106 @@ To pick the right `CCWindowManagerConnection` instance, CCKit2 asks the app to s

The application delegate class in the skeleton above is appropriate for most applications, but it can be extended to receive those events too.

## JSX
CCKit2 includes support for defining views declaratively using JSX, which is an XML-derived syntax for creating elements directly in code. This syntax makes it much easier to see the view hierarchy at a glance.

JSX is only available when using TypeScript.

### Configuration
Add the following options to your `tsconfig.json` to enable JSX:
```json
{
"configurationOptions": {
"jsx": "react",
"jsxFactory": "CCJSX.createElement"
},
"include": [
"./src/*.tsx"
]
}
```

Then rename any file which will have JSX elements to have the `tsx` extension, and add `import * as CCJSX from "CCKit2/CCJSX";` to the top of each of those files.

### Imports
Due to limitations in TSX and the CCKit2 codebase, it is not possible to use the class as a component type directly. This is worked around through a separately exported function component in each class module.

For each view type used in the JSX hierarchy, import its `JSX` exported component and name it something familiar (for example, the view type name suffixed with `JSX`). If you also need the class itself, use the `default as` syntax.

For example:

```ts
// imports the CCButton JSX component
import {JSX as CCButtonJSX} from "CCKit2/CCButton";
// imports both CCView and its JSX component
import {JSX as CCViewJSX, default as CCView} from "CCKit2/CCView";
```

Then use the JSX component for all views in place of the class.

### View Construction
To implement JSX in your view controller, instead of creating the view hierarchy with code in `viewDidLoad`, you implement the `constructedView` getter which returns a JSX-constructed root view.

For example, an existing application with the following code:
```ts
public viewDidLoad(): void {
super.viewDidLoad();
let label = new CCLabel({x: 1, y: 1}, "text");
label.textColor = CCColor.blue;
this.view.addSubview(label);
}
```

could be transformed into a JSX view like this:
```tsx
public get constructedView(): CCView {
return <CCViewJSX frame="1 1 20 10">
<CCLabelJSX pos="1 1" textColor="blue">text</CCLabelJSX>
</CCViewJSX>
}
```

The frame for the root view isn't important, as it's overwritten with the window's dimensions on load. Implement `preferredContentSize` to set the root view's default size.

### Attributes and Children
Attributes on elements are used for both construction parameters and other settable properties. All views require at least a position or frame, which is encoded as a string with numbers separated by spaces - XML parameters must be strings (unless imported from TS code as described below), so structures such as `CCRect` and `CCPoint` are encoded this way. Some views may require additional attributes for construction. The rest of the attributes are optional, and are fetched from the public fields on the interface that can be represented as a string.

As described in JSX's syntax, attribute values and children may include TypeScript code in `{}` brackets, which will be evaluated and inserted at runtime. For convenience, values of attributes inserted this way may return their original type instead of a string.

The text property of views that have one are usually a special case: these elements expect the value to be in the body of the element instead of an attribute. This also means that these views cannot have subviews - the body is only for text. For example, a label will look like `<CCLabelJSX pos="1 1">Text</CCLabelJSX>` instead of `<CCLabelJSX pos="1 1" text="Text" />`.

Other elements may have any number of subviews as children, as well as `constraint` elements for declaring constraints (described below). Empty elements may be terminated with the standard XML `/>` self-termination syntax.

### Constraints
Constraints may be defined as `constraint` tag children of an element, using the same attributes as are available on `CCLayoutConstraint` objects. Each constraint has its first item implicitly set to its parent. The second item may be declared using either a view directly, an outlet to a view, or the special key `"superview"`. If using an outlet, the target element must be declared above the current view, as outlets are evaluated just-in-time in order.

### Outlets and Actions
Outlets allow you to connect created views to properties in the containing view controller. The `outlet` attribute is available on every view element, and takes the result of `CCViewController.outlet(key: string)` as a value.

To connect a view to the view controller, define a public property on the class with an optional type of the view's class (`!` recommended). Then assign the `outlet` attribute to the result of `this.outlet` called with the name of the property as a string. For example:

```tsx
public label!: CCLabel; // will be connected to the label below
// ...
<CCLabelJSX pos="1 1" outlet={this.outlet("label")}>Text</CCLabelJSX>
```

Once the root view is loaded, the property will be assigned with the view with the outlet. Future methods (including `viewDidLoad`) can use the property as normal.

CCKit2's JSX implementation includes strong typing to enforce outlet connections. The `outlet` attribute will cause an error if the property name doesn't exist on the view controller, or if the property is the wrong type.

Actions function similarly, but in reverse: actions are bindings to methods in the view controller which you can pass to a view. Views may define one or more action attributes, which take the result of `CCViewController.action(method: Function)`. Unlike outlets, the action method takes the method to call directly, not the name - this allows enforcing the type of the method the same way that outlets do.

This example demonstrates connecting an action to a button (which is required by `CCButton`'s attribute list):

```tsx
public pressed(sender: CCView): void { // sender is optional
// do thing...
}
// ...
<CCButtonJSX pos="1 1" action={this.action(this.pressed)}>Button</CCButtonJSX>
```

## Next Steps
Full documentation on all of the classes available in CCKit2 is available [on the website](https://phoenix-computercraft.github.io/CCKit2/). The classes are categorized by their type, with the Views category containing all visual elements and related classes. Look through the fields and methods of each class to discover how to use them.

Expand Down
2 changes: 1 addition & 1 deletion docs/assets/hierarchy.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 23 additions & 2 deletions docs/assets/highlight.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@
--dark-hl-8: #B5CEA8;
--light-hl-9: #AF00DB;
--dark-hl-9: #C586C0;
--light-hl-10: #0070C1;
--dark-hl-10: #4FC1FF;
--light-hl-10: #800000;
--dark-hl-10: #808080;
--light-hl-11: #E50000;
--dark-hl-11: #9CDCFE;
--light-hl-12: #000000FF;
--dark-hl-12: #D4D4D4;
--light-hl-13: #0070C1;
--dark-hl-13: #4FC1FF;
--light-code-background: #FFFFFF;
--dark-code-background: #1E1E1E;
}
Expand All @@ -37,6 +43,9 @@
--hl-8: var(--light-hl-8);
--hl-9: var(--light-hl-9);
--hl-10: var(--light-hl-10);
--hl-11: var(--light-hl-11);
--hl-12: var(--light-hl-12);
--hl-13: var(--light-hl-13);
--code-background: var(--light-code-background);
} }

Expand All @@ -52,6 +61,9 @@
--hl-8: var(--dark-hl-8);
--hl-9: var(--dark-hl-9);
--hl-10: var(--dark-hl-10);
--hl-11: var(--dark-hl-11);
--hl-12: var(--dark-hl-12);
--hl-13: var(--dark-hl-13);
--code-background: var(--dark-code-background);
} }

Expand All @@ -67,6 +79,9 @@
--hl-8: var(--light-hl-8);
--hl-9: var(--light-hl-9);
--hl-10: var(--light-hl-10);
--hl-11: var(--light-hl-11);
--hl-12: var(--light-hl-12);
--hl-13: var(--light-hl-13);
--code-background: var(--light-code-background);
}

Expand All @@ -82,6 +97,9 @@
--hl-8: var(--dark-hl-8);
--hl-9: var(--dark-hl-9);
--hl-10: var(--dark-hl-10);
--hl-11: var(--dark-hl-11);
--hl-12: var(--dark-hl-12);
--hl-13: var(--dark-hl-13);
--code-background: var(--dark-code-background);
}

Expand All @@ -96,4 +114,7 @@
.hl-8 { color: var(--hl-8); }
.hl-9 { color: var(--hl-9); }
.hl-10 { color: var(--hl-10); }
.hl-11 { color: var(--hl-11); }
.hl-12 { color: var(--hl-12); }
.hl-13 { color: var(--hl-13); }
pre, code { background: var(--code-background); }
2 changes: 1 addition & 1 deletion docs/assets/navigation.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/assets/search.js

Large diffs are not rendered by default.

Loading