class Widget: ''' This is the base widget implementation that all widgets in Toga derive from. It defines the interface for core functionality for children, styling, layout and ownership by specific App and Window. Apart from the above, this is an abstract implementation which must be made concrete by some platform-specific code for the _apply_layout method. :param id: An identifier for this widget. :type id: ``str`` :param style: An optional style object. If no style is provided then a new one will be created for the widget. :type style: :class:`colosseum.CSSNode` ''' def __init__(self, id=None, style=None, factory=None, **config): self._id = id if id else identifier(self) self._parent = None self._children = None self._window = None self._app = None self._impl = None self.__container = None self._layout_in_progress = False self._config = config self.layout = Layout(self) if style: self.style = style.copy() else: self.style = CSS() if factory is None: self.factory = get_platform_factory() else: self.factory = factory def __repr__(self): return "<%s:%s>" % (self.__class__.__name__, id(self)) @property def id(self): ''' The node identifier. This id can be used to target CSS directives :rtype: ``str`` ''' return self._id @property def style(self): ''' The style object for this widget. :return: The style for this widget :rtype: :class:`colosseum.CSSNode` ''' return self._style @style.setter def style(self, value): self._style = value.bind(self) @property def parent(self): ''' The parent of this node. :rtype: :class:`toga.Widget` ''' return self._parent @property def children(self): ''' The children of this node. This *always* returns a list, even if the node is a leaf and cannot have children. :rtype: ``list`` :return: A list of the children for this widget ''' if self._children is None: return [] else: return self._children def add(self, child): ''' Add a widget as a child of this one. Raises an :class:`ValueError` if this widget is a leaf, and cannot have children. :param child: The child to add to the widget :type child: :class:`toga.Widget` ''' if self._children is None: raise ValueError('Widget cannot have children') self._children.append(child) child.app = self.app child._parent = self if self.parent: self.parent.layout.dirty = True self._impl._add_child(child) @property def app(self): ''' The App to which this widget belongs. :rtype: :class:`toga.App` ''' return self._app @app.setter def app(self, app): ''' Set the app to which this widget belongs :param app: The Application host :type app: :class:`toga.App` ''' if self._app is not None: if self._app != app: raise ValueError( "Widget %s is already associated with an App" % self) elif app is not None: self._app = app self._impl._set_app(app) if self._children is not None: for child in self._children: child.app = app @property def window(self): ''' The Window to which this widget belongs. :rtype: :class:`toga.Window` ''' return self._window @window.setter def window(self, window): ''' Set the Window to which this widget belongs. :param window: The new window :type window: :class:`toga.Window` ''' self._window = window self._impl._set_window(window) if self._children is not None: for child in self._children: child.window = window @property def _container(self): ''' The display container to which this widget belongs. ''' return self.__container @_container.setter def _container(self, container): self.__container = container self._impl._set_container(container) if self._children is not None: for child in self._children: child._container = container # def _create(self): # self._impl.create() # # self._configure(**self._config) def _initialize(self, **initial): pass def _update_layout(self, **style): """Force a layout update on the widget. The update request can be accompanied by additional style information (probably min_width, min_height, width or height) to control the layout. """ if self._layout_in_progress: return self._layout_in_progress = True if style: self.style.set(**style) # Recompute layout for this widget self.style.apply() # Update the layout parameters for all children. # This will also perform a leaf-first update of # the constraint on each widget. self._update_child_layout() # Set the constraints the widget to adhere to the new style. self._impl._apply_layout() self._layout_in_progress = False def _update_child_layout(self): # print("UPDATE CHILD LAYOUT - widget") if self._children is not None: for child in self.children: # if child.is_container: child._update_layout() def set_font(self, font): """ Set a font on this widget. :param font: The new font :type font: :class:`toga.Font` """ self._set_font(font)
class Widget: """ This is the base widget implementation that all widgets in Toga derive from. It defines the interface for core functionality for children, styling, layout and ownership by specific App and Window. Apart from the above, this is an abstract implementation which must be made concrete by some platform-specific code for the _apply_layout method. Args: id (str): An identifier for this widget. style (:obj:`colosseum.CSSNode`): An optional style object. If no style is provided then a new one will be created for the widget. factory (:obj:`module`): A python module that is capable to return a implementation of this class with the same name (optional & normally not needed). """ def __init__(self, id=None, style=None, factory=None): self._id = id if id else identifier(self) self._parent = None self._children = None self._window = None self._app = None self._impl = None self._layout_in_progress = False self.layout = Layout(self) if style: self.style = style.copy() else: self.style = CSS() self._font = None self.factory = get_platform_factory(factory) def __repr__(self): return "<%s:%s>" % (self.__class__.__name__, id(self)) @property def id(self): """ The node identifier. This id can be used to target CSS directives Returns: The widgets identifier as a ``str``. """ return self._id @property def parent(self): """ The parent of this node. Returns: The parent :class:`toga.Widget`. """ return self._parent @property def children(self): """ The children of this node. This *always* returns a list, even if the node is a leaf and cannot have children. Returns: A list of the children for this widget. """ if self._children is None: return [] else: return self._children def add(self, child): """ Add a widget as a child of this one. Args: child (:class:`toga.Widget`): A widget to add as a child to this widget. Raises: ValueError: If this widget is a leaf, and cannot have children. """ if self._children is None: raise ValueError('Widget cannot have children') self._children.append(child) child.app = self.app child._parent = self if self.parent: self.parent.layout.dirty = True if self._impl: self._impl.add_child(child._impl) @property def app(self): """ The App to which this widget belongs. On setting the app we also iterate over all children of this widget and set them to the same app. Returns: The :class:`toga.App` to which this widget belongs. Raises: ValueError: If the widget is already associated with another app. """ return self._app @app.setter def app(self, app): if self._app is not None: if self._app != app: raise ValueError("Widget %s is already associated with an App" % self) elif app is not None: self._app = app self._impl.set_app(app) if self._children is not None: for child in self._children: child.app = app @property def window(self): """ The Window to which this widget belongs. On setting the window, we automatically update all children of this widget to belong to the same window. Returns: The :class:`toga.Window` to which the widget belongs. """ return self._window @window.setter def window(self, window): self._window = window if self._impl: self._impl.set_window(window) if self._children is not None: for child in self._children: child.window = window @property def style(self): """ The style object for this widget. Returns: The style object :class:`colosseum.CSSNode` of the widget. """ return self._style @style.setter def style(self, value): self._style = value.bind(self) @property def font(self): """ Font the widget. Returns: The :class:`toga.Font` of the widget. """ return self._font @font.setter def font(self, font): self._font = font self._impl.set_font(font) @property def enabled(self): return self._impl.enabled @enabled.setter def enabled(self, value): self._impl.enabled = value def rehint(self): self._impl.rehint() def _update_layout(self, **style): """Force a layout update on the widget. The update request can be accompanied by additional style information (probably min_width, min_height, width or height) to control the layout. """ if self._layout_in_progress: return self._layout_in_progress = True if style: self.style.set(**style) # Recompute layout for this widget self.style.apply() # Update the layout parameters for all children. # This will also perform a leaf-first update of # the constraint on each widget. self._update_child_layout() # Set the constraints the widget to adhere to the new style. self._impl.apply_layout() self._impl.apply_sub_layout() self._layout_in_progress = False def _update_child_layout(self): if self._children is not None: for child in self.children: child._update_layout()