.. _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 `_ `Widget interface reference `_ .. 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 `_. 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: