2022-07-07 22:16:10 +02:00
|
|
|
"""
|
|
|
|
Components/Transition
|
|
|
|
=====================
|
|
|
|
|
|
|
|
.. rubric::
|
|
|
|
A set of classes for implementing transitions between application screens.
|
|
|
|
|
|
|
|
.. versionadded:: 1.0.0
|
|
|
|
|
|
|
|
Changing transitions
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
You have multiple transitions available by default, such as:
|
|
|
|
|
|
|
|
- :class:`MDFadeSlideTransition`
|
|
|
|
state one: the new screen closes the previous screen by lifting from the
|
|
|
|
bottom of the screen and changing from transparent to non-transparent;
|
|
|
|
|
|
|
|
state two: the current screen goes down to the bottom of the screen,
|
|
|
|
passing from a non-transparent state to a transparent one, thus opening the
|
|
|
|
previous screen;
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
You cannot control the direction of a slide using the direction attribute.
|
|
|
|
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/transition-md-fade-slide-transition.gif
|
|
|
|
:align: center
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
__all__ = (
|
|
|
|
"MDFadeSlideTransition",
|
|
|
|
"MDSlideTransition",
|
|
|
|
"MDSwapTransition",
|
|
|
|
"MDTransitionBase",
|
|
|
|
)
|
|
|
|
|
|
|
|
from kivy.animation import Animation, AnimationTransition
|
|
|
|
from kivy.uix.screenmanager import (
|
|
|
|
ScreenManagerException,
|
|
|
|
SlideTransition,
|
|
|
|
SwapTransition,
|
|
|
|
TransitionBase,
|
|
|
|
)
|
|
|
|
|
|
|
|
from kivymd.uix.screenmanager import MDScreenManager
|
|
|
|
|
|
|
|
|
|
|
|
class MDTransitionBase(TransitionBase):
|
|
|
|
_direction = "in"
|
2022-10-03 00:46:03 +02:00
|
|
|
hero_widget = None
|
|
|
|
hero_from_widget = None # kivymd.uix.hero.MDHeroFrom object
|
2022-07-07 22:16:10 +02:00
|
|
|
|
|
|
|
def start(self, instance_screen_manager: MDScreenManager) -> None:
|
|
|
|
super().start(instance_screen_manager)
|
|
|
|
|
|
|
|
{"in": self.animated_hero_in, "out": self.animated_hero_out}[
|
|
|
|
self._direction
|
|
|
|
]()
|
|
|
|
|
|
|
|
def animated_hero_in(self) -> None:
|
2022-10-03 00:46:03 +02:00
|
|
|
if self.manager._heroes_data and self.manager.current_hero:
|
|
|
|
self.hero_from_widget = self.manager.get_hero_from_widget()
|
|
|
|
self._check_widget_properties()
|
|
|
|
self.hero_widget = self.hero_from_widget.children[0]
|
|
|
|
self.hero_from_widget.remove_widget(self.hero_widget)
|
|
|
|
|
|
|
|
self.hero_widget.pos = self.screen_out.to_widget(
|
|
|
|
*self.hero_from_widget.to_window(*self.hero_from_widget.pos)
|
|
|
|
)
|
|
|
|
self.hero_widget.size = self.hero_from_widget.size
|
|
|
|
self.manager.get_root_window().add_widget(self.hero_widget)
|
|
|
|
|
|
|
|
Animation(
|
|
|
|
size=self.screen_in.hero_to.size,
|
|
|
|
d=self.duration,
|
|
|
|
pos=self.screen_in.hero_to.pos,
|
|
|
|
).start(self.hero_widget)
|
|
|
|
self.hero_from_widget.dispatch(
|
|
|
|
"on_transform_in", self.hero_widget, self.duration
|
|
|
|
)
|
2022-07-07 22:16:10 +02:00
|
|
|
|
|
|
|
def animated_hero_out(self) -> None:
|
2022-10-03 00:46:03 +02:00
|
|
|
if self.manager._heroes_data and self.manager.current_hero:
|
|
|
|
self.screen_out.hero_to.remove_widget(self.hero_widget)
|
|
|
|
self.manager.get_root_window().add_widget(self.hero_widget)
|
2022-07-07 22:16:10 +02:00
|
|
|
|
2022-10-03 00:46:03 +02:00
|
|
|
self.hero_from_widget.dispatch(
|
|
|
|
"on_transform_out", self.hero_widget, self.duration
|
|
|
|
)
|
|
|
|
Animation(
|
|
|
|
pos=self.screen_in.to_widget(
|
|
|
|
*self.hero_from_widget.to_window(*self.hero_from_widget.pos)
|
|
|
|
),
|
|
|
|
size=self.hero_from_widget.size,
|
|
|
|
d=self.duration,
|
|
|
|
).start(self.hero_widget)
|
2022-10-02 17:16:59 +02:00
|
|
|
|
2022-10-03 00:46:03 +02:00
|
|
|
def on_complete(self) -> None:
|
2022-07-07 22:16:10 +02:00
|
|
|
super().on_complete()
|
|
|
|
|
|
|
|
if self._direction == "out":
|
|
|
|
self._direction = "in"
|
2022-10-03 00:46:03 +02:00
|
|
|
if self.manager._heroes_data and self.manager.current_hero:
|
|
|
|
self.manager.get_root_window().remove_widget(self.hero_widget)
|
|
|
|
self.hero_from_widget.add_widget(self.hero_widget)
|
2022-07-07 22:16:10 +02:00
|
|
|
else:
|
|
|
|
self._direction = "out"
|
2022-10-03 00:46:03 +02:00
|
|
|
if self.manager._heroes_data and self.manager.current_hero:
|
|
|
|
self.manager.get_root_window().remove_widget(self.hero_widget)
|
|
|
|
self.screen_in.hero_to.add_widget(self.hero_widget)
|
2022-07-07 22:16:10 +02:00
|
|
|
|
2022-10-03 00:46:03 +02:00
|
|
|
def _check_widget_properties(self):
|
|
|
|
if not self.screen_in.hero_to:
|
2022-07-07 22:16:10 +02:00
|
|
|
raise Exception(
|
2022-10-03 00:46:03 +02:00
|
|
|
f"The `hero_to` attribute is not specified for screen {self.screen_in}"
|
2022-07-07 22:16:10 +02:00
|
|
|
)
|
2022-10-03 00:46:03 +02:00
|
|
|
if len(self.hero_from_widget.children) > 1:
|
2022-07-07 22:16:10 +02:00
|
|
|
raise Exception(
|
2022-10-03 00:46:03 +02:00
|
|
|
f"{self.hero_from_widget.__class__} accept only one widget"
|
2022-07-07 22:16:10 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class MDSwapTransition(SwapTransition, MDTransitionBase):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class MDSlideTransition(SlideTransition, MDTransitionBase):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class MDFadeSlideTransition(MDSlideTransition):
|
|
|
|
def start(self, instance_screen_manager: MDScreenManager) -> None:
|
|
|
|
if self.is_active:
|
|
|
|
raise ScreenManagerException("start() is called twice!")
|
|
|
|
|
|
|
|
self.manager = instance_screen_manager
|
|
|
|
self._anim = Animation(d=self.duration, s=0)
|
|
|
|
self._anim.bind(
|
|
|
|
on_progress=self._on_progress, on_complete=self._on_complete
|
|
|
|
)
|
|
|
|
|
|
|
|
if self._direction == "in":
|
|
|
|
self.add_screen(self.screen_in)
|
|
|
|
self.animated_hero_in()
|
|
|
|
else:
|
|
|
|
self.animated_hero_out()
|
|
|
|
self.add_screen(self.screen_in)
|
|
|
|
self.add_screen(self.screen_out)
|
|
|
|
|
|
|
|
self.screen_in.transition_progress = 0.0
|
|
|
|
self.screen_in.transition_state = "in"
|
|
|
|
self.screen_out.transition_progress = 0.0
|
|
|
|
self.screen_out.transition_state = "out"
|
|
|
|
self.screen_in.dispatch("on_pre_enter")
|
|
|
|
self.screen_out.dispatch("on_pre_leave")
|
|
|
|
|
|
|
|
self.is_active = True
|
|
|
|
self._anim.start(self)
|
|
|
|
self.dispatch("on_progress", 0)
|
|
|
|
|
|
|
|
if self._direction == "in":
|
|
|
|
self.screen_in.y = 0
|
|
|
|
self.screen_in.opacity = 0
|
|
|
|
|
|
|
|
def on_progress(self, progression: float) -> None:
|
|
|
|
progression = AnimationTransition.out_quad(progression)
|
|
|
|
|
|
|
|
if self._direction == "in":
|
|
|
|
self.screen_in.y = (
|
|
|
|
self.manager.y + self.manager.height * progression
|
|
|
|
) - self.screen_in.height
|
|
|
|
self.screen_in.opacity = progression
|
|
|
|
if self._direction == "out":
|
|
|
|
self.screen_out.y = (
|
|
|
|
self.manager.y - self.manager.height * progression
|
|
|
|
)
|
|
|
|
self.screen_out.opacity = 1 - progression
|