""" 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" hero_widget = None hero_from_widget = None # kivymd.uix.hero.MDHeroFrom object 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: 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 ) def animated_hero_out(self) -> None: 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) 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) def on_complete(self) -> None: super().on_complete() if self._direction == "out": self._direction = "in" 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) else: self._direction = "out" 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) def _check_widget_properties(self): if not self.screen_in.hero_to: raise Exception( f"The `hero_to` attribute is not specified for screen {self.screen_in}" ) if len(self.hero_from_widget.children) > 1: raise Exception( f"{self.hero_from_widget.__class__} accept only one widget" ) 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