Esempio n. 1
0
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)
Esempio n. 2
0
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()