/weft

Weft is a dynamic UI generation library for Cocoa applications (a component of Project Mentat)

Primary LanguageObjective-CMIT LicenseMIT

Weft

Version 0.9 - 22nd July 2019

Weft is a framework designed to be embedded into a Cocoa application to allow the application to create custom user interfaces at run-time. The primary use-case is to allow a user-written script to create a custom form that can capture user data and return it to the script. Weft is a component of Project Mentat.

The Weft framework includes a WeftRunner class that accepts a UI definition as an XML string. Weft defines an XML syntax for creating elements such as textfields and buttons. The WeftRunner returns an NSWindowController instance that can display the UI, talk to the host application via a WeftApplicationDelegate protocol, and provide the data from form elements to the host application when the window is dismissed.

See the Element Guide for description of the Weft XML syntax.

Example

For a more complete example see the WeftHarness target in the Xcode project. Clone the project and run WeftHarness for a live demo.

<window title='Weft Test Window' width='480' height='90' insets='10,10,10,10'>
  <row>
    <label title='Name:' same-width='password-label'/>
    <textfield id='name' placeholder='John. Q. Public'/>
  </row>
  <row>
    <label id='password-label' title='Password:'/>
    <password id='password'/>
  </row>
  <row insets='0,10,0,10' pin-superview-leading='1' distribution='fillproportionally'>
    <checkbox title='Remember Me' id='remember'/>
    <popupbutton id='length' choices='1 week,2 weeks,1 month' default='2 weeks'/>
    <ok/>
  </row>
</window>

Watch the video

Getting Started

Installation

Weft is intended to be installed using the Carthage package manager. See the section on installing frameworks into your application.

Add the following to your Cartfile:

github "https://github.com/mmower/weft"

Then run carthage update --platform macOS to download and build the framework before adding it to your application as described above.

Alternatively you can download and build the framework manually and copy it into your application, updating the embedded binaries section of your target in Xcode.

Class, Categories, & Protocols Guide

Weft defines a number of classes, categories, and protocols that an application hosting Weft needs to interact with.

The WeftRunner class creates a WeftApplication from an XML string defining the interface. The definition is compiled into a tree of NSView objects representing the user-interface which in turn is wrapped in an NSWindow and returned via an NSWindowController that the host application can use to manage the UI. By implementing the methods of the WeftApplicationDelegate protocol the host application can respond to changes in the UI (e.g. buttons being pushed), the user dismissing the UI, and obtain the data stored in the data elements of the UI (e.g. textfield contents & checkbox states).

WeftRunner (class)

The WeftRunner class is the host applications interface to the Weft framework. Each WeftRunner is responsible for managing an NSWindow and returns an NSWindowController to the host application by which it can control the display of the user interface.

Methods

- (instancetype)initWithSource:(NSString *)source delegate:(id<WeftApplicationDelegate>)delegate

Creates an instance of WeftRunner to manage a WeftApplication defined by the source XML string. Associates a WeftApplicationDelegate provided by the host app to receive callbacks from the WeftApplication during the operation of the user interface.

- (NSWindowController *)run

Creates an NSWindow and NSWindowController to operate the user interface. The window controller instance is returned to the

- (void)close

WeftApplication (class)

The WeftApplication class is used internally to represent the definition of the interface parsed from the source XML and the NSView elements that are used represent them in the live interface. Host applications do not create WeftApplication instances as this is the job of the WeftRunner. An instance of WeftApplication is passed to all delegate methods.

The lifecycle of WeftApplication instances is governed by the WeftRunner. The host application should not need to keep a references to WeftApplication itself. The result of calling methods on WeftApplication after the UI has been dismissed are undefined.

Methods

- (NSDictionary *)values

The values message can be sent to a WeftApplication to retrieve the current value of all data elements in the interface. The returned dictionary usese the id attributes of these elements as keys referencing appropriate attribute values (e.g. an NSString for the value of an NSTextfield specified by a <textfield> element).

WeftApplicationDelegate (protocol)

WeftApplicationDelegate defines 1 mandatory and 3 optional methods for allowing the host and WeftApplication to interact.

Note that if the delegate also supports the NSTextFieldDelegate protocol then it will be registered as the delegate for any NSTextField objects generated.

Mandatory methods

- (void)weftApplication:(WeftApplication *)app complete:(BOOL)ok

When the user clicks on the button represented by an <ok> or <cancel> element the WeftApplication sends this message to its delegate. If an <ok> element was pressed the complete: argument is YES otherwise it is NO. The host is responsible for calling -close on the provided NSWindowController to dismiss the interface.

Optional methods

- (void)weftApplication:(WeftApplication *)app buttonPushed:(NSButton *)button

When the user clicks on a button represented by a <button> element the WeftApplication sends this message to its delegate.

- (void)weftApplication:(WeftApplication *)app radioSelected:(NSButton *)radio

When the user selects a different radio button represented by <radio> element the WeftApplication sends this message to its delegate.

- (void)weftApplication:(WeftApplication *)app checkboxToggled:(NSButton *)checkbox

When the user selects or deselects a checkbox represented by the <checkbox> element the WeftApplication sends this message to its delegate.

NSView+Weft (category)

Weft defines an NSString * property elementId on NSView.

- (NSString *)elementId

Returns the value that was specified using the id attribute that is mandary on data elements in Weft XML. For example id is a mandary attribute of the <textfield> element. This allows the host application to relate the elements defined in the source to the dynamically generated NSView instances that represent them in the user interface. The host application code should not need to call the associated -setElementId: method itself, doing so may cause problems.

Element Guide

Structural Elements

The structural elements create the outline of the user interface. All Weft interfaces start with a <window> which, by default, contains a col with no insets and using a distribution of fillequal into which further elements can be places. This simplifies in that the user does not need to specify a container for elements. Further <row> and <col> elements may be used to structure the interface into rows and columns as required.

<window>

Example

A <window> element specifies the top-level window and contains all other elements.

<window title="Weft Demo" width="640" height="480">
  …
</window>
Attributes
* title: Title for the window
* width: initial width of the window (this may be changed if the layout ends up wider)
* height: initial height of the window (this may be changed if the layout ends up taller)
* insets: a comma-separated set of values for the top,left,bottom,right margin of the window

<row>

A <row> organises its contents horizontally. Internally a row is represented via an NSStackView whose orientation property is set to NSUserInterfaceLayoutOrientationHorizontal.

Attributes
  • insets - a series of values used as a margin between the edge of the row and its contents. Specified as top, left, bottom, right
  • spacing - number of points of spacing between elements
  • distribution - controls how contents are distributed across the width of the row. Valid values are equalcentering, equalspacing, fill, fillequally, fillproportionally, gravity

<col>

A <col> organises its contents vertically. Internally a col is represented via an NSStackView whose orientation property is set to NSUserInterfaceLayoutOrientationVertical.

Attributes

See row

Data Elements

Data elements represent the input controls that appear within the UI layout and have values that can be obtained via the - values method.

Mandatory Attributes

The following attributes are mandatory for all data elements:

  • id: a unique identifier for the element that will be associated with its correspdonding NSView instance via the -elementId method.

Optional Attributes

The following attributes are supported for all data elements but are optional:

  • gravity: when the data element is placed into a <col> or <row> whose distribution is set to gravity then this attribute specifies the gravity area into which the control will be placed. Valid values are leading, center, and trailing for <row> elements and top, center, and bottom for <col> elements.
  • same-width:
  • same-height:
  • pin-superview:
  • pin-superview-leading:
  • pin-superview-top:
  • pin-superview-trailing:
  • pin-superview-bottom:

<textfield>

A <textfield> creates an NSTextField that supports a single line of editable text.

Attributes
  • default: default value to user for the textfield
  • placeholder: value to be displayed in textfield if there is no value
  • tooltip: value to be displayed as a tooltip
  • disabled: set to 1 to disable this control

<textbox>

A <textbox> creates an NSTextView that supports editing a longer text. The initial content of the textbox should be added between the opening and closing tags.

Example
<textbox id='message'>
  Here is the text of your message.
</textbox>
Attributes

<password>

A <password> creates an NSSecureTextField that allows for editing sensitive values such as passwords and prevents them appearing on screen.

Attributes

<button>

A <button> element specifies a push-button (NSButton) control.

Attributes
  • id: unique identifier for the button. Will be sent to the delegate.

<ok>

An <ok> element specifies an Ok button. It is a special type of button that, when pushed, sends the WeftApplicationDelegate a message - (void)weftApplication:(WeftApplication *)app complete:(BOOL)ok passing YES as the complete value.

Attributes
  • title: the title to appear on the ok button

<cancel>

A <cancel> element specifies a Cancel button. It is a special type of button that, when pushed, sends the WeftApplicationDelegate a message - (void)weftApplication:(WeftApplication *)app complete:(BOOL)ok passing NO as the complete value.

Attributes

<checkbox>

A <checkbox> element specifies an on/off checkbox control.

Attributes
  • id: unique identifier for the checkbox. Will be used as a data key
  • title: string for the label associated with the checkbox
  • disabled: specify a value of 1 to have the control be disabled
  • gravity: if in a <row> or <col> using the gravity distribution specify a value of leading,

<radio>

Attributes

<popupbutton>

Attributes

<datepicker>

Attributes

Acknowledgements

Philosophically Weft was inspired by Pashua and Cocoa Dialog which demonstrated that such an approach was viable. Also XAML and XUL which encouraged a need for simplicity of purpose. Weft is made possible by Apple AutoLayout and NSStackView.

License

Weft is released under the MIT license, see the LICENSE file for details.