Skip to content

Instantly share code, notes, and snippets.

@alterecco
Created November 28, 2011 23:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alterecco/1402535 to your computer and use it in GitHub Desktop.
Save alterecco/1402535 to your computer and use it in GitHub Desktop.

DSF documentation

Version
Date

DSF (Dock Screen Framework) is a framework that provides an API (Application Programming Interface) to build Dock Screens in pure tscript (Transcendence Scripting Language). It also comes with some common utility functions that can be used in other mods and defines some common useful screens.

Limitations

DSF (Dock Screen Framework) is a very powerful framework, but it has its limitations. Not every conceivable Dock Screen can be built using DSF without having very good knowledge of tscript (Transcendence Scripting Language) and how DSF works. The set of standard screens that DSF provides have no fancy background images for example, so if you want to add that kind of flavour and use DSF then you would have to make some additions yourself. See adding-types for an example. A complex Canvas screen like the neurohack screen can be hard to build as well. DSF does not come ready to build such a screen, but, with some work, can be extended to do it.

Examples

Simple Example

Here is an example to get started on. It is the definition of a very simple Dock Screen with a title, a description and a single action that updates the description.

(dsf/new-screen 'example "Example Screen")
(dsf/set-description "This is the initial description")
(dsf/add-action 'update '("Update Description" 'U) (lambda nil
    (block nil
        (dsf/clear-description)
        (dsf/set-description "This is the updated description")
    )
))

That is how simple it is to define a basic Dock Screen. Under the hood there is a lot of stuff going on in DSF (Dock Screen Framework), but the basic concept is rather simple. Let us walk through the above example line by line and have a look at how it works.

Creating the screen

With a call to dsf/new-screen we start defining a new screen. We supply a unique screen-id that we will use to refer to this screen from now on. We also supply the text for the title of the screen. One of the first things that happen upon creating a new screen is setting it as the active screen. Almost all API (Application Programming Interface) functions in DSF (Dock Screen Framework) work only on the active screen. That means we don't have to supply the screen id when we use the API functions.

We could also have supplied the type of the screen we wanted to use, but since we are fine with the default we leave it out. DSF comes with four builtin Screen Types <screen-type> (Standard, CustomPicker, ItemPicker and Canvas). The default type is Standard.

Note

On Visibility

It is important to realise that at this point, the screen we are defining is not actually being shown on the screen. It is just being defined for later use. More on that in the visibility section.

Setting the description

We set an initial description using dsf/set-description. Since we have no fancy needs we just pass in a string. This string will be used raw when the screen description is finally set. Notice how we don't need a screen id. DSF lets us work easily on the current active screen.

Adding the action

We add an action to the screen by calling dsf/add-action. We supply it with three arguments, an action id, a handle and an expression.

The action-id is a unique name which serves to identify this action. It need only be unique for this single screen though. That means you can define multiple screens with a 'select action, for example.

The handle sets the action-handle of the action. Typically it will be a tuple (a list with two elements) with the actions label as the first item and the key as the second.

The expression we use here is a lambda that contains the script we would like to run when the action is invoked. As you can see, when that happens, we first clear the current description and then set a new one. Since this is happening inside an action, the pane is going to be updated at the end, with the result that a new description will be shown.

This is just a small example of what an action can do. You can actually do just about anything inside an action, even define entire new screens.

Showing the screen

Like stated in the note above, the screen is not actually visible at this point. It is just stored as a set of data that DSF can use to show a screen.

When the time comes that you want to show your screen, all it takes is simply a call to dsf/show-screen. If you want to show a specific screen then you can pass in the screen id, but often you will just want to show the active screen. So, all it would take to show the screen we defined in the example above, is to follow up with a simple:

(dsf/show-screen)

That would pop up a Dock Screen with the contents of our 'example screen.

Note

Default Actions

You might notice how we don't define an Exit action or a Back action, yet both appear. DSF automatically adds those two actions when applicable, unless you specifically tell it not to.

That concludes our simple example. Next we will build one of the default Transcendence screens in DSF, so we can compare them side by side.

A Transcendence Screen

Adding a new screen type

TODO

API (Application Programming Interface)

Screen Functions

(dsf/new-screen id [name] [type])

Create a new screen and set it as the current active-screen.

arg id

id of the screen

arg name

optional name of the screen

arg type

optional screen-type

You must provide an id, as that is used to uniquely identify <screen-id> this screen during this session. name is optional, but often you will just set it here. It can be any expression. See screen-name. If you leave it out, or set it to nil then it will be blank. type can be any valid screen-type. It defaults to 'standard.

(dsf/use-screen id)

Switch the active-screen to the screen specified by id. This also turns off the current screens visibility <visibility>. If the id provided does not match a defined screen weird things may happen.

(dsf/show-screen [id])

Show the currently active-screen or, the screen specified by id if provided.

(dsf/back)

Show previous screen if any such exists. Any expressions added to the current screen via dsf/on-back or dsf/on-global-back will be evaluated.

(dsf/exit [force])

Exit DSF (Dock Screen Framework). If we are in a nested-screen and force is not true then we will return to previous screen. Any expressions added to the current screen via dsf/on-exit or dsf/on-global-exit will be evaluated.

(dsf/set-type type)

Set the type of the screen. type can be a builtin screen-type or a type defined by the user.

(dsf/get-type)

Return the current type of the screen.

(dsf/set-name expression)

Set the name of the screen. expression can be any expression

(dsf/get-name)

Get the evaluated version of the name. The default is an empty string.

(dsf/set-pane pane)

Set the pane type. For the default DSF (Dock Screen Framework) screens this can be any of 'default, 'textinput or 'counter, as explained in pane-type

Updates Visible <visibility>

(dsf/get-pane)

Return the name of the current pane

(dsf/set-description expression)

Set the description for the screen.

There are two types of values you can provide. A string or a lambda.

If you provide a string it will be appended to the current description, or become the current description when no other is set.

If you need more control, then provide a lambda. The lambda must take one argument, which will be the current description. Any value returned by that function will be used as the final description.

Here follows two examples of the use

First with strings only:

(dsf/set-description "This is the first line")
(dsf/set-description "\nThis is the second line")

This will lead to the description being

This is the first line
This is the second line

Second using a mix of lambdas and strings

(dsf/set-description "Welcome to the Sales Emporium.")
(dsf/set-description (lambda (desc) (cat desc "\n\n" "You are currently on the " (objGetName gSource 1) " station")))

That will lead to a description that reads:

Welcome to the Sales Emporium

You are currently on the {Name Of Station} station

In many cases using plain strings will serve just fine, but when you need to compute some value for the description it is often an idea to use a lambda.

If you want to overwrite the current description, then use a lambda and just return what you want the description to be.

Updates Visible <visibility>

(dsf/clear-description)

Clears any description set with dsf/set-description

(dsf/notify expression)

The expression argument works exactly the same as the one for dsf/set-description. The only difference is that it will only be used once (and it will be prepended to the current description, with two line breaks in between).

Use this if you need to notify of some result in an action:

(dsf/add-action 'notify "Notify" 'N (lambda nil
    (dsf/notify "I completed some other task when running this action")
))

(dsf/set-textinput value)

Set the value of the textinput field

(dsf/get-textinput)

Get the value of the textinput field

(dsf/set-counter value)

Set the value of the counter

(dsf/get-counter [low] [high])

Get the value of the counter. If either low or high are provided, then the value will be constrained within those boundaries

(dsf/set-data-from expression)

Sets the source object to get data from in an itempicker screen. This can be a expression, a space-object or one of 'player or 'station. If you pass in 'player the value gets set to gplayership when the screen is shown. If you pass in 'station it gets set to gsource.

(dsf/get-data-from)

Returns the object we get data from

(dsf/set-item-filter expression)

Set a filter that determines what items will be shown in an itempicker screen. Only items matching the expression will be shown.

There are two possible types of values, a string and a lambda.

If you pass in a string it will be taken as literal item-criteria, and only items matching the criteria will be shown, e.g. *NU -Illegal; will match all undamaged, un-installed items that are not illegal.

Now, if you need much more control you can pass in a lambda. Because of the way DSF (Dock Screen Framework) works, you actually need to provide an expression that returns a lambda that takes a single argument.

Here is an example:

(dsf/set-item-filter (lambda nil
    (block (some-criteria)
        ;; here you could actually set some variables that would be
        ;; available withing the returned function
        (setq some-criteria "*NU")
        ;; now we define and return the actual lambda
        ;; it must take a single argument, the current item
        (lambda (itm) (block nil
            ;; lets check if the item matches some-criteria
            ;; and has a good price
            (and (itmMatches itm some-criteria)
                (ls (itmGetPrice itm) 1000))
        ))
    )
))

The inner function (the one that accepts a single argument) is called once for each item in on the source, with the item as its argument, and every item it returns true for will be included in the final list. The example above would include every undamaged and un-installed item with a price lower than 1000 credits.

(dsf/clear-item-filter)

Clear any filter set with dsf/set-item-filter

(dsf/get-item-filter)

Return the filter to be used, or, as a default, *

(dsf/add-list-entry entry)

Add a entry to the list that will be displayed in a custompicker. There is a very specific syntax for this entry which looks like this:

(dsf/add-list-entry (list
    (list 'title        "Title of Entry")
    (list 'subtitle     "Subtitle of Entry")
    (list 'icon         "Icon of Entry")
    (list 'something    "Optional data")
))

'title, 'subtitle and 'icon are built in values. Apart from that you can add as much free form data as you would like. All of the values are optional, but it is recommended to include a title. All of the values can be a expression as well. Any optional fields you add will be retrievable later using dsf/get-list-entry.

If you want to add an 'icon then it must be a valid image resource unid.

In addition there are two other special fields, 'on-select and 'hide. If 'on-select is an expression it will be run when the list entry is selected. Use this for adding actions when the entry is selected or changing the description or whatnot. The sky is the limit. If you add a 'hide field it will be used to determine if the entry should be hidden. You can set it as an expression, or simply to some true value.

You can use literal lists as a shorthand if you don't need closures or variable evaluation inside the entry. Here is the same list as above:

(dsf/add-list-entry '(
    (title        "Title of Entry")
    (subtitle     "Subtitle of Entry")
    (icon         "Icon of Entry")
    (something    "Optional data")
))

(dsf/get-list-entry [key])

Return the current list entry (the one the list cursor is currently at). If the optional key is provided it will return the value of the field that has that key. If that field does not exist it will return nil.

(dsf/get-list-entry 'title)

This will return the expression used to set the title for the current entry.

(dsf/add-list-entries entries)

This is a wrapper around dsf/add-list-entry, useful for when you want to add a lot of entries in one go. It lets you build large lists easily. Here is an example, adding two entries:

(dsf/add-list-entries (list
    (list
        (list 'title        "Entry Number One")
        (list 'subtitle     "Subtitle Number One")
    )
    (list
        (list 'title        "Entry Number Two")
        (list 'subtitle     "Subtitle Number Two")
    )
))

Just like with dsf/add-list-entry you can use literal lists when it fits.

(dsf/clear-list-entries)

Clear all list entries

(dsf/get-list-entries)

Return a list of entries or nil

(dsf/add-list-filter)

Add a filter expression that will be used to filter the list data before it is displayed. The expression must be a lambda that takes one argument. This argument will be the complete set of list entries. You can add as many filters as you want. They will be processed in order, each receiving the data returned by the previous one.

You can use these filters to sort the entries, or to generate new entries entirely. The lambda must return valid list entries for it to be of any use.

(dsf/clear-list-filter)

Clear the list filters

(dsf/get-list-filter)

Return a list of list filters or nil

(dsf/get-item)

Return the current selected item. Only works on itempicker screens

(dsf/remove-item [amount])

Remove the current selected item. Only works on itempicker screens. If amount is specified, only remove that many. If it is not specified, all items will be removed.

(dsf/is-list-empty)

Return true if the current custompicker or itempicker list is empty. (Good for checking if actions should be hidden)

Action Functions

(dsf/add-action id [handle] [expression])

Add a new action identified by id to the current active-screen.

arg id

id of the action

arg handle

the label and key of the action

arg expression

the expression to run when the action is invoked

type handle

string, tuple (a list with two elements), or lambda

The id must be unique for this screen only. It is an action-id used to refer to this action on this screen. handle and expression are both optional, but typically they are easiest to set in one go.

The handle is used to set the label that will be displayed for the action and key that will invoke it. If you don't want a key then just pass in a string. If you want a key as well, then you should provide a tuple (a list with two elements) with the first element being the label and the second the key. The key should always be a character contained in the label. If you need dynamic labels and keys, then you can use a lambda that returns either a string or a tuple (a list with two elements)

The expression is the code that will be evaluated when the action is invoked. It can be either a lambda or a literal piece of code.

Here are some examples of adding an action that updates the description when invoked. This showcases the different ways of writing the same action:

using a lambda as expression:

(dsf/add-action 'action-one '("Action One" 'O) (lambda nil
    (block nil
        (dsf/set-description "Ran Action One")
    )
))

using a literal block and a handle set via a variable:

(setq action-handle (list "Action Two" 'T))
(dsf/add-action 'action-two action-handle '(block nil
    (dsf/set-description "Ran Action Two")
))

or even simpler, just using a literal piece of code:

(dsf/add-action 'action-three '("Action Three" 'A) '(dsf/set-description "Ran Action Three"))

Personally i perfer the lambda syntax, since it is less cryptic and allows for the use of closure. But in the above case, the last example is the simplest and suffices.

(dsf/set-no-back [expression])

Set whether the current screen should show a back action. If you don't pass in an expression it will default to true. The back action works with the history.

Updates Visible <visibility>

(dsf/clear-no-back)

Remove any no-back expression. This will most likely lead to a back action being shown.

Updates Visible <visibility>

(dsf/get-no-back)

Return the evaluated value of the no-back expression.

(dsf/set-no-exit [expression])

Just like dsf/set-no-back this controls whether an exit action should be shown. expression defaults to true

Updates Visible <visibility>

(dsf/clear-no-exit)

Remove any no-exit expression. This will most likely lead to an exit action being shown.

Updates Visible <visibility>

(dsf/get-no-exit)

Return the evaluated value of the no-exit expression.

(dsf/set-action-handle id handle)

Set the handle for a given action id. handle can be either a string, a tuple (a list with two elements) or a lambda returning one of those two.

Updates Visible <visibility>

(dsf/clear-action-handle id)

Clear the handle for a given action id

Updates Visible <visibility>

(dsf/get-action-handle id)

Get the evaluated handle for an id

(dsf/set-action-expression id expression)

Set the expression for a given action id. It will not be evaluated until the action is run. It will typically be a lambda or a literal block. Look at dsf/add-action for some examples of valid expressions.

(dsf/clear-action-expression id)

Remove the expression associated with the action

(dsf/get-action-expression id)

Return the expression that will be run for the action specified by id

(dsf/set-default-action id)

Set the action defined by id as the default action. That means it will be the one invoked when Enter is pressed in a screen. If you already a default action, this will supersede it and become the new default action.

Updates Visible <visibility>

(dsf/set-cancel-action id)

Set the action defined by id as the cancel action. That means it is the action that will be invoked when Escape is pressed.

Updates Visible <visibility>

(dsf/set-next-action id)

Set the action defined by id as the previous action. That means it is the action that will be invoked when the left arrow key is pressed.

Updates Visible <visibility>

(dsf/set-prev-action id)

Set the action defined by id as the next action. That means it is the action that will be invoked when the right arrow key is pressed.

Updates Visible <visibility>

(dsf/set-disable-action id [expression])

Set an expression for an action that will control whether or not the action should be disabled. This expression will not be tested until the action is actually shown. The default value is true, so if you leave out the expression the action will just be unconditionally disabled.

A typical example of it's use is to add a lambda as expression, one that tests some value, for example if there are any items on the screen (in an itempicker screen)

(dsf/set-disable-action 'some-action (lambda nil (dsf/list-is-empty)))

dsf/list-is-empty returns true if there are no items in the screen, which leads to the action being disabled.

Updates Visible <visibility>

(dsf/clear-disable-action id)

Clear the expression set with dsf/set-disable-action

Updates Visible <visibility>

(dsf/is-disabled-action id)

Return whether the action should be disabled or not

(dsf/set-hide-action id [expression])

Mimics dsf/set-disable-action, just controls whether the action should be hidden.

Updates Visible <visibility>

(dsf/clear-hide-action id)

Clear the expression set with dsf/set-disable-action

Updates Visible <visibility>

(dsf/is-hidden-action id)

Return whether the action should be hidden or not

(dsf/set-no-save id [expression])

Set an expression that controls whether the action should be save when run. This deals with history. If no expression is provided it defaults to true.

(dsf/clear-no-save id)

Clear the expression set with dsf/set-disable-action

(dsf/get-no-save id)

Return whether the action should save or not

Event Functions

(dsf/on-screen-init expression)

(dsf/clear-on-screen-init)

(dsf/on-pane-init expression)

(dsf/clear-on-pane-init)

(dsf/on-display-init expression)

(dsf/clear-on-display-init)

(dsf/on-display-canvas expression)

(dsf/clear-on-display-canvas)

(dsf/on-run-action expression)

(dsf/clear-on-run-action)

(dsf/on-back expression)

(dsf/clear-on-back)

(dsf/on-exit expression)

(dsf/clear-on-exit)

(dsf/on-global-back expression)

(dsf/clear-on-global-back)

(dsf/on-global-exit expression)

(dsf/clear-on-global-exit)

(dsf/on-initial-item expression)

(dsf/clear-on-initial-item)

Helper Functions

(dsf/session-get path)

(dsf/session-set path value [operation])

(dsf/session-delete path)

(dsf/screen-get path)

(dsf/screen-set path value [operation])

(dsf/screen-delete path)

(dsf/screen-remove-value path value)

Concepts

DSF (Dock Screen Framework) introduces a few major concepts. This section will give an overview of them. We will also briefly mention some of the builtin Transcendence and tscript (Transcendence Scripting Language) concepts that Dock Screens make use of.

Session

History

Screen ID

When we create a new screen we start by giving it an identifier (we will refer to this as a screen-id). This identifier should be unique for your current session or you will be overwriting the previous screen definition.

All data pertaining to this specific screen will be stored under the identifier we supplied.

Action ID

Action Handle

Screen Name

Active Screen

Screen Visibility

Screen Type

Standard

The standard screen is the default screen used. It has an empty area on the left side where a background image can go. The standard screen in DSF (Dock Screen Framework) does not come with a background image. If you need this you will need to make your own Screen definition (There will be an example of how to do this later)

ItemPicker

The CustomPicker screen is used for presenting arbitrary lists of information. It is a very flexible screens and excellent for presenting the player with complex data or a wide variety of choices.

CustomPicker

The ItemPicker is used for displaying items on objects (ships or stations)

Canvas

The Canvas screen is used for free form drawing and text display.

Pane Type

The default DSF (Dock Screen Framework) screens have 3 standard panes defined

Default

TODO

TextInput

TODO

Counter

TODO

DSF Expression

DSF (Dock Screen Framework) expressions are a central type of data in DSF (Dock Screen Framework). These are values that will be evalated inside DSF (Dock Screen Framework) before being used. An example of types of values and their evaluated value follows:

1                           -> 1
"String"                    -> "String"
'literal                    -> 'literal
'(block "Literal Code")     -> "Literal Code"
(lambda nil "Lambda")       -> "Lambda"

Translated, that means, any atoms (numbers, strings and literals) that can not be evaluated are returned as-is. Any literal that can be evaluated will be, and the resulting value will be used. Lambdas will be evaluated and their return value used.

Use dsf/eval if you want to evaluate something in this way.

Nested Screen

Space Object

gPlayerShip

gSource

Item Criteria

UNID

Closures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment