Concepts and core objects¶
The Scene¶
To render a UI, you need to use a UIScene:
from clubsandwich.director import DirectorLoop, Scene
from clubsandwich.ui import (
    ButtonView,
)
class MainMenuScene(UIScene):
    def __init__(self, *args, **kwargs):
        views = [ButtonView(text="Quit", callback=self.quit)]
        super().__init__(views, *args, **kwargs)
    def quit(self):
        self.director.pop_scene()
class GameLoop(DirectorLoop):
    def get_initial_scene(self):
        return MainMenuScene()
GameLoop().run()
- 
class 
clubsandwich.ui.UIScene(views, *args, **kwargs)¶ Parameters: views (list|View) – One or more subviews of the root view See
Scenefor the other args.Scene that renders a view hierarchy inside a
FirstResponderContainerView.Log the view hierarchy by pressing the backslash key at any time.
Layout¶
Layouts, most of the time, are specified using
LayoutOptions objects. These more
or less follow the UIKit springs-and-struts model. Here’s a diagram:
+----------------------------+
|             ^              |
|            top             |
|             v              |
|        +---------+         |
|        |       ^ |         |
|        |       | |         |
|<-left->|<-width->|<-right->|
|        |       | |         |
|        |   height|         |
|        |       v |         |
|        +---------+         |
|             ^              |
|          bottom            |
|             v              |
+----------------------------+
Each listed metric can either be a spring or a strut. Struts are exact values based on either a constant or a percentage of the superview. Springs are totally unconstrained, and their values are derived after the struts have been enforced.
In clubsandwich, springs are represented by None. All the other values
define some kind of strut:
- Ints >= 1 are constants.
 - Floats from 0 (inclusive) to 1 (exclusive) are fractions of the superview’s width or height.
 - The string 
intrinsicmeans “at layout time, take the value from myView.intrinsic_sizeproperty.” This is useful when you don’t know in advance how big your content is. - The string 
framemeans “at layout time, take the value from the corresponding attribute of myView.frameproperty.” 
So to put something 3 cells from the left that is 50% as wide as its
superview, you’d set left to 3 and width to 0.5. To fill the
superview (this is the default), set left/right/top/bottom to 0 and
width/height to None.
There is a special case: when width is set but left and right are
None, the view is centered horizontally. It works the same way for the
vertical axis.
The LayoutOptions class has several convenience initializers and
methods to make these specs extremely concise.
- 
class 
clubsandwich.ui.LayoutOptionValue¶ This is not a real class, but in these docs, it represents the possible values for the attributes of
LayoutOptions.None: Do not constrain this value. It is a spring.0.0-1.0left-inclusive: Use a fraction of the superview’s size on the appropriate axis.>=1: Use a constant integer'intrinsic': The view defines anintrinsic_sizeproperty; use this value. Mostly useful forLabelView.'frame': Derive a constant from the initial frame of this view. This initial frame is stored inView.layout_spec, so if you need to change it, you can just change that attribute.
- 
class 
clubsandwich.ui.layout_options.LayoutOptions¶ Parameters: - width (LayoutOptionValue) – width spec
 - height (LayoutOptionValue) – height spec
 - top (LayoutOptionValue) – top spec
 - right (LayoutOptionValue) – right spec
 - bottom (LayoutOptionValue) – bottom spec
 - left (LayoutOptionValue) – left spec
 
It is possible to define values that conflict. The behavior in these cases is undefined.
- 
width¶ A
LayoutOptionValueconstraining this view’s width (or not).
- 
height¶ A
LayoutOptionValueconstraining this view’s height (or not).
- 
top¶ A
LayoutOptionValueconstraining this view’s distance from the top of its superview (or not).
- 
right¶ A
LayoutOptionValueconstraining this view’s distance from the right of its superview (or not).
- 
bottom¶ A
LayoutOptionValueconstraining this view’s distance from the bottom of its superview (or not).
- 
left¶ A
LayoutOptionValueconstraining this view’s distance from the left of its superview (or not).
- 
classmethod 
centered(width, height)¶ Create a
LayoutOptionsobject that positions the view in the center of the superview with a constant width and height.
- 
classmethod 
column_left(width)¶ Create a
LayoutOptionsobject that positions the view as a full-height left column with a constant width.
- 
classmethod 
column_right(width)¶ Create a
LayoutOptionsobject that positions the view as a full-height right column with a constant width.
- 
classmethod 
row_bottom(height)¶ Create a
LayoutOptionsobject that positions the view as a full-height bottom row with a constant height.
- 
classmethod 
row_top(height)¶ Create a
LayoutOptionsobject that positions the view as a full-height top row with a constant height.
- 
with_updates(**kwargs)¶ Returns a new
LayoutOptionsobject with the given changes to its attributes. For example, here’s a view with a constant width, on the right side of its superview, with half the height of its superview:# "right column, but only half height" LayoutOptions.column_right(10).with_updates(bottom=0.5)
Views¶
- 
class 
clubsandwich.ui.View(frame=None, subviews=None, scene=None, layout_options=None, clear=False)¶ Parameters: - frame (Rect) – Rect relative to superview’s 
View.bounds - subviews (list) – List of subviews
 - scene (UIScene) – Scene that’s handling this view
 - layout_options (LayoutOptions) – How to position this view
 - clear (bool) – If 
True, clear bounds each render. (Each individual view implements this because it depends on color.) 
Renders itself and its subviews in an area of 2D space relative to its superview.
Variables: - subviews (list) – List of subviews
 - is_hidden (bool) – 
Trueiff this view will not be drawn. Feel free to set this yourself. - layout_options (LayoutOptions) –
 - layout_spec (Rect) – A copy of the frame made at init time, used by
LayoutOptionsto derive values during layout. - is_first_responder (bool) – 
Trueiff this view is currently the first responder. 
- frame (Rect) – Rect relative to superview’s 
 
Positioning¶
- 
View.bounds¶ This view’s rect from its internal frame of reference. That means
self.bounds.originis alwaysPoint(0, 0).
- 
View.frame¶ This view’s rect relative to its superview’s bounds.
- 
View.scene¶ The scene this view is being rendered in, or
None.
- 
View.intrinsic_size¶ Optional. Values for
intrinsic-valued attributes ofLayoutOptions.
View hierarchy¶
- 
View.superview¶ Weak reference to the view this view is a child of, or
None.
Layout¶
- 
View.set_needs_layout(val=True)¶ Parameters: val (bool) – If True, view needs to be redrawn. (defaultTrue)Call this if the view’s
frame()or content changes.draw()is only called if this was called first.Note that if you’re changing either
View.layout_optionsor changing something that affects the view’s springs-and-struts layout metrics, you may need to callself.superview.set_needs_layout()to have the layout algorithm re-run on your view.
- 
View.layout_subviews()¶ Set the frames of all subviews relative to
self.bounds. By default, applies the springs-and-struts algorithm using each view’slayout_optionsandlayout_specproperties.You shouldn’t need to override this unless
LayoutOptionsisn’t expressive enough for you.
Drawing¶
- 
View.draw(ctx)¶ Parameters: ctx (BearLibTerminalContext) – Draw this view. ctx is a full copy of the BearLibTerminal API moved into this view’s frame of reference, so you can use (0, 0) as the upper left corner.
This method will not be called if
View.is_hiddenisTrue.
First Responder¶
You might want to read about FirstResponderContainerView
before diving into this section.
- 
View.terminal_read(val)¶ Parameters: val – Return value of terminal_read()Returns: bool ( Trueif you handled the event)Fires when an input event occurs, and either:
- This view is the first responder
 - The first responder is a descendant, and no other descendants have already handled this event
 
You must return a truthy value if you handled the event so it doesn’t get handled twice.
- 
View.can_become_first_responder¶ View subclasses should return
Trueiff they want to be selectable and handle user input.
- 
View.did_become_first_responder()¶ Called immediately after view becomes the first responder.
- 
View.descendant_did_become_first_responder(view)¶ Parameters: view (View) – Called when any descendant of this view becomes the first responder. This is so scrollable view containers can scroll it into view.
- 
View.can_resign_first_responder¶ View subclass can return
Trueto prevent thetabkey from taking focus away. It should be rare to need this.
- 
View.did_resign_first_responder()¶ Called immediately after view resigns first responder status.
- 
View.descendant_did_resign_first_responder(view)¶ Parameters: view (View) – Called when any descendant of this view unbecomes the first responder. This is so scrollable view containers can release keyboard event handlers.
- 
View.first_responder_container_view¶ The ancestor (including
self) that is aFirstResponderContainerView.The most common use for this will probably be to manually change the first responder:
def a_method_on_your_view(self): # forceably become first responder, muahaha! self.first_responder_container_view.set_first_responder(self)
Tree traversal¶
- 
View.leftmost_leaf¶ Leftmost leaf of the tree.
- 
View.postorder_traversal¶ Generator of all nodes in this subtree, including
self, such that a view is visited after all its subviews.
- 
View.ancestors¶ Generator of all ancestors of this view, not including
self.
- 
View.get_ancestor_matching(predicate)¶ Parameters: predicate (func) – predicate(View) -> boolReturns the ancestor matching the given predicate, or
None.
First Responder¶
- 
class 
clubsandwich.ui.FirstResponderContainerView(*args, **kwargs)¶ Manages the “first responder” system. The control that receives BearLibTerminal events at a given time is the first responder.
This container view listens for the tab key. When it’s pressed, the subview tree is walked until another candidate is found, or there are no others. That new subview is the new first responder.
You don’t need to create this class yourself.
UIScenemakes it for you.If you want to write a control that handles input, read the source of the
ButtonViewclass.- 
find_next_responder()¶ Resign active first responder and switch to the next one.
- 
find_prev_responder()¶ Resign active first responder and switch to the previous one.
- 
set_first_responder(new_value)¶ Resign the active first responder and set a new one.
- 
terminal_read_after_first_responder(val, can_resign)¶ Parameters: - val (int) – Return value of 
terminal_read() - can_resign (bool) – 
Trueiff there is an active first responder that can resign 
If writing a custom first responder container view, override this to customize input behavior. For example, if writing a list view, you might want to use the arrows to change the first responder.
- val (int) – Return value of 
 
-