User Interfaces
===============

Introduction
------------
Asciimatics provides a `widgets` sub-package that allows you to
create interactive text user interfaces.  At its heart, the logic is quite
simple, reusing concepts from standard web and desktop GUI frameworks.

1. The basic building block for your text UI is a `Widget`.  There is a set
   of standard ones provided by asciimatics, but you can create a custom set if
   needed.  The basic set has strong parallels with simple web input forms -
   e.g. buttons, check boxes, etc.
2. The `Widgets` need to be arranged on the `Screen` and rearranged whenever it
   is resized.  The `Layout` class handles this for you.  You just need to add
   your `Widgets` to one.
3. You then need to display the `Layouts`.  To do this, you must add them to a
   `Frame`.  This class is an `Effect` and so can be used in any `Scene`
   alongside any other `Effect`. The `Frame` will draw any parts of the
   `Layouts` it contains that are visible within its boundaries.  The net result
   is that it begins to look a bit like a window in GUI frameworks.

And that's it!  You can set various callbacks to get triggered when key events
occur - e.g. changes to values, buttons get clicked, etc. - and use these to
trigger your application processing.  For an example, see the contact_list.py
sample provided - which will look a bit like this:

.. image:: contacts.png
    :alt: Screen shot of the contacts list sample

Common keys
~~~~~~~~~~~
When navigating around a Frame, you can use the following keys.

===================  ==========================================================
Key                  Action
===================  ==========================================================
Tab                  Move to the next Widget in the Frame
Backtab (shift+tab)  Move to the previous Widget in the Frame
Up arrow             Move to the Widget above the current focus in the same
                     column.
Down arrow           Move to the Widget below the current focus in the same
                     column.
Left arrow           Move to the last Widget in the column to the left of
                     the column with the current input focus.
Right arrow          Move to the first Widget in the column to the right of
                     the column with the current input focus.
Space or Return      Select the current Widget - e.g. click a Button.
===================  ==========================================================

Note that the cursor keys will not traverse between Layouts.  In addition,
asciimatics will not allow you to navigate to a disabled widget.

Inside the standard text edit Widgets, the cursor key actions are overridden and
instead they will allow you to for navigate around the editable text as you
would expect.  In addition you can also use the following extra keys.

===================  ==========================================================
Key                  Action
===================  ==========================================================
Home/End             Move to the start/end of the current line.
Delete               Delete the character under the cursor.
Backspace            Delete the character before the cursor.
===================  ==========================================================

Tab/backtab will still navigate out of text edit Widgets, but the rest of the
keys (beyond those described above) will simply add to the text in the current
line.

Model/View Design
-----------------
Before we jump into exactly what all the objects are and what they do for you,
it is important to understand how you must put them together to make the best
use of them.

The underlying Screen/Scene/Effect design of asciimatics means that objects
regularly get thrown away and recreated - especially when the Screen is
resized.  It is therefore vital to separate your data model from your code to
display it on the screen.

This split is often (wrongly) termed the `MVC
<https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller>`__ model,
but a more accurate description is `Separated Presentation
<http://martinfowler.com/eaaDev/SeparatedPresentation.html>`__.  No matter what
term you use, the concept is easy: use a separate class to handle your
persistent data storage.

In more concrete terms, let's have a closer look at the contact_list sample.
This consists of 3 basic classes:

1. `ContactModel`: This is the model.  It stores simple contact details in a
   sqlite in-memory database and provides a simple create/read/update/delete
   interface to manipulate any contact.  Note that you don't have to be this
   heavy-weight with the data storage; a simple class to wrap a list of
   dictionaries would also suffice - but doesn't look as professional for a
   demo!

.. container:: toggle

    .. container:: header

        **Show/hide code**

    .. code-block:: python

        class ContactModel(object):
            def __init__(self):
                # Create a database in RAM
                self._db = sqlite3.connect(':memory:')
                self._db.row_factory = sqlite3.Row

                # Create the basic contact table.
                self._db.cursor().execute('''
                    CREATE TABLE contacts(
                        id INTEGER PRIMARY KEY,
                        name TEXT,
                        phone TEXT,
                        address TEXT,
                        email TEXT,
                        notes TEXT)
                ''')
                self._db.commit()

                # Current contact when editing.
                self.current_id = None

            def add(self, contact):
                self._db.cursor().execute('''
                    INSERT INTO contacts(name, phone, address, email, notes)
                    VALUES(:name, :phone, :address, :email, :notes)''',
                                          contact)
                self._db.commit()

            def get_summary(self):
                return self._db.cursor().execute(
                    "SELECT name, id from contacts").fetchall()

            def get_contact(self, contact_id):
                return self._db.cursor().execute(
                    "SELECT * from contacts where id=?", str(contact_id)).fetchone()

            def get_current_contact(self):
                if self.current_id is None:
                    return {}
                else:
                    return self.get_contact(self.current_id)

            def update_current_contact(self, details):
                if self.current_id is None:
                    self.add(details)
                else:
                    self._db.cursor().execute('''
                        UPDATE contacts SET name=:name, phone=:phone, address=:address,
                        email=:email, notes=:notes WHERE id=:id''',
                                              details)
                    self._db.commit()

            def delete_contact(self, contact_id):
                self._db.cursor().execute('''
                    DELETE FROM contacts WHERE id=:id''', {"id": contact_id})
                self._db.commit()

2. `ListView`: This is the main view.  It queries the `ContactModel` for the
   list of known contacts and displays them in a list, complete with some extra
   buttons to add/edit/delete contacts.

.. container:: toggle

    .. container:: header

        **Show/hide code**

    ..  code-block:: python

        class ListView(Frame):
            def __init__(self, screen, model):
                super(ListView, self).__init__(screen,
                                               screen.height * 2 // 3,
                                               screen.width * 2 // 3,
                                               on_load=self._reload_list,
                                               hover_focus=True,
                                               title="Contact List")
                # Save off the model that accesses the contacts database.
                self._model = model

                # Create the form for displaying the list of contacts.
                self._list_view = ListBox(
                    Widget.FILL_FRAME,
                    model.get_summary(), name="contacts", on_select=self._on_pick)
                self._edit_button = Button("Edit", self._edit)
                self._delete_button = Button("Delete", self._delete)
                layout = Layout([100], fill_frame=True)
                self.add_layout(layout)
                layout.add_widget(self._list_view)
                layout.add_widget(Divider())
                layout2 = Layout([1, 1, 1, 1])
                self.add_layout(layout2)
                layout2.add_widget(Button("Add", self._add), 0)
                layout2.add_widget(self._edit_button, 1)
                layout2.add_widget(self._delete_button, 2)
                layout2.add_widget(Button("Quit", self._quit), 3)
                self.fix()

            def _on_pick(self):
                self._edit_button.disabled = self._list_view.value is None
                self._delete_button.disabled = self._list_view.value is None

            def _reload_list(self):
                self._list_view.options = self._model.get_summary()
                self._model.current_id = None

            def _add(self):
                self._model.current_id = None
                raise NextScene("Edit Contact")

            def _edit(self):
                self.save()
                self._model.current_id = self.data["contacts"]
                raise NextScene("Edit Contact")

            def _delete(self):
                self.save()
                self._model.delete_contact(self.data["contacts"])
                self._reload_list()

            @staticmethod
            def _quit():
                raise StopApplication("User pressed quit")

3. `ContactView`: This is the detailed view.  It queries the `ContactModel` for
   the current contact to be displayed at the start (which may be none if the
   user is adding a contact) and writes any changes back to the model when the
   user clicks OK.

