summaryrefslogtreecommitdiff
path: root/docs/manual/widgetmethods.txt
blob: 197983788d99938c7d5564ec5813860ce47b662c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
.. _widget-methods:

Widget Methods
--------------

Widgets in Urwid are easiest to create by extending other widgets. If you are
making a new type of widget that can use other widgets to display its content,
like a new type of button or control, then you should start by extending
:class:`WidgetWrap` and passing the display widget to its constructor.

This section describes the :class:`Widget` interface in detail and is useful if
you're looking to modify the behavior of an existing widget, build a new widget
class from scratch or just want a better understanding of the library.

One design choice that stands out is that widgets in Urwid typically have no
size. Widgets don't store the size they will be displayed at, and instead are
passed that information when they need it.

This choice has some advantages:

* widgets may be reused in different locations
* reused widgets only need to be rendered once per size displayed
* widgets don't need to know their parents
* less data to store and update
* no worrying about widgets that haven't received their size yet
* same widgets could be displayed at different sizes to different users
  simultaneously

It also has disadvantages:

* difficult to determine a widget's size on screen
* more parameters to parse
* duplicated size calculations across methods

For determining a widget's size on screen it is possible to look up the size(s)
it was rendered at in the :class:`CanvasCache`. There are plans to address some
of the duplicated size handling code in the container widgets in a future Urwid
release.

The same holds true for a widget's focus state, so that too is passed in to
functions that need it.

The :class:`Widget` base class has some metaclass magic that creates a
:attr:`__super` attribute for calling your superclass: :attr:`self.__super` is
the same as the usual ``super(MyClassName, self)``.

.. TODO: what to do with these references?

`Widget base class reference <http://excess.org/urwid/reference.html#Widget>`_

`Widget interface reference <http://excess.org/urwid/reference.html#Widget_interface_definition>`_

.. class:: Widget()

   .. method:: sizing()

      Return a set of the sizing modes this widget supports, one or more of
      *FLOW*, *BOX* or *FIXED*.

      The :class:`Widget` base class defines this method as ``return
      self._sizing``, so if your widget's sizing modes never change you can
      define :attr:`_sizing` in your class definition, and not bother
      overriding this method.

      If you inherit from :class:`FlowWidget`, :class:`BoxWidget` or
      :class:`FixedWidget`, :attr:`_sizing` is already defined for you as
      ``set([FLOW])``, ``set([BOX])`` or ``set([FIXED])``, respectively.

      If *FLOW* is among the values returned then your other methods must be
      able to accept a single-element tuple ``(maxcol,)`` to their ``size``
      parameters, and the :meth:`rows` method must be defined. Flow widgets are
      given a certain column size by their parents and may take as many rows as
      they require to display their content.

      If *BOX* is among the values returned then your other methods must be
      able to accept a two-element tuple ``(maxcol, maxrow)`` to their size
      paramters. Box widgets are given their size by their parents and must
      display their contents in the space given.

      If *FIXED* is among the values returned then your other methods must be
      able to accept an empty tuple `()` to their size parameters, and the
      :meth:`pack` method must be defined. Fixed widgets have a fixed number of
      rows and columns, and so are the least flexible and must me handled
      carefully.

   .. method:: render(size, focus=False)

      Render the widget content and return it as a :class:`Canvas` subclass.
      :class:`Text` widgets return a :class:`TextCanvas` (arbitrary text and
      attributes), :class:`SolidFill` widgets return a :class:`SolidCanvas` (a
      single character repeated across the whole surface) and container widgets
      return a :class:`CompositeCanvas` (one or more other canvases arranged
      arbitrarily).

      If *focus* is ``False``, the returned canvas may not have a cursor
      position set.

      There is some metaclass magic in the Widget base class that causes the
      result of this method to be cached by :class:`CanvasCache`, and later
      calls will automatically look up the value in the cache first. The class
      variable :attr:`ignore_focus` may be defined and set to ``True`` if this
      widget renders the same regardless of the value of the `focus` parameter.

      Any time the content of your widget changes you must call
      :meth:`_invalidate` to remove any cached canvases, or your widget may not
      change as you expect.

   .. method:: selectable()

      Return `True` if this is a widget that is designed to take the focus,
      `False` otherwise.

      The :class:`Widget` base class defines this method as ``return
      self._selectable`` and :attr:`_selectable` is defined as ``False``, so
      you may do nothing if your widget is not selectable, or if your widget is
      always selectable just set your :attr:`_selectable` class variable to
      ``True``.
      
      If this method returns ``True`` then the :meth:`keypress` method must be
      implemented.
      
      Returning ``False`` does not guarantee that this widget will never be in
      focus, only that this widget will usually be skipped over when changing
      focus. It is still possible for non selectable widgets to have the focus
      (typically when there are no other selectable widgets visible).

   .. method:: rows(size, focus=False)

      Return the number of rows required for this widget given a number of
      columns in size.
      
      This is the method flow widgets use to communicate their size to other
      widgets without having to render a canvas. This should be a quick
      calculation as this function may be called a number of times in normal
      operation. If your implementation may take a long time you should add
      your own caching here.
      
      There is some metaclass magic in the :class:`Widget` base class that
      causes the result of this function to be computed from any canvas cached
      by :class:`CanvasCache`, so if your widget has been rendered you may not
      receive calls to this function. The class variable :attr:`ignore_focus`
      may be defined and set to ``True`` if this widget renders the same size
      regardless of the value of the *focus* parameter.

   .. method:: pack(size, focus=False)

      Return a "packed" size ``(maxcol, maxrow)`` for this widget, a minimum
      size where all content could still be displayed. Fixed widgets must
      implement this method and return their size when ``()`` is passed as the
      *size* parameter.
      
      The :class:`Widget` base class definition of this method returns the
      *size* passed, or the *maxcol* passed and the value of :meth:`rows` as
      the *maxrow* when ``(maxcol,)`` is passed as the *size* parameter.
      
      This is a new method that hasn't been fully implemented across the
      standard widget types. In particular it has not yet been implemented for
      container widgets.
      
      :class:`Text` widgets `have implemented this method
      <http://excess.org/urwid/reference.html#Text-pack>`_. You can use
      :meth:`pack` to calculate the minumum columns and rows required to
      display a text widget without wrapping, or call it iteratively to
      calculate the minimum number of columns required to display the text
      wrapped into a target number of rows.

   .. method:: keypress(size, key)

      Handle the keypress event for *key* and return ``None``, otherwise return *key*.
      
      Container widgets will typically call the :meth:`keypress` method on whichever of their children is
      set as being in focus.
      
      A *command_map* dictionary-like object is used by the standard widgets to determine what action
      should be performed for a given *key*. You may modify these values to your liking in your application.
      See `command_map.py <#>`_ for the defaults.

      .. TODO: insert a link to command_map.py
      
      In your own widgets you may use whatever logic you like: filtering or translating keys, selectively
      passing along events etc.


   .. method:: mouse_event(size, event, button, col, row, focus)

      Handle a mouse event and return ``True``, otherwise return ``False``.
      
      Container widgets will typically call the :meth:`mouse_event` method on whichever of their children is
      at the position ``(col, row)``. ``(col, row)`` is relative to the top-left corner of the widget.


   .. method:: get_cursor_coords(size)

      Return the cursor coordinates ``(col, row)`` of a cursor that will appear
      as part of the canvas rendered by this widget when in focus, or ``None``
      if no cursor is displayed. The :class:`ListBox` widget uses this method
      to make sure a cursor in the focus widget is not scrolled out of view.
      It is a separate method to avoid having to render the whole widget while
      calculating layout.
      
      Container widgets will typically call the :meth:`get_cursor_coords`
      method on their focus widget.


   .. method:: get_pref_col(size)

      Return the preferred column for the cursor to be displayed in this
      widget. This value might not be the same as the column returned from
      :meth:`get_cursor_coords`.
      
      The :class:`ListBox` and :class:`Pile` widgets call this method on a
      widget losing focus and use the value returned to call
      :meth:`move_cursor_to_coords` on the widget becoming the focus. This
      allows the focus to move up and down through widgets while keeping the
      cursor in approximately the same column on screen.


   .. method:: move_cursor_to_coords(size, col, row)

      Set the cursor position within a widget and return ``True``, if the
      position cannot be set somewhere within the row specified return
      ``False``.

.. vim: set ft=rst: