# Source code for enaml.widgets.toolkit_object

    #------------------------------------------------------------------------------
    # Copyright (c) 2013-2025, Nucleic Development Team.
    #
    # Distributed under the terms of the Modified BSD License.
    #
    # The full license is in the file LICENSE, distributed with this software.
    #------------------------------------------------------------------------------
    from atom.api import Atom, Event, Typed, ForwardTyped

    from enaml.application import Application
    from enaml.core.declarative import Declarative, d_
    from enaml.core.object import flag_generator, flag_property

    class ProxyToolkitObject(Atom):
        """ The base class of all proxy toolkit objects.

        A ProxyToolkitObject is responsible for the communication between
        the Declarative declaration of the object and then implementation
        object which actually performs the behavior.

        Initialization of proxy is backend dependent behavior. Most uses
        will want to initialize the entire proxy tree using traversals
        which are appropriate for their use case. The top level Window
        widget provides a entry point method into the proxy tree for this
        to occur.

        """
        #: A reference to the ToolkitObject declaration.
        declaration = ForwardTyped(lambda: ToolkitObject)

        @property
        def is_active(self):
            """ Test whether or not the proxy is fully activated.

            """
            return self.declaration.proxy_is_active

        def activate_top_down(self):
            """ A method called by the declaration to activate the proxy.

            This method is called in top-down order, during the descent
            phase of the declaration's proxy activation pass.

            """
            pass

        def activate_bottom_up(self):
            """ A method called by the declaration to activate the proxy.

            This method is called in bottom-up order, during the ascent
            phase of the declaration's proxy activation pass.

            """
            pass

        def destroy(self):
            """ Destroy the proxy and any of its resources.

            This method is called by the declaration when it is destroyed.
            It should be reimplemented by subclasses when more control
            is required.

            """
            del self.declaration

        def parent(self):
            """ Get the parent proxy object for this object.

            Returns
            -------
            result : ProxyToolkitObject or None
                The parent toolkit object of this object, or None if no
                such parent exists.

            """
            d = self.declaration.parent
            if isinstance(d, ToolkitObject):
                return d.proxy

        def children(self):
            """ Get the child objects for this object.

            Returns
            -------
            result : generator
                A generator which yields the child proxy objects.

            """
            for d in self.declaration.children:
                if isinstance(d, ToolkitObject):
                    yield d.proxy

        def child_added(self, child):
            """ Handle a child being added to the object.

            This method will only be called after the proxy tree is active
            and the UI is running. The default implementation is a no-op.

            Parameters
            ----------
            child : ProxyToolkitObject
                The toolkit proxy child added to the object.

            """
            pass

        def child_removed(self, child):
            """ Handle a child being removed from the object.

            This method will only be called after the proxy tree is active
            and the UI is running. Notably, it will not be called when the
            child is removed because it was destroyed. To handle that case,
            reimplement the 'destroy' method in a subclass. The default
            implementation is a no-op.

            Parameters
            ----------
            child : ProxyToolkitObject
                The toolkit proxy removed the object.

            """
            pass

    #: A flag indicating that the object's proxy is ready for use.
    ACTIVE_PROXY_FLAG = next(flag_generator)

    class ToolkitObject(Declarative):
        """ The base class of all toolkit objects in Enaml.

        """
        #: An event fired when an object's proxy is activated. It is
        #: triggered once during the object lifetime, at the end of the
        #: activate_proxy method.
        activated = d_(Event(), writable=False)

        #: A reference to the ProxyToolkitObject
        proxy = Typed(ProxyToolkitObject)

        #: A property which gets and sets the active proxy flag. This should
        #: not be manipulated directly by user code. This flag will be set to
        #: True by external code after the proxy widget hierarchy is setup.
        proxy_is_active = flag_property(ACTIVE_PROXY_FLAG)

        def initialize(self):
            """ A reimplemented initializer.

            This initializer will invoke the application to create the
            proxy if one has not already been provided.

            """
            if not self.proxy:
                app = Application.instance()
                if app is None:
                    msg = 'cannot create a proxy without an active Application'
                    raise RuntimeError(msg)
                self.proxy = app.create_proxy(self)
            super(ToolkitObject, self).initialize()

        def destroy(self):
            """ A reimplemented destructor.

            This destructor invokes the 'destroy' method on the proxy
            toolkit object.

            """
            super(ToolkitObject, self).destroy()
            self.proxy_is_active = False
            if self.proxy:
                self.proxy.destroy()
                del self.proxy

        def child_added(self, child):
            """ A reimplemented child added event handler.

            This handler will invoke the superclass handler and then invoke
            the 'child_added()' method on an active proxy.

            """
            super(ToolkitObject, self).child_added(child)
            if isinstance(child, ToolkitObject) and self.proxy_is_active:
                if not child.proxy_is_active:
                    child.activate_proxy()
                self.proxy.child_added(child.proxy)

        def child_removed(self, child):
            """ A reimplemented child removed event handler.

            This handler will invoke the superclass handler and then invoke
            the 'child_removed()' method on an active proxy. The method on
            the active proxy will be called even if the child proxy has been
            destroyed.

            """
            super(ToolkitObject, self).child_removed(child)
            if isinstance(child, ToolkitObject) and self.proxy_is_active:
                self.proxy.child_removed(child.proxy)

        def activate_proxy(self):
            """ Activate the proxy object tree.

            This method should be called by a node to activate the proxy
            tree by making two initialization passes over the tree, from
            this node downward. This method is automatically at the proper
            times and should not normally need to be invoked by user code.

            """
            self.activate_top_down()
            for child in self.children:
                if isinstance(child, ToolkitObject):
                    child.activate_proxy()
            self.activate_bottom_up()
            self.proxy_is_active = True
            self.activated()

        def activate_top_down(self):
            """ Initialize the proxy on the top-down activation pass.

            By default, this method calls the 'activate_top_down' method on the
            proxy object. It may be reimplemented by subclasses which wish
            to perform toolkit-specific initialization.

            """
            self.proxy.activate_top_down()

        def activate_bottom_up(self):
            """ Initialize the proxy on the bottom-up activation pass.

            By default, this method calls the 'activate_bottom_up' method on the
            proxy object. It may be reimplemented by subclasses which wish
            to perform toolkit-specific initialization.

            """
            self.proxy.activate_bottom_up()

        #--------------------------------------------------------------------------
        # Private API
        #--------------------------------------------------------------------------
        def _update_proxy(self, change):
            """ Update the proxy widget when the Widget data changes.

            This method only updates the proxy when an attribute is updated;
            not when it is created or deleted. It is useful for subclasses
            as a base observer handler

            """
            if change['type'] == 'update' and self.proxy_is_active:
                handler = getattr(self.proxy, 'set_' + change['name'], None)
                if handler is not None:
                    handler(change['value'])