.. container:: toggle

    .. container:: header

        **Show/hide code**

    .. code-block:: python

        class ContactView(Frame):
            def __init__(self, screen, model):
                super(ContactView, self).__init__(screen,
                                                  screen.height * 2 // 3,
                                                  screen.width * 2 // 3,
                                                  hover_focus=True,
                                                  title="Contact Details")
                # Save off the model that accesses the contacts database.
                self._model = model

                # Create the form for displaying the list of contacts.
                layout = Layout([100], fill_frame=True)
                self.add_layout(layout)
                layout.add_widget(Text("Name:", "name"))
                layout.add_widget(Text("Address:", "address"))
                layout.add_widget(Text("Phone number:", "phone"))
                layout.add_widget(Text("Email address:", "email"))
                layout.add_widget(TextBox(5, "Notes:", "notes", as_string=True))
                layout2 = Layout([1, 1, 1, 1])
                self.add_layout(layout2)
                layout2.add_widget(Button("OK", self._ok), 0)
                layout2.add_widget(Button("Cancel", self._cancel), 3)
                self.fix()

            def reset(self):
                # Do standard reset to clear out form, then populate with new data.
                super(ContactView, self).reset()
                self.data = self._model.get_current_contact()

            def _ok(self):
                self.save()
                self._model.update_current_contact(self.data)
                raise NextScene("Main")

            @staticmethod
            def _cancel():
                raise NextScene("Main")

Displaying your UI
------------------
OK, so you want to do something a little more interactive with your user.  The
first thing you need to decide is what information you want to get from them
and how you're going to achieve that.  In short:

1. What data you want them to be able to enter - e.g. their name.
2. How you want to break that down into fields - e.g. first name, last name.
3. What the natural representation of those fields would be - e.g. text strings.

At this point, you can now decide which Widgets you want to use.  The standard
selection is as follows.

========================    ====================================================
Widget type                 Description
========================    ====================================================
:py:obj:`.Button`           Action buttons - e.g. ok/cancel/etc.
:py:obj:`.CheckBox`         Simple yes/no tick boxes.
:py:obj:`.Divider`          A spacer between widgets (for aesthetics).
:py:obj:`.Label`            A label for a group of related widgets.
:py:obj:`.ListBox`          A list of possible options from which the user can
                            select one value.
:py:obj:`.RadioButtons`     A list of radio buttons.  These allow the user to
                            select one value from a list of options.
:py:obj:`.Text`             A single line of editable text.
:py:obj:`.TextBox`          A multi-line box of editable text.
========================    ====================================================

Asciimatics will automatically arrange these for you with just a little extra
help.  All you need to do is decide how many columns you want for your fields
and which fields should be in which columns.  To tell asciimatics what to do
you create a `Layout` (or more than one if you want a more complex
structure where different parts of the screen need differing column counts) and
associate it with the `Frame` where you will display it.

For example, this will create a Frame that is 80x20 characters and define 4
columns that are each 20 columns wide:

.. code-block:: python

    frame = Frame(screen, 80, 20, has_border=False)
    layout = Layout([1, 1, 1, 1])
    frame.add_layout(layout)

Once you have a Layout, you can add Widgets to the relevant column.  For
example, this will add a button to the first and last columns:

.. code-block:: python

    layout2.add_widget(Button("OK", self._ok), 0)
    layout2.add_widget(Button("Cancel", self._cancel), 3)

If you want to put a standard label on all your input fields, that's fine too;
asciimatics will decide how big your label needs to be across all fields in the
same column and then indent them all to create a more aesthetically pleasing
layout.  For example, this will provide a single column with labels for each
field, indenting all of the fields to the same depth:

.. code-block:: python

    layout = Layout([100])
    self.add_layout(layout)
    layout.add_widget(Text("Name:", "name"))
    layout.add_widget(Text("Address:", "address"))
    layout.add_widget(Text("Phone number:", "phone"))
    layout.add_widget(Text("Email address:", "email"))
    layout.add_widget(TextBox(5, "Notes:", "notes", as_string=True))

Disabling widgets
~~~~~~~~~~~~~~~~~
Any widget can be disabled by setting the ``disabled`` property.  When this
is ``True``, asciimatics will redraw the widget using the 'disabled' colour
palette entry and prevent the user from selecting it or editing it.

It is still possible to change the widget programmatically, though.  For
example, you can still change the ``value`` of a disabled widget.

Layouts in more detail
~~~~~~~~~~~~~~~~~~~~~~
If you need to do something more complex, you can use multiple Layouts.
Asciimatics uses the following logic to determine the location of Widgets.

1.  The `Frame` owns one or more `Layouts`.  The `Layouts` stack one above each
    other when displayed - i.e. the first `Layout` in the `Frame` is above the
    second, etc.
2.  Each `Layout` defines some horizontal constraints by defining columns as a
    proportion of the full `Frame` width.
3.  The `Widgets` are assigned a column within the `Layout` that owns them.
4.  The `Layout` then decides the exact size and location to make each
    `Widget` best fit the visible space as constrained by the above.

For example::

    +------------------------------------------------------------------------+
    |Screen..................................................................|
    |........................................................................|
    |...+----------------------------------------------------------------+...|
    |...|Frame                                                           |...|
    |...|+--------------------------------------------------------------+|...|
    |...||Layout 1                                                      ||...|
    |...|+--------------------------------------------------------------+|...|
    |...|+------------------------------+-------------------------------+|...|
    |...||Layout 2                      |                               ||...|
    |...|| - Column 1                   | - Column 2                    ||...|
    |...|+------------------------------+-------------------------------+|...|
    |...|+-------------+---------------------------------+--------------+|...|
    |...||Layout 3     | < Widget 1 >                    |              ||...|
    |...||             | ...                             |              ||...|
    |...||             | < Widget N >                    |              ||...|
    |...|+-------------+---------------------------------+--------------+|...|
    |...+----------------------------------------------------------------+...|
    |........................................................................|
    +------------------------------------------------------------------------+

This consists of a single `Frame` with 3 `Layouts`.  The first is a single,
full-width column, the second has two 50% width columns and the third consists
of 3 columns of relative size 25:50:25.  The last actually contains some Widgets
in the second column (though this is just for illustration purposes as we'd
expect most Layouts to have some Widgets in them).

Filling the space
~~~~~~~~~~~~~~~~~
Once you've got the basic rows and columns for your UI sorted, you may want to
use some strategic spacing.  At the simplest level, you can use the previously
mentioned :py:obj:`.Divider` widget to create some extra vertical space or
insert a visual section break.

Moving up the complexity, you can pick different sizes for your Frames based
on the size of your current Screen.  The Frame will be recreated when the 
screen is resized and so you will use more or less real estate appropriately.

Finally, you could also tell asciimatics to use an object to fill any
remaining space.  This allows for the sort of UI like you'd see in applications
like top where you have a fixed header or footer, but then a variably sized
part that contains the data to be displayed.

You can achieve this in 2 ways:

1. You can tell a Layout to fill any remaining space in the Frame using
   `fill_frame=True` on construction.
2. You can tell some Widgets to fill any remaining space in the Frame using
   a height of `Widget.FILL_FRAME` on construction.

These two methods can be combined to tell a Layout to fill the Frame and a
Widget to fill this Layout.  See the ListView class in the contact_list demo
code.

.. warning::

    Note that you can only have one Layout and/or Widget that fills the Frame.
    Trying to set more than one will be rejected.

Large forms
~~~~~~~~~~~
If you have a very large form, you may find it is too big to fit into a
standard screen.  This is not a problem.  You can keep adding your Widgets to
your Layout and asciimatics will automatically clip the content to the space
available and scroll the content as required.

If you do this, it is recommended that you set `has_border=True` on the Frame
so that the user can use the scroll bar provided to move around the form.

Colour schemes
~~~~~~~~~~~~~~
The colours for any Widget are determined by the `palette` property of
the Frame that contains the Widget.  If desired, it is possible to have a
different palette for every Frame, however your users may prefer a more
consistent approach.

The palette is just a simple dictionary to map Widget components to a
colour tuple.  The following table shows the required keys.

========================  =====================================================
Key                       Usage
========================  =====================================================
"background"              Frame background
"disabled"                Any disabled Widget
"label"                   Widget labels
"borders"                 Frame border and Divider Widget
"scroll"                  Frame scroll bar 
"title"                   Frame title
"edit_text"               Text and TextBox
"focus_edit_text"         Text and TextBox with input focus     
"button"                  Buttons
"focus_button"            Buttons with input focus
"control"                 Checkboxes and RadioButtons
"selected_control"        Checkboxes and RadioButtons when selected
"focus_control"           Checkboxes and RadioButtons with input focus
"selected_focus_control"  Checkboxes and RadioButtons with both
"field"                   Value of an option for a Checkbox, RadioButton or
                          Listbox
"selected_field"          As above when selected
"focus_field"             As above with input focus
"selected_focus_field"    As above with both
========================  =====================================================

Getting values
--------------
Now that you have a `Frame` with some `Widgets` in it and the user is filling
them in, how do you find out what they entered?  There are 2 basic ways to do
this:

1. You can query each Widget directly, using the `value` property.  This returns
   the current value the user has entered at any time (even when the Frame is
   not active).  Note that it may be `None` for those `Widgets` where there is
   no value - e.g. buttons.
2. You can query the `Frame`by looking at the `data` property.  This will return
   the value for every Widget in the former as a dictionary, using the Widget
   `name` properties for the keys. 

For example:

.. code-block:: python

    # Form definition
    layout = Layout([100])
    frame.add_layout(layout)
    layout.add_widget(Text("Name:", "name"))
    layout.add_widget(Text("Address:", "address"))
    layout.add_widget(TextBox(5, "Notes:", "notes", as_string=True))

    # Sample frame.data after user has filled it in.
    {
        "name": "Peter",
        "address": "Somewhere on earth",
        "notes": "Some multi-line\ntext from the user."
    }

Input focus
~~~~~~~~~~~
As mentioned in the explanation of colour palettes, asciimatics has the concept
of an input focus.  This is the Widget that will take any input from the
keyboard.  Assuming you are using the default palette, the Widget with the
input focus will be highlighted.  You can move the focus using the cursor keys,
tab/backtab or by using the mouse.

The exact way that the mouse affects the focus depends on a combination of the
capabilities of your terminal/console and the settings of your Frame.  At a
minimum, clicking on the Widget will always work.  If you specify
`hover_focus=True` and your terminal supports reporting mouse move events, just
hovering over the Widget with the mouse pointer will move the focus.

Global key handling
~~~~~~~~~~~~~~~~~~~
In addition to mouse control to switch focus, you can also set up a global
event handler to navigate your forms.  This is useful for keyboard shortcuts
- e.g. Ctrl+Q to quit your program.

To set up this handler, you need to pass it into your screen on the `play()`
Method.  For example

.. code-block:: python

    # Event handler for global keys
    def global_shortcuts(event):
        if isinstance(event, KeyboardEvent):
            c = event.key_code
            # Stop on ctrl+q or ctrl+x
            if c in (17, 24):
                raise StopApplication("User terminated app")

    # Pass this to the screen...
    screen.play(scenes, unhandled_input=global_shortcuts)

.. warning::

    Note that the global handler is only called if the focus does not process
    the event.  Some widgets - e.g. TextBox - take any printable text and so
    the only keys that always get to this handler are the control codes.
    Others will sometimes get here depending on the type of Widget in focus.

By default, the global handler will do nothing if you are playing any Scenes
containing a Frame.  Otherwise it contains the top-level logic for skipping to
the next Scene (on space or enter), or exiting the program (on Q or X).

Dealing with Ctrl+C and Ctrl+Z
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A lot of modern UIs want to be able to use Ctrl+C/Z to do something other than
kill the application.  The problem for Python is that this normally triggers a
`KeyboardInterrupt`  - which typically kills the application - or causes the
operating system to suspend the process (on UNIX variants).

If you want to prevent this and use Ctrl+C/Z for another purpose, you can tell
asciimatics to catch the low-level signals to prevent these interrupts from
being generated (and so return the keypress to your application).  This is done
by specifying `catch_interrupt=True` when you create the `Screen` by calling
:py:meth:`.wrapper`.

Dealing with Ctrl+S
~~~~~~~~~~~~~~~~~~~
Back in the days when terminals really were separate machines connected over
wires to a computer, it was necessary to be able to signal that the terminal
needed time to catch up.  This was done using software flow control, using the
Ctrl+S/Ctrl+Q control codes to tell the computer to stop/restart sending text.

These days, it's not really necessary, but is still a supported feature on most
terminals.  On some systems you can switch this off so you get access to Ctrl+S,
but it is not possible on them all.  See :ref:`ctrl-s-issues-ref` for details
on how to fix this.

Flow of control
---------------
By this stage you should have a program with some Frames and can extract what
your user has entered into any of them.  But how do you know when to act and
move between Frames?  The answer is callbacks and exceptions.

Callbacks
~~~~~~~~~
A callback is just a function that you pass into another function to be
called when the associated event occurs.  In asciimatics, they can usually be
identified by the fact that they start with `on` and correspond to a
significant input action from the user, e.g. `on_click`.

When writing your application, you simply need to decide which events you
want to use to trigger some processing and create apropriate callbacks.  The
most common pattern is to use a `Button` and define an `on_click` callback.

In addition, there are other events that can be triggered when widget values
change.  These can be used to provide dynamic effects like enabling/disabling
Buttons based on the current value of another Widget.

Exceptions
~~~~~~~~~~
Asciimatics uses exceptions to tell the animation engine to move to a new Scene
or stop the whole process.  Other exceptions are not caught and so can still be
used as normal.  The details for the new exceptions are as follows:

1. :py:obj:`.StopApplication` - This exception will stop the animation engine
   and return flow to the function that called into the Screen.
2. :py:obj:`.NextScene` - This exception tells the animation engine to move to
   a new Scene.  The precise Scene is determined by the name passed into the
   exception.  If none is specified, the engine will simply roundi robin to the
   next available Scene.

Note that the above logic requires each Scene to be given a unique name on
construction.  For example:

.. code-block:: python

    # Given this scene list...
    scenes = [
        Scene([ListView(screen, contacts)], -1, name="Main"),
        Scene([ContactView(screen, contacts)], -1, name="Edit Contact")
    ]
    screen.play(scenes)

    # You can use this code to move back to the first scene at any time...
    raise NextScene("Main")

Dynamic scenes
--------------
With everything above, you should now be able to create a fully working text
UI.  There are just a few more final touches to consider.  These all touch on
dynamically changing or reconstructing your Scene.

Adding other effects
~~~~~~~~~~~~~~~~~~~~
Since Frames are just another Effect, they can be combined with any other Effect
in a Scene.  For example, this will put a simple input form over the top of the
animated Julia set Effect:


.. code-block:: python

    scenes = []
    effects = [
        Julia(screen),
        InputFormFrame(screen)
    ]
    scenes.append(Scene(effects, -1))
    screen.play(scenes)

The ordering is important.  The effects at the bottom of the list are at the top
of the screen Z order and so will be displayed in preference to those lower in
the Z order (i.e. those earlier in the list).

Pop-up dialogs
~~~~~~~~~~~~~~
Along a similar line, you can also add a :py:obj:`.PopUpDialog` to your Scenes
at any time.  These consist of a single text message and a set of buttons that
you can define when creating the dialog.

Owing to restrictions on how objects need to be rebuilt when the screen is
resized, these should be limited to simple are confirmation or error cases -
e.g. "Are you sure you want to quit?"  For more details on the restrictions,
see the next section.

