Component documentation for developers
======================================

This file is heavily work in progress. Use at your own risk.
Components might eat your cat, dog and girlfriend all at once!

About components
----------------

Pyntor is not a slideshow program by itself, it merely manages to load
the correct components for each part of a presentation. As it is based
on Pygame, all component rendering and interactivity also requires
Pygame knowledge.

In the end, every component results in a small *.py file which is then
placed into the 'components' directory of Python.

Component structure
-------------------

Each component must have at least one global variable ("component")
which is an object of class Component. Additional global variables
should be present, but are not required.

Global variables are:
- 'component', a Component object which handles rendering and user input
- 'metainfo', a dictionary containing some meta information about the component
- 'documentation', plain-text documentation as a multi-line string
- 'parameters', a tuple describing the interface to this component

These global variables will now be described in detail.

Meta information:
 Information about a component's author, licence etc. should be present here.
 The variable is a hash map, and the following keys are currently defined:
 - "author"
 - "authoremail"
 - "licence"
 - "homepage"
 - "version"

Parameters:
 Pyntor versions prior to 0.6 only accepted unnamed parameters to components
 as a list. However, with a growing number of parameters and components, one
 is easily lost.
 Now it is possible to have "new-style" parameters which are passed as a
 hash map to the component's initialisation method. This mode is automatically
 activated if the 'parameters' global variable is present.
 It should contain a list of possible parameters, together with their
 description and default values in case the option is not present.
 An example follows:

 parameters = (
  ("duration", "Duration of the cookie display, in seconds", 10),
  ("cookiefile", "Cookie file to use", None),
  (None, "Additional parameters", [])
 )

 A default value of None means that the option is required.
 A parameter value of None means that a variable number of arguments is
 accepted in addition to the named arguments. This is called varargs, and
 its default must always be the empty list([]).

 Parameters can still be given unnamed to the component - they will then be
 assigned to the next previously unassigned named parameter. If there are
 too many of them, they will end up in the varargs list, or in case no
 varargs is supported, an error will be given.

 To get back to the example above, the following would happen:
 - <component> -cookiefile foo: valid
 - <component>: invalid, since cookiefile is missing
 - <component> foo: invalid, since foo relates to duration (earlier in list)
 - <component> 5 foo bar baz: valid, with [bar, baz] being the varargs

API documentation for class Component
-------------------------------------

class Component:
 attributes:
  - disabled (unset by default)
  - pages (must be set to number of pages by the component)
  - fulltext (dictionary containing text for full-text search)
  - headings (like fulltext, but for headings only)
 def init(options)
 def render(screen, page, globalpage)
 def interactive(event)

init:
 This is called whenever the component is used in a presentation.
 It gets a list of options (old-style) or a dictionary of options (new-style)
 which the component can use to initialise itself.
 If there is an error, for example a file is missing or a parameter is
 of the wrong type, the component should print an error message and,
 the important part, set 'self.disabled' to true.
 Pyntor will then know that something went wrong. The same can be done in
 the other methods as well.
 At the end, the component should have set 'self.pages' to indicate how
 many pages it needs with the given options.
 Optionally, in the case of text-centric components, 'self.fulltext' and
 'self.headings' should be created as dictionaries, containing the full
 text for each of the pages.
 Pyntor will use this information for its fulltext search feature.

render:
 This method is only needed for components which render graphics.
 It gets the global pygame screen where to render, and the information about
 which of its own pages is active (usually the 1st, unless it's a slide
 display component) and which is the corresponding global page number.
 Pyntor ensures that the display is updated by itself, the the component
 should not call update() or flip(), or it might lead to flicker.

interactive:
 This method is only needed for components which accept user input.
 It gets as its parameter a pygame event object, which should be evaluated.
 Pyntor checks to see what the return value of 'interactive' is. One
 of the following values is possible:
 - "reload": Pyntor should display the current page again
 - "previous": Pyntor should go one page back
 - "next": Pyntor should proceed
 - "quit": Pyntor should quit
 Note that previous/next might well lead into other components becoming
 active!

Component interaction
---------------------

Components are automatically grouped by Pyntor depending on whether they
render some graphics, accept user input, or both. A grouping transition
appears whenever an interactivity component is followed by a render
component. Or, expressed in other terms, render components followed by
components doing both followed by interactivity components are grouped
together.
The following gives an example:

background.py: [render]
slide.py:      [render][interactive]
marker.py:             [interactive]

Page numbers are given not to each component, but to each group:

[r][r+i][i]|[r][i]|[r+i][i]|[r+i]|[r+i]|[r]
     1     |  2   |   3    |  4  |  5  | -

Of course, whenever a render component indicates that it renders multiple
pages, the numbers will reflect this.

Global gadgets
--------------

Pyntor supports some global gadgets which do not need to be implemented by
all components again.

These are currently:
 - a clock (activate with "T")
 - page/progress display - not available yet!
 - page switcher (activate with "P")
 - full-text search (activate with "S")

All key combinations which are not detected by Pyntor are passed to the
currently active component. No mouse events are currently passed, they are
all used by Pyntor itself, but this might change.

Screenshots and exports
-----------------------

It is important to know that when Pyntor runs in export mode (--export), some
restrictions apply to the component. Well-designed components should always
work in export mode, too.
The components should not handle any interactivity in the render() method.
They should also not assume that the pygame display is active, because it is
actually not. In export mode, components render to off-screen images.

Future versions
---------------

Pyntor always tries to support old component formats, from version 0.5 on.
However the format might break again one day. It's important to submit new
components to the Pyntor component repository, so they can be tested with
new versions as well.

