U
    Pe                     @   s  d Z dZddlmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZ ddlmZmZmZmZmZmZmZmZ dd	lmZmZ dd
lmZ ddlmZ ddlmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z' G dd de(Z)G dd deZ*G dd deZ+G dd de+Z,G dd de+Z-G dd de+Z.G dd de.Z/G dd de+Z0G dd de,Z1G dd  d e,Z2G d!d" d"e,Z3G d#d$ d$e,Z4G d%d& d&eZ5e6d'krdd(l7m8Z8 dd)l9m:Z: e;d* G d+d, d,e8Z<e< =  d-S ).aZ  Screen Manager
==============

.. image:: images/screenmanager.gif
    :align: right

.. versionadded:: 1.4.0

The screen manager is a widget dedicated to managing multiple screens for your
application. The default :class:`ScreenManager` displays only one
:class:`Screen` at a time and uses a :class:`TransitionBase` to switch from one
Screen to another.

Multiple transitions are supported based on changing the screen coordinates /
scale or even performing fancy animation using custom shaders.

Basic Usage
-----------

Let's construct a Screen Manager with 4 named screens. When you are creating
a screen, **you absolutely need to give a name to it**::

    from kivy.uix.screenmanager import ScreenManager, Screen

    # Create the manager
    sm = ScreenManager()

    # Add few screens
    for i in range(4):
        screen = Screen(name='Title %d' % i)
        sm.add_widget(screen)

    # By default, the first screen added into the ScreenManager will be
    # displayed. You can then change to another screen.

    # Let's display the screen named 'Title 2'
    # A transition will automatically be used.
    sm.current = 'Title 2'

The default :attr:`ScreenManager.transition` is a :class:`SlideTransition` with
options :attr:`~SlideTransition.direction` and
:attr:`~TransitionBase.duration`.

Please note that by default, a :class:`Screen` displays nothing: it's just a
:class:`~kivy.uix.relativelayout.RelativeLayout`. You need to use that class as
a root widget for your own screen, the best way being to subclass.

.. warning::
    As :class:`Screen` is a :class:`~kivy.uix.relativelayout.RelativeLayout`,
    it is important to understand the
    :ref:`kivy-uix-relativelayout-common-pitfalls`.

Here is an example with a 'Menu Screen' and a 'Settings Screen'::

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen

    # Create both screens. Please note the root.manager.current: this is how
    # you can control the ScreenManager from kv. Each screen has by default a
    # property manager that gives you the instance of the ScreenManager used.
    Builder.load_string("""
    <MenuScreen>:
        BoxLayout:
            Button:
                text: 'Goto settings'
                on_press: root.manager.current = 'settings'
            Button:
                text: 'Quit'

    <SettingsScreen>:
        BoxLayout:
            Button:
                text: 'My settings button'
            Button:
                text: 'Back to menu'
                on_press: root.manager.current = 'menu'
    """)

    # Declare both screens
    class MenuScreen(Screen):
        pass

    class SettingsScreen(Screen):
        pass

    class TestApp(App):

        def build(self):
            # Create the screen manager
            sm = ScreenManager()
            sm.add_widget(MenuScreen(name='menu'))
            sm.add_widget(SettingsScreen(name='settings'))

            return sm

    if __name__ == '__main__':
        TestApp().run()


Changing Direction
------------------

A common use case for :class:`ScreenManager` involves using a
:class:`SlideTransition` which slides right to the next screen
and slides left to the previous screen. Building on the previous
example, this can be accomplished like so::

    Builder.load_string("""
    <MenuScreen>:
        BoxLayout:
            Button:
                text: 'Goto settings'
                on_press:
                    root.manager.transition.direction = 'left'
                    root.manager.current = 'settings'
            Button:
                text: 'Quit'

    <SettingsScreen>:
        BoxLayout:
            Button:
                text: 'My settings button'
            Button:
                text: 'Back to menu'
                on_press:
                    root.manager.transition.direction = 'right'
                    root.manager.current = 'menu'
    """)


Advanced Usage
--------------

From 1.8.0, you can now switch dynamically to a new screen, change the
transition options and remove the previous one by using
:meth:`~ScreenManager.switch_to`::

    sm = ScreenManager()
    screens = [Screen(name='Title {}'.format(i)) for i in range(4)]

    sm.switch_to(screens[0])
    # later
    sm.switch_to(screens[1], direction='right')

Note that this method adds the screen to the :class:`ScreenManager` instance
and should not be used if your screens have already been added to this
instance. To switch to a screen which is already added, you should use the
:attr:`~ScreenManager.current` property.


Changing transitions
--------------------

You have multiple transitions available by default, such as:

- :class:`NoTransition` - switches screens instantly with no animation
- :class:`SlideTransition` - slide the screen in/out, from any direction
- :class:`CardTransition` - new screen slides on the previous
  or the old one slides off the new one depending on the mode
- :class:`SwapTransition` - implementation of the iOS swap transition
- :class:`FadeTransition` - shader to fade the screen in/out
- :class:`WipeTransition` - shader to wipe the screens from right to left
- :class:`FallOutTransition` - shader where the old screen 'falls' and
  becomes transparent, revealing the new one behind it.
- :class:`RiseInTransition` - shader where the new screen rises from the
  screen centre while fading from transparent to opaque.

You can easily switch transitions by changing the
:attr:`ScreenManager.transition` property::

    sm = ScreenManager(transition=FadeTransition())

.. note::

    Currently, none of Shader based Transitions use
    anti-aliasing. This is because they use the FBO which doesn't have
    any logic to handle supersampling. This is a known issue and we
    are working on a transparent implementation that will give the
    same results as if it had been rendered on screen.

    To be more concrete, if you see sharp edged text during the animation, it's
    normal.

)ScreenScreenManagerScreenManagerExceptionTransitionBaseShaderTransitionSlideTransitionSwapTransitionFadeTransitionWipeTransitionFallOutTransitionRiseInTransitionNoTransitionCardTransition    )	iteritems)Logger)EventDispatcher)Clock)FloatLayout)StringPropertyObjectPropertyAliasPropertyNumericPropertyListPropertyOptionPropertyBooleanPropertyColorProperty)	AnimationAnimationTransition)RelativeLayout)Builder)RenderContext	RectangleFbo
ClearColorClearBuffersBindTexture
PushMatrix	PopMatrix	TranslateCallbackScalec                   @   s   e Zd ZdZdS )r   z.Exception for the :class:`ScreenManager`.
    N)__name__
__module____qualname____doc__ r/   r/   :/tmp/pip-unpacked-wheel-xzebddm3/kivy/uix/screenmanager.pyr      s   r   c                   @   sd   e Zd ZdZedZedddZedZ	e
ddd	Zd
Zdd Zdd Zdd Zdd Zdd ZdS )r   a  Screen is an element intended to be used with a :class:`ScreenManager`.
    Check module documentation for more information.

    :Events:
        `on_pre_enter`: ()
            Event fired when the screen is about to be used: the entering
            animation is started.
        `on_enter`: ()
            Event fired when the screen is displayed: the entering animation is
            complete.
        `on_pre_leave`: ()
            Event fired when the screen is about to be removed: the leaving
            animation is started.
        `on_leave`: ()
            Event fired when the screen is removed: the leaving animation is
            finished.

    .. versionchanged:: 1.6.0
        Events `on_pre_enter`, `on_enter`, `on_pre_leave` and `on_leave` were
        added.
     NTZ	allownone        out)inr4   options)on_pre_enteron_enteron_pre_leaveon_leavec                 G   s   d S Nr/   selfargsr/   r/   r0   r8     s    zScreen.on_pre_enterc                 G   s   d S r<   r/   r=   r/   r/   r0   r9     s    zScreen.on_enterc                 G   s   d S r<   r/   r=   r/   r/   r0   r:   !  s    zScreen.on_pre_leavec                 G   s   d S r<   r/   r=   r/   r/   r0   r;   $  s    zScreen.on_leavec                 C   s
   d| j  S )Nz<Screen name=%r>namer>   r/   r/   r0   __repr__'  s    zScreen.__repr__)r+   r,   r-   r.   r   rA   r   managerr   transition_progressr   transition_state
__events__r8   r9   r:   r;   rC   r/   r/   r/   r0   r      s   		r   c                   @   s   e Zd ZdZe Ze ZedZe Z	e
dZeddZdZdd Zd	d
 Zdd Zdd Zdd Zdd Zdd Zdd ZdS )r   a  TransitionBase is used to animate 2 screens within the
    :class:`ScreenManager`. This class acts as a base for other
    implementations like the :class:`SlideTransition` and
    :class:`SwapTransition`.

    :Events:
        `on_progress`: Transition object, progression float
            Fired during the animation of the transition.
        `on_complete`: Transition object
            Fired when the transition is finished.
    g?FTr2   on_progresson_completec                 C   s   | j rtd|| _t| jdd| _| jj| j| jd | 	| j
 d| j
_d| j
_d| j_d| j_| j
d | jd	 d
| _ | j|  | dd dS )n(internal) Starts the transition. This is automatically
        called by the :class:`ScreenManager`.
        zstart() is called twice!r   )dsrH   r3   r5   r4   r8   r:   TrI   N)	is_activer   rD   r   duration_animbind_on_progress_on_complete
add_screen	screen_inrE   rF   
screen_outdispatchstart)r>   rD   r/   r/   r0   rX   i  s"    
zTransitionBase.startc                 C   s,   | j r"| j |  | d d| _ d| _dS )zm(internal) Stops the transition. This is automatically called by the
        :class:`ScreenManager`.
        rJ   NF)rP   cancelrW   rN   rB   r/   r/   r0   stop  s
    
zTransitionBase.stopc                 C   s   | j | dS )zG(internal) Used to add a screen to the :class:`ScreenManager`.
        NrD   real_add_widgetr>   screenr/   r/   r0   rT     s    zTransitionBase.add_screenc                 C   s   | j | dS )zL(internal) Used to remove a screen from the :class:`ScreenManager`.
        N)rD   real_remove_widgetr]   r/   r/   r0   remove_screen  s    zTransitionBase.remove_screenc                 C   s   |  | j d S r<   )r`   rV   rB   r/   r/   r0   rJ     s    zTransitionBase.on_completec                 C   s   d S r<   r/   )r>   progressionr/   r/   r0   rI     s    zTransitionBase.on_progressc                 G   s,   |d }|| j _d| | j_| d| d S )N      ?rI   )rU   rE   rV   rW   )r>   lprogressr/   r/   r0   rR     s    zTransitionBase._on_progressc                 G   s2   d| _ | d | jd | jd d | _d S )NFrJ   r9   r;   )rN   rW   rU   rV   rP   r>   rd   r/   r/   r0   rS     s
    
zTransitionBase._on_completeN)r+   r,   r-   r.   r   rV   rU   r   rO   rD   r   rN   rP   rG   rX   rZ   rT   r`   rJ   rI   rR   rS   r/   r/   r/   r0   r   +  s    		

r   c                       st   e Zd ZdZedZedZeddddgZdd Z	dd Z
 fd	d
Zdd Zdd Zdd Z fddZ  ZS )r   a  Transition class that uses a Shader for animating the transition between
    2 screens. By default, this class doesn't assign any fragment/vertex
    shader. If you want to create your own fragment shader for the transition,
    you need to declare the header yourself and include the "t", "tex_in" and
    "tex_out" uniform::

        # Create your own transition. This shader implements a "fading"
        # transition.
        fs = """$HEADER
            uniform float t;
            uniform sampler2D tex_in;
            uniform sampler2D tex_out;

            void main(void) {
                vec4 cin = texture2D(tex_in, tex_coord0);
                vec4 cout = texture2D(tex_out, tex_coord0);
                gl_FragColor = mix(cout, cin, t);
            }
        """

        # And create your transition
        tr = ShaderTransition(fs=fs)
        sm = ScreenManager(transition=tr)

    Nr      c              	   C   s   t |jdd}| t| j  t  W 5 Q R X ||j |j  t  t	|j
 |j d W 5 Q R X |j t  W 5 Q R X |S )NT)sizeZwith_stencilbufferr   )r"   rh   r#   
clearcolorr$   addcanvasbeforer&   r(   xyafterr'   )r>   r^   Zfbor/   r/   r0   make_screen_fbo  s    
z ShaderTransition.make_screen_fboc                 C   s   || j d< d S )Nt)
render_ctx)r>   re   r/   r/   r0   rI     s    zShaderTransition.on_progressc                    s   d| j d< tt|   d S )Nrc   rq   )rr   superr   rJ   rB   	__class__r/   r0   rJ     s    
zShaderTransition.on_completec                 G   s<   | j r8| j j| jjjkr8| j | jjkr8| jj| j j d S r<   )rV   rk   rD   childrenremover=   r/   r/   r0   _remove_out_canvas  s    z#ShaderTransition._remove_out_canvasc              	      s.   j j j_ j j j_ j j   jj j j  fdd}  j _	  j  _
 jj j	  jj j
 t j jddd _ jd t j
jdd t j	jdd  jj\}} j	jj\}}t||f||f j	jjd t| W 5 Q R X d jd	< d jd
<  jj j d S )Nc                    s   t  jd  j|  d S )Nrb   )r   Zschedule_oncerx   rr   rw   )instrrB   r/   r0   remove_screen_out  s    z6ShaderTransition.add_screen.<locals>.remove_screen_outT)fsvsZuse_parent_modelviewZuse_parent_projectionrg   )textureindex   )rh   pos
tex_coordsZtex_outZtex_in)rV   r   rU   rh   rD   r_   rk   rj   rp   fbo_infbo_outr    r{   r|   rr   r%   r}   r!   r   r)   )r>   r^   rz   rm   rn   whr/   rB   r0   rT     s2    


zShaderTransition.add_screenc                 C   sJ   | j j| j | j j| j | j j| j |   | j | j d S r<   )	rD   rk   rw   r   r   rr   rx   r\   rU   r]   r/   r/   r0   r`     s
    zShaderTransition.remove_screenc                    s   |    tt|   d S r<   )rx   rs   r   rZ   rB   rt   r/   r0   rZ     s    zShaderTransition.stop)r+   r,   r-   r.   r   r{   r|   r   ri   rp   rI   rJ   rx   rT   r`   rZ   __classcell__r/   r/   rt   r0   r     s   r   c                       s(   e Zd ZdZedZ fddZ  ZS )r   zvNo transition, instantly switches to the next screen with no delay or
    animation.

    .. versionadded:: 1.8.0
    r3   c                    s*   | j j| j_| j j| j_tt|   d S r<   )rD   r   rU   rV   rs   r   rJ   rB   rt   r/   r0   rJ   )  s    zNoTransition.on_complete)r+   r,   r-   r.   r   rO   rJ   r   r/   r/   rt   r0   r      s   r   c                       s4   e Zd ZdZedddZdd Z fddZ  ZS )	r   zhSlide Transition, can be used to show a new screen from any direction:
    left, right, up or down.
    left)r   rightupdownr6   c                 C   s  | j }| j}| j}|j\}}|j\}}| j}	tj}
|
|}|	dkrp| |_|_||d|   |_	|||  |_	n|	dkr| |_|_|||  |_	||d|   |_	nl|	dkr| |_	|_	||d|   |_|||  |_n6|	dkr| |_	|_	|||  |_||d|   |_d S )Nr   rg   r   r   r   )
rU   rV   rD   r   rh   	directionr   out_quadrn   rm   )r>   ra   abrD   rm   rn   widthheightr   alr/   r/   r0   rI   ;  s0    


zSlideTransition.on_progressc                    s*   | j j| j_| j j| j_tt|   d S r<   )rD   r   rU   rV   rs   r   rJ   rB   rt   r/   r0   rJ   U  s    zSlideTransition.on_complete)	r+   r,   r-   r.   r   r   rI   rJ   r   r/   r/   rt   r0   r   /  s   r   c                       s8   e Zd ZdZedddgdZ fddZdd Z  ZS )	r   a  Card transition that looks similar to Android 4.x application drawer
    interface animation.

    It supports 4 directions like SlideTransition: left, right, up and down,
    and two modes, pop and push. If push mode is activated, the previous
    screen does not move, and the new one slides in from the given direction.
    If the pop mode is activated, the previous screen slides out, when the new
    screen is already on the position of the ScreenManager.

    .. versionadded:: 1.10
    pushpopr6   c                    sp   t t| | | j}| j}| j}|dkrH|j|j |j|j n$|dkrl|j|j |j|j dS )rK   r   r   N)	rs   r   rX   moderU   rV   rk   rw   rj   )r>   rD   r   r   r   rt   r/   r0   rX   s  s    zCardTransition.startc                 C   s`  | j }| j}| j}|j\}}|j\}}| j}	| j}
tj}||}|
dkr||f|_|	dkrr||d|   |f|_n^|	dkr||d|   |f|_n>|	dkr|||d|   f|_n|	dkr|||d|   f|_n|
dkr\||f|_|	dkr|||  |f|_nX|	dkr"|||  |f|_n:|	dkr@||||  f|_n|	dkr\||||  f|_d S )Nr   r   rg   r   r   r   r   )	rU   rV   rD   r   rh   r   r   r   r   )r>   ra   r   r   rD   rm   rn   r   r   r   r   r   r/   r/   r0   rI     s:    








zCardTransition.on_progress)	r+   r,   r-   r.   r   r   rX   rI   r   r/   r/   rt   r0   r   [  s   r   c                       sP   e Zd ZdZ fddZ fddZdd Zdd	 Z fd
dZdd Z	  Z
S )r   z`Swap transition that looks like iOS transition when a new window
    appears on the screen.
    c                    s   t t| jf | i | _d S r<   )rs   r   __init__scalesr>   kwargsrt   r/   r0   r     s    zSwapTransition.__init__c              
      s   | j | jfD ]b}|jj tdd tdd}W 5 Q R X |jj tdd W 5 Q R X |j| j	d || j
|< qtt| | d S )Nswaptransition_scale)group)center)rU   rV   rk   rl   r&   r*   ro   r'   rQ   update_scaler   rs   r   rX   )r>   rD   r^   Zscalert   r/   r0   rX     s    


zSwapTransition.startc                 C   s   || j | _d S r<   )r   origin)r>   r^   r   r/   r/   r0   r     s    zSwapTransition.update_scalec                 C   s   | j |d d S )Nrg   r[   r]   r/   r/   r0   rT     s    zSwapTransition.add_screenc                    s\   | j j| j_| j j| j_| j| jfD ]$}|jj|jjfD ]}|d q8q$tt	| 
  d S )Nr   )rD   r   rU   rV   rk   rl   ro   Zremove_grouprs   r   rJ   )r>   r^   rk   rt   r/   r0   rJ     s    zSwapTransition.on_completec           	         s.  | j }| j}| j} fdddD | j| _ fdddD | j| _|j |_|_tj} dk r| d }|jd }|jd }|j	|| d	  |_
|j	|| d	  |_	n~| j | jjd
 kr| j| j  | j| j  | d d }|jd }|jd }|j
|d|   |_
|j	d| | d	  |_	d S )Nc                    s   g | ]}d  d  qS )rc   ffffff?r/   .0xyzra   r/   r0   
<listcomp>  s     z.SwapTransition.on_progress.<locals>.<listcomp>r   c                    s   g | ]}d  d   qS )      ?r/   r   r   r/   r0   r     s     r   r   r   皙?g       @rb   g333333?rg   )rU   rV   rD   r   r   Zcenter_yr   Zin_out_siner   Zcenter_xrm   rv   r_   r\   )	r>   ra   r   r   rD   r   p2r   Zwidthbr/   r   r0   rI     s*    



zSwapTransition.on_progress)r+   r,   r-   r.   r   rX   r   rT   rJ   rI   r   r/   r/   rt   r0   r     s   r   c                   @   s   e Zd ZdZdZeeZdS )r	   z1Wipe transition, based on a fragment Shader.
    aI  $HEADER$
    uniform float t;
    uniform sampler2D tex_in;
    uniform sampler2D tex_out;

    void main(void) {
        vec4 cin = texture2D(tex_in, tex_coord0);
        vec4 cout = texture2D(tex_out, tex_coord0);
        gl_FragColor = mix(cout, cin, clamp((-1.5 + 1.5*tex_coord0.x + 2.5*t),
            0.0, 1.0));
    }
    N)r+   r,   r-   r.   ZWIPE_TRANSITION_FSr   r{   r/   r/   r/   r0   r	     s   r	   c                   @   s   e Zd ZdZdZeeZdS )r   z1Fade transition, based on a fragment Shader.
    aU  $HEADER$
    uniform float t;
    uniform sampler2D tex_in;
    uniform sampler2D tex_out;

    void main(void) {
        vec4 cin = vec4(texture2D(tex_in, tex_coord0.st));
        vec4 cout = vec4(texture2D(tex_out, tex_coord0.st));
        vec4 frag_col = vec4(t * cin) + vec4((1.0 - t) * cout);
        gl_FragColor = frag_col;
    }
    N)r+   r,   r-   r.   ZFADE_TRANSITION_FSr   r{   r/   r/   r/   r0   r     s   r   c                   @   s$   e Zd ZdZedZdZeeZdS )r
   a  Transition where the new screen 'falls' from the screen centre,
    becoming smaller and more transparent until it disappears, and
    revealing the new screen behind it. Mimics the popular/standard
    Android transition.

    .. versionadded:: 1.8.0

    g333333?ao  $HEADER$
    uniform float t;
    uniform sampler2D tex_in;
    uniform sampler2D tex_out;

    void main(void) {
        /* quantities for position and opacity calculation */
        float tr = 0.5*sin(t);  /* 'real' time */
        vec2 diff = (tex_coord0.st - 0.5) * (1.0/(1.0-tr));
        vec2 dist = diff + 0.5;
        float max_dist = 1.0 - tr;

        /* in and out colors */
        vec4 cin = vec4(texture2D(tex_in, tex_coord0.st));
        vec4 cout = vec4(texture2D(tex_out, dist));

        /* opacities for in and out textures */
        float oin = clamp(1.0-cos(t), 0.0, 1.0);
        float oout = clamp(cos(t), 0.0, 1.0);

        bvec2 outside_bounds = bvec2(abs(tex_coord0.s - 0.5) > 0.5*max_dist,
                                     abs(tex_coord0.t - 0.5) > 0.5*max_dist);

        vec4 frag_col;
        if (any(outside_bounds) ){
            frag_col = vec4(cin.x, cin.y, cin.z, 1.0);
            }
        else {
            frag_col = vec4(oout*cout.x + oin*cin.x, oout*cout.y + oin*cin.y,
                            oout*cout.z + oin*cin.z, 1.0);
            }

        gl_FragColor = frag_col;
    }
    N)	r+   r,   r-   r.   r   rO   ZFALLOUT_TRANSITION_FSr   r{   r/   r/   r/   r0   r
     s   	$r
   c                   @   s$   e Zd ZdZedZdZeeZdS )r   zTransition where the new screen rises from the screen centre,
    becoming larger and changing from transparent to opaque until it
    fills the screen. Mimics the popular/standard Android transition.

    .. versionadded:: 1.8.0
    r   a  $HEADER$
    uniform float t;
    uniform sampler2D tex_in;
    uniform sampler2D tex_out;

    void main(void) {
        /* quantities for position and opacity calculation */
        float tr = 0.5 - 0.5*sqrt(sin(t));  /* 'real' time */
        vec2 diff = (tex_coord0.st - 0.5) * (1.0/(1.0-tr));
        vec2 dist = diff + 0.5;
        float max_dist = 1.0 - tr;

        /* in and out colors */
        vec4 cin = vec4(texture2D(tex_in, dist));
        vec4 cout = vec4(texture2D(tex_out, tex_coord0.st));

        /* opacities for in and out textures */
        float oin = clamp(sin(2.0*t), 0.0, 1.0);
        float oout = clamp(1.0 - sin(2.0*t), 0.0, 1.0);

        bvec2 outside_bounds = bvec2(abs(tex_coord0.s - 0.5) > 0.5*max_dist,
                                     abs(tex_coord0.t - 0.5) > 0.5*max_dist);

        vec4 frag_col;
        if (any(outside_bounds) ){
            frag_col = vec4(cout.x, cout.y, cout.z, 1.0);
            }
        else {
            frag_col = vec4(oout*cout.x + oin*cin.x, oout*cout.y + oin*cin.y,
                            oout*cout.z + oin*cin.z, 1.0);
            }

        gl_FragColor = frag_col;
    }
    N)	r+   r,   r-   r.   r   rO   ZRISEIN_TRANSITION_FSr   r{   r/   r/   r/   r0   r   @  s   $r   c                       s  e Zd ZdZedddZeedZe	 Z
edddZdd Zeedd	Z fd
dZdd Zdd Zdd Zd2ddZ fddZ fddZdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) Z fd*d+Z fd,d-Z  fd.d/Z! fd0d1Z"  Z#S )3r   zScreen manager. This is the main class that will control your
    :class:`Screen` stack and memory.

    By default, the manager will show only one screen at a time.
    NTr2   )Z	baseclassc                 C   s   dd | j D S )Nc                 S   s   g | ]
}|j qS r/   r@   r   rM   r/   r/   r0   r     s     z3ScreenManager._get_screen_names.<locals>.<listcomp>screensrB   r/   r/   r0   _get_screen_names  s    zScreenManager._get_screen_namesr   )rQ   c                    s4   d|krt  | _tt| jf | | d| j d S )N
transitionr   )r   r   rs   r   r   Zfbind_update_posr   rt   r/   r0   r     s    zScreenManager.__init__c                 C   s$   |  d|  || jkr || _d S )Nscreen_names)propertyrW   current_screencurrent)r>   r^   rA   r/   r/   r0   _screen_name_changed  s    
z"ScreenManager._screen_name_changedc                 O   sh   t |tstd|jr2|j| kr*tdtd| |_|j| jd | j| | jdkrd|j	| _dS )z^
        .. versionchanged:: 2.1.0
            Renamed argument `screen` to `widget`.
        )ScreenManager accepts only Screen widget.zpScreen already managed by this ScreenManager (are you calling `switch_to` when you should be setting `current`?)z0Screen already managed by another ScreenManager.r@   N)

isinstancer   r   rD   rQ   r   r   appendr   rA   )r>   widgetr?   r   r/   r/   r0   
add_widget  s"    


zScreenManager.add_widgetc                 O   sz   t |tstd|| jkr d S | j|krVt| }|j|krPd | _|j	| n|| _d |_
|j| jd | j| d S )Nz;ScreenManager uses remove_widget only for removing Screens.r@   )r   r   r   r   r   nextrA   r   parentr_   rD   unbindr   rw   )r>   r   r?   r   otherr/   r/   r0   remove_widget  s    



zScreenManager.remove_widgetc                 O   s2   |dkr| j dd }| j}|D ]}|| q dS )za
        .. versionchanged:: 2.1.0
            Renamed argument `screens` to `children`.
        N)r   r   )r>   rv   r?   r   r   r   r/   r/   r0   clear_widgets  s
    zScreenManager.clear_widgetsc                    s(   |j }|r|| tt| | d S r<   )r   r_   rs   r   r   )r>   r^   r?   r   rt   r/   r0   r\     s    
zScreenManager.real_add_widgetc                    s   t t| | d S r<   )rs   r   r   )r>   r^   r?   rt   r/   r0   r_     s    z ScreenManager.real_remove_widgetc                 C   s   |d kr| j   d | _d S | |}|| jkr4d S | j   | j}|| _|rl|| j _|| j _| j |  n.| | | j|_| 	  |
d |
d d S )Nr8   r9   )r   rZ   r   
get_screenrU   rV   rX   r\   r   Z	do_layoutrW   )r>   instancevaluer^   Zprevious_screenr/   r/   r0   
on_current  s&    





zScreenManager.on_currentc                    sR    fdd| j D }t|}|dkr0td  |dkrJtd |f  |d S )z{Return the screen widget associated with the name or raise a
        :class:`ScreenManagerException` if not found.
        c                    s   g | ]}|j  kr|qS r/   r@   r   r@   r/   r0   r   3  s     
 z,ScreenManager.get_screen.<locals>.<listcomp>r   zNo Screen with name "%s".rg   zMultiple screens named "%s": %s)r   lenr   r   warn)r>   rA   matchesZnum_matchesr/   r@   r0   r   /  s    zScreenManager.get_screenc                    s   t  fdd| jD S )zaReturn True if a screen with the `name` has been found.

        .. versionadded:: 1.6.0
        c                    s   g | ]}|j  kr|qS r/   r@   r   r@   r/   r0   r   @  s     
 z,ScreenManager.has_screen.<locals>.<listcomp>)boolr   )r>   rA   r/   r@   r0   
has_screen;  s    zScreenManager.has_screenc                 C   sR   | j }|sdS z(|| j}|d t| }|| jW S  tk
rL   Y dS X dS )z?Py2K backwards compatibility without six or other lib.
        Nrg   r   r~   r   r   rA   
ValueErrorr>   r   r~   r/   r/   r0   __next__B  s    zScreenManager.__next__c                 C   s   |   S )z8Return the name of the next screen from the screen list.)r   rB   r/   r/   r0   r   O  s    zScreenManager.nextc                 C   sR   | j }|sdS z(|| j}|d t| }|| jW S  tk
rL   Y dS X dS )zEReturn the name of the previous screen from the screen list.
        Nrg   r   r   r/   r/   r0   previousS  s    zScreenManager.previousc                    s   |dk	st t|tstdj  |jkrH|jrH	 |_j|
dd}|rd|_t|D ]\}}tj|| ql|jk	r| j|krdS j  fddjjd |j_dS )a  Add a new or existing screen to the ScreenManager and switch to it.
        The previous screen will be "switched away" from. `options` are the
        :attr:`transition` options that will be changed before the animation
        happens.

        If no previous screens are available, the screen will be used as the
        main one::

            sm = ScreenManager()
            sm.switch_to(screen1)
            # later
            sm.switch_to(screen2, direction='left')
            # later
            sm.switch_to(screen3, direction='right', duration=1.)

        If any animation is in progress, it will be stopped and replaced by
        this one: you should avoid this because the animation will just look
        weird. Use either :meth:`switch_to` or :attr:`current` but not both.

        The `screen` name will be changed if there is any conflict with the
        current screen.

        .. versionadded: 1.8.0
        Nr   r   c                    s*    j kr  _| jd d S )NrJ   )rv   r   r   r   r   Zold_currentZold_transitionremove_old_screenr>   r/   r0   r     s    

z2ScreenManager.switch_to.<locals>.remove_old_screenr   )AssertionErrorr   r   r   r   rZ   r   r   rA   _generate_screen_namer   r   setattrrD   r   r   rQ   r   )r>   r^   r7   Zspecified_transitionkeyr   r/   r   r0   	switch_to`  s.    






zScreenManager.switch_toc                 C   s*   d}d |}| |s|S |d7 }qd S )Nr   z	_screen{}rg   )formatr   )r>   irA   r/   r/   r0   r     s
    

z#ScreenManager._generate_screen_namec                 C   s8   | j D ],}| jjr,|| jjks|| jjkr,q||_qd S r<   )rv   r   rN   rU   rV   r   )r>   r   r   childr/   r/   r0   r     s    


zScreenManager._update_posc                    s   | j jrdS t ||S NF)r   rN   rs   	on_motion)r>   etypemert   r/   r0   r     s    zScreenManager.on_motionc                    s   | j jrdS tt| |S r   )r   rN   rs   r   on_touch_downr>   touchrt   r/   r0   r     s    zScreenManager.on_touch_downc                    s   | j jrdS tt| |S r   )r   rN   rs   r   on_touch_mover   rt   r/   r0   r     s    zScreenManager.on_touch_movec                    s   | j jrdS tt| |S r   )r   rN   rs   r   on_touch_upr   rt   r/   r0   r     s    zScreenManager.on_touch_up)N)$r+   r,   r-   r.   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r\   r_   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r/   r/   rt   r0   r   w  s6   
		
Br   __main__)App)ButtonaR  
<Screen>:
    canvas:
        Color:
            rgb: .2, .2, .2
        Rectangle:
            size: self.size

    GridLayout:
        cols: 2
        Button:
            text: 'Hello world'
        Button:
            text: 'Hello world'
        Button:
            text: 'Hello world'
        Button:
            text: 'Hello world'
c                   @   s$   e Zd Zdd Zdd Zdd ZdS )TestAppc                 G   s   t | j| j_d S r<   )r   smr   rf   r/   r/   r0   change_view  s    zTestApp.change_viewc                 G   s   | j | j d d S )Ntest1)r   r   r   rf   r/   r/   r0   r`     s    zTestApp.remove_screenc                 C   s   t  }tt d | _}|tdd |tdd tdd}|j| jd tddd	}|j| j	d || || || |S )
Nr   r   r@   Ztest2)NN)	size_hint)Z
on_released   )r   rm   )
r   r   r   r   r   r   r   rQ   r   r`   )r>   rootr   ZbtnZbtn2r/   r/   r0   build  s    



zTestApp.buildN)r+   r,   r-   r   r`   r   r/   r/   r/   r0   r     s   r   N)>r.   __all__Zkivy.compatr   Zkivy.loggerr   Z
kivy.eventr   Z
kivy.clockr   Zkivy.uix.floatlayoutr   Zkivy.propertiesr   r   r   r   r   r   r   r   Zkivy.animationr   r   Zkivy.uix.relativelayoutr   Z	kivy.langr   Zkivy.graphicsr    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   	Exceptionr   r   r   r   r   r   r   r   r	   r   r
   r   r   r+   Zkivy.appr   Zkivy.uix.buttonr   load_stringr   runr/   r/   r/   r0   <module>   sB    ;(4T}x,H>97  R