Screen resizing
~~~~~~~~~~~~~~~
If you follow the standard application mainline logic as found in all the
sample code, your application will want to resize all your Effects and Widgets
whenever the user resizes the terminal.  To do this you need to get a new
Screen then rebuild a new set of objects to use that Screen.

Sound like a bit of a drag, huh?  This is why it is recommended that you
separate your presentation from the rest of your application logic.  If you do
it right you will find that it actually just means you go through exactly the
same initialization path as you did before to create your Scenes in the first
place.  There are a couple of gotchas, though.

First, you need to make sure that asciimatics will exit and recreate a new
Screen when the terminal is resized.  You do that with this boilerplate code
that is in most of the samples.

.. code-block:: python

    def main(screen, scene):
        # Define your Scenes here
        scenes = ...

        # Run your program
        screen.play(scenes, stop_on_resize=True, start_scene=scene)

    last_scene = None
    while True:
        try:
            Screen.wrapper(main, arguments=[last_scene])
            sys.exit(0)
        except ResizeScreenError as e:
            last_scene = e.scene

This will allow you to decide how all your UI should look whenever the screen
is resized and will restart at the Scene that was playing at the time of the
resizing.

Restoring state
~~~~~~~~~~~~~~~
Recreating your view is only half the story.  Now you need to ensure that you
have restored any state inside your application - e.g. any dynamic effects are
added back in, your new Scene has the same internal state as the old, etc.
Asciimatics provides a standard interface (the `clone` method) to help you out
here.

When the running `Scene` is resized (and passed back into the Screen as the
start scene), the new `Scene` will run through all the `Effects` in the old
copy looking for any with a `clone` method.  If it finds one, it will call it
with 2 parameters: the new `Screen` and the new `Scene` to own the cloned
`Effect`.  This allows you to take full control of how the new `Effect` is
recreated.  Asciimatics uses this interface in 2 ways by default:

1.  To ensure that any :py:obj:`~.Frame.data` is restored in the new `Scene`.
2.  To duplicate any dynamically added :py:obj:`.PopUpDialog` objects in the
    new `Scene`.

You could override this processing to handle your own custom cloning logic.  The
formal definition of the API is defined as follows.

.. code-block:: python

    def clone(self, screen, scene):
        """
        Create a clone of this Effect into a new Screen.

        :param screen: The new Screen object to clone into.
        :param scene: The new Scene object to clone into.
        """

Reducing CPU usage
~~~~~~~~~~~~~~~~~~
It is the nature of text UIs that they don't need to refresh anywhere near as
often as a full-blown animated Scene.  Asciimatics therefore optimizes the
refresh rate when only Frames are being displayed on the Screen.

However, there are some widgets that can reduce the need for animation even
further by not requesting animation updates (e.g. for a blinking cursor).  If
this is an issue for your application, you can specify ``reduce_cpu=True`` when
constructing your Frames.  See contact_list.py for an example of this.

Custom widgets
--------------
To develop your own widget, you need to define a new class that inherits from
:py:obj:`.Widget`.  You then have to implement the following functions.

1. :py:meth:`~.Widget.reset` - This is where you should reset any state for your
   widget.  It gets called whenever the owning Frame is initialised, which can
   be when it is first displayed, when the user moves to a new Scene or when the
   screen is resized.
2. :py:meth:`~.Widget.update` - This is where you should put the logic to draw
   your widget.  It gets called every time asciimatics needs to redraw the
   screen (and so should always draw the entire widget).
3. :py:meth:`~.Widget.process_event` - This is where you should put your code
   to handle mouse and keyboard events.
4. :py:obj:`~.Widget.value` - This must return the current value for the
   widget.
5. :py:meth:`~.Widget.required_height` - This returns the minimum required
   height for your widget.  It is used by the owning Layout to determine the
   size and location of your widget.

With these all defined, you should now be able to add your new custom widget
to a Layout like any of the standard ones delivered in this package.
