changeset 463:5f0705a9cd3b

install: set minimum version to Kivy 2.0.0: this version brings async support and bugfixes which were backported, thus backport is not needed anymore and has been removed.
author Goffi <goffi@goffi.org>
date Sat, 20 Mar 2021 14:21:15 +0100
parents 83c67b093350
children be8ed5e4c718
files cagou/backport/LICENSE cagou/backport/README cagou/backport/__init__.py cagou/backport/carousel.py cagou/core/cagou_main.py setup.py
diffstat 6 files changed, 1 insertions(+), 756 deletions(-) [+]
line wrap: on
line diff
--- a/cagou/backport/LICENSE	Sat Mar 20 13:51:27 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-Copyright (c) 2010-2019 Kivy Team and other contributors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
--- a/cagou/backport/README	Sat Mar 20 13:51:27 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-This directory include files backported from unreleased versions of Kivy until
-they are actually released. Note that the LICENSE in this directory apply only
-to this directory, other files follow general Cagou license specified in root
-directory's COPYING (AGPL v3+).
-
-The files here (except __init__.py) comes from Kivy repository (https://github.com/kivy/kivy) and are
-copyrighted to their respective authors
-
-"__init__.py" is part of Cagou and licensed under the same license (see root COPYING).
--- a/cagou/backport/__init__.py	Sat Mar 20 13:51:27 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-#!/usr/bin/env python3
-
-# Cagou: desktop/mobile frontend for Salut à Toi XMPP client
-# Copyright (C) 2016-2021 Jérôme Poisson (goffi@goffi.org)
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-
-# You should have received a copy of the GNU Affero General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-import kivy
-from sat.core import log as logging
-
-
-log = logging.getLogger(__name__)
-
-
-def do_backport():
-    version = tuple(kivy.parse_kivy_version(kivy.__version__)[0])
-    if version < (2, 0, 0):
-        log.info("installing backport for Carousel")
-        import sys
-        from kivy.uix import carousel  # NOQA
-        from . import carousel as carousel_backport
-        sys.modules['kivy.uix.carousel'] = carousel_backport
-    else:
-        log.info("Kivy >= 2.0.0 is used, Carousel backport is not needed anymore")
--- a/cagou/backport/carousel.py	Sat Mar 20 13:51:27 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,690 +0,0 @@
-'''
-Carousel
-========
-
-.. image:: images/carousel.gif
-    :align: right
-
-.. versionadded:: 1.4.0
-
-The :class:`Carousel` widget provides the classic mobile-friendly carousel view
-where you can swipe between slides.
-You can add any content to the carousel and have it move horizontally or
-vertically. The carousel can display pages in a sequence or a loop.
-
-Example::
-
-    from kivy.app import App
-    from kivy.uix.carousel import Carousel
-    from kivy.uix.image import AsyncImage
-
-
-    class CarouselApp(App):
-        def build(self):
-            carousel = Carousel(direction='right')
-            for i in range(10):
-                src = "http://placehold.it/480x270.png&text=slide-%d&.png" % i
-                image = AsyncImage(source=src, allow_stretch=True)
-                carousel.add_widget(image)
-            return carousel
-
-
-    CarouselApp().run()
-
-
-Kv Example::
-
-    Carousel:
-        direction: 'right'
-        AsyncImage:
-            source: 'http://placehold.it/480x270.png&text=slide-1.png'
-        AsyncImage:
-            source: 'http://placehold.it/480x270.png&text=slide-2.png'
-        AsyncImage:
-            source: 'http://placehold.it/480x270.png&text=slide-3.png'
-        AsyncImage:
-            source: 'http://placehold.it/480x270.png&text=slide-4.png'
-
-
-.. versionchanged:: 1.5.0
-    The carousel now supports active children, like the
-    :class:`~kivy.uix.scrollview.ScrollView`. It will detect a swipe gesture
-    according to the :attr:`Carousel.scroll_timeout` and
-    :attr:`Carousel.scroll_distance` properties.
-
-    In addition, the slide container is no longer exposed by the API.
-    The impacted properties are
-    :attr:`Carousel.slides`, :attr:`Carousel.current_slide`,
-    :attr:`Carousel.previous_slide` and :attr:`Carousel.next_slide`.
-
-'''
-
-__all__ = ('Carousel', )
-
-from functools import partial
-from kivy.clock import Clock
-from kivy.factory import Factory
-from kivy.animation import Animation
-from kivy.uix.stencilview import StencilView
-from kivy.uix.relativelayout import RelativeLayout
-from kivy.properties import BooleanProperty, OptionProperty, AliasProperty, \
-    NumericProperty, ListProperty, ObjectProperty, StringProperty
-
-
-class Carousel(StencilView):
-    '''Carousel class. See module documentation for more information.
-    '''
-
-    slides = ListProperty([])
-    '''List of slides inside the Carousel. The slides are the
-    widgets added to the Carousel using the :attr:`add_widget` method.
-
-    :attr:`slides` is a :class:`~kivy.properties.ListProperty` and is
-    read-only.
-    '''
-
-    def _get_slides_container(self):
-        return [x.parent for x in self.slides]
-
-    slides_container = AliasProperty(_get_slides_container, bind=('slides',))
-
-    direction = OptionProperty('right',
-                               options=('right', 'left', 'top', 'bottom'))
-    '''Specifies the direction in which the slides are ordered. This
-    corresponds to the direction from which the user swipes to go from one
-    slide to the next. It
-    can be `right`, `left`, `top`, or `bottom`. For example, with
-    the default value of `right`, the second slide is to the right
-    of the first and the user would swipe from the right towards the
-    left to get to the second slide.
-
-    :attr:`direction` is an :class:`~kivy.properties.OptionProperty` and
-    defaults to 'right'.
-    '''
-
-    min_move = NumericProperty(0.2)
-    '''Defines the minimum distance to be covered before the touch is
-    considered a swipe gesture and the Carousel content changed.
-    This is a expressed as a fraction of the Carousel's width.
-    If the movement doesn't reach this minimum value, the movement is
-    cancelled and the content is restored to its original position.
-
-    :attr:`min_move` is a :class:`~kivy.properties.NumericProperty` and
-    defaults to 0.2.
-    '''
-
-    anim_move_duration = NumericProperty(0.5)
-    '''Defines the duration of the Carousel animation between pages.
-
-    :attr:`anim_move_duration` is a :class:`~kivy.properties.NumericProperty`
-    and defaults to 0.5.
-    '''
-
-    anim_cancel_duration = NumericProperty(0.3)
-    '''Defines the duration of the animation when a swipe movement is not
-    accepted. This is generally when the user does not make a large enough
-    swipe. See :attr:`min_move`.
-
-    :attr:`anim_cancel_duration` is a :class:`~kivy.properties.NumericProperty`
-    and defaults to 0.3.
-    '''
-
-    loop = BooleanProperty(False)
-    '''Allow the Carousel to loop infinitely. If True, when the user tries to
-    swipe beyond last page, it will return to the first. If False, it will
-    remain on the last page.
-
-    :attr:`loop` is a :class:`~kivy.properties.BooleanProperty` and
-    defaults to False.
-    '''
-
-    def _get_index(self):
-        if self.slides:
-            return self._index % len(self.slides)
-        return None
-
-    def _set_index(self, value):
-        if self.slides:
-            self._index = value % len(self.slides)
-        else:
-            self._index = None
-
-    index = AliasProperty(_get_index, _set_index,
-                          bind=('_index', 'slides'),
-                          cache=True)
-    '''Get/Set the current slide based on the index.
-
-    :attr:`index` is an :class:`~kivy.properties.AliasProperty` and defaults
-    to 0 (the first item).
-    '''
-
-    def _prev_slide(self):
-        slides = self.slides
-        len_slides = len(slides)
-        index = self.index
-        if len_slides < 2:  # None, or 1 slide
-            return None
-        if self.loop and index == 0:
-            return slides[-1]
-        if index > 0:
-            return slides[index - 1]
-
-    previous_slide = AliasProperty(_prev_slide,
-                                   bind=('slides', 'index', 'loop'),
-                                   cache=True)
-    '''The previous slide in the Carousel. It is None if the current slide is
-    the first slide in the Carousel. This ordering reflects the order in which
-    the slides are added: their presentation varies according to the
-    :attr:`direction` property.
-
-    :attr:`previous_slide` is an :class:`~kivy.properties.AliasProperty`.
-
-    .. versionchanged:: 1.5.0
-        This property no longer exposes the slides container. It returns
-        the widget you have added.
-    '''
-
-    def _curr_slide(self):
-        if len(self.slides):
-            return self.slides[self.index or 0]
-
-    current_slide = AliasProperty(_curr_slide,
-                                  bind=('slides', 'index'),
-                                  cache=True)
-    '''The currently shown slide.
-
-    :attr:`current_slide` is an :class:`~kivy.properties.AliasProperty`.
-
-    .. versionchanged:: 1.5.0
-        The property no longer exposes the slides container. It returns
-        the widget you have added.
-    '''
-
-    def _next_slide(self):
-        if len(self.slides) < 2:  # None, or 1 slide
-            return None
-        if self.loop and self.index == len(self.slides) - 1:
-            return self.slides[0]
-        if self.index < len(self.slides) - 1:
-            return self.slides[self.index + 1]
-
-    next_slide = AliasProperty(_next_slide,
-                               bind=('slides', 'index', 'loop'),
-                               cache=True)
-    '''The next slide in the Carousel. It is None if the current slide is
-    the last slide in the Carousel. This ordering reflects the order in which
-    the slides are added: their presentation varies according to the
-    :attr:`direction` property.
-
-    :attr:`next_slide` is an :class:`~kivy.properties.AliasProperty`.
-
-    .. versionchanged:: 1.5.0
-        The property no longer exposes the slides container.
-        It returns the widget you have added.
-    '''
-
-    scroll_timeout = NumericProperty(200)
-    '''Timeout allowed to trigger the :attr:`scroll_distance`, in milliseconds.
-    If the user has not moved :attr:`scroll_distance` within the timeout,
-    no scrolling will occur and the touch event will go to the children.
-
-    :attr:`scroll_timeout` is a :class:`~kivy.properties.NumericProperty` and
-    defaults to 200 (milliseconds)
-
-    .. versionadded:: 1.5.0
-    '''
-
-    scroll_distance = NumericProperty('20dp')
-    '''Distance to move before scrolling the :class:`Carousel` in pixels. As
-    soon as the distance has been traveled, the :class:`Carousel` will start
-    to scroll, and no touch event will go to children.
-    It is advisable that you base this value on the dpi of your target device's
-    screen.
-
-    :attr:`scroll_distance` is a :class:`~kivy.properties.NumericProperty` and
-    defaults to 20dp.
-
-    .. versionadded:: 1.5.0
-    '''
-
-    anim_type = StringProperty('out_quad')
-    '''Type of animation to use while animating to the next/previous slide.
-    This should be the name of an
-    :class:`~kivy.animation.AnimationTransition` function.
-
-    :attr:`anim_type` is a :class:`~kivy.properties.StringProperty` and
-    defaults to 'out_quad'.
-
-    .. versionadded:: 1.8.0
-    '''
-
-    ignore_perpendicular_swipes = BooleanProperty(False)
-    '''Ignore swipes on axis perpendicular to direction.
-
-    :attr:`ignore_perpendicular_swipes` is a
-    :class:`~kivy.properties.BooleanProperty` and defaults to False.
-
-    .. versionadded:: 1.10.0
-    '''
-
-    # private properties, for internal use only ###
-    _index = NumericProperty(0, allownone=True)
-    _prev = ObjectProperty(None, allownone=True)
-    _current = ObjectProperty(None, allownone=True)
-    _next = ObjectProperty(None, allownone=True)
-    _offset = NumericProperty(0)
-    _touch = ObjectProperty(None, allownone=True)
-
-    _change_touch_mode_ev = None
-
-    def __init__(self, **kwargs):
-        self._trigger_position_visible_slides = Clock.create_trigger(
-            self._position_visible_slides, -1)
-        super(Carousel, self).__init__(**kwargs)
-        self._skip_slide = None
-        self.touch_mode_change = False
-        self._prioritize_next = False
-        self.fbind('loop', lambda *args: self._insert_visible_slides())
-
-    def load_slide(self, slide):
-        '''Animate to the slide that is passed as the argument.
-
-        .. versionchanged:: 1.8.0
-        '''
-        slides = self.slides
-        start, stop = slides.index(self.current_slide), slides.index(slide)
-        if start == stop:
-            return
-
-        self._skip_slide = stop
-        if stop > start:
-            self._prioritize_next = True
-            self._insert_visible_slides(_next_slide=slide)
-            self.load_next()
-        else:
-            self._prioritize_next = False
-            self._insert_visible_slides(_prev_slide=slide)
-            self.load_previous()
-
-    def load_previous(self):
-        '''Animate to the previous slide.
-
-        .. versionadded:: 1.7.0
-        '''
-        self.load_next(mode='prev')
-
-    def load_next(self, mode='next'):
-        '''Animate to the next slide.
-
-        .. versionadded:: 1.7.0
-        '''
-        if self.index is not None:
-            w, h = self.size
-            _direction = {
-                'top': -h / 2,
-                'bottom': h / 2,
-                'left': w / 2,
-                'right': -w / 2}
-            _offset = _direction[self.direction]
-            if mode == 'prev':
-                _offset = -_offset
-
-            self._start_animation(min_move=0, offset=_offset)
-
-    def get_slide_container(self, slide):
-        return slide.parent
-
-    @property
-    def _prev_equals_next(self):
-        return self.loop and len(self.slides) == 2
-
-    def _insert_visible_slides(self, _next_slide=None, _prev_slide=None):
-        get_slide_container = self.get_slide_container
-
-        previous_slide = _prev_slide if _prev_slide else self.previous_slide
-        if previous_slide:
-            self._prev = get_slide_container(previous_slide)
-        else:
-            self._prev = None
-
-        current_slide = self.current_slide
-        if current_slide:
-            self._current = get_slide_container(current_slide)
-        else:
-            self._current = None
-
-        next_slide = _next_slide if _next_slide else self.next_slide
-        if next_slide:
-            self._next = get_slide_container(next_slide)
-        else:
-            self._next = None
-
-        if self._prev_equals_next:
-            setattr(self, '_prev' if self._prioritize_next else '_next', None)
-
-        super_remove = super(Carousel, self).remove_widget
-        for container in self.slides_container:
-            super_remove(container)
-
-        if self._prev and self._prev.parent is not self:
-            super(Carousel, self).add_widget(self._prev)
-        if self._next and self._next.parent is not self:
-            super(Carousel, self).add_widget(self._next)
-        if self._current:
-            super(Carousel, self).add_widget(self._current)
-
-    def _position_visible_slides(self, *args):
-        slides, index = self.slides, self.index
-        no_of_slides = len(slides) - 1
-        if not slides:
-            return
-        x, y, width, height = self.x, self.y, self.width, self.height
-        _offset, direction = self._offset, self.direction[0]
-        _prev, _next, _current = self._prev, self._next, self._current
-        get_slide_container = self.get_slide_container
-        last_slide = get_slide_container(slides[-1])
-        first_slide = get_slide_container(slides[0])
-        skip_next = False
-        _loop = self.loop
-
-        if direction in 'rl':
-            xoff = x + _offset
-            x_prev = {'l': xoff + width, 'r': xoff - width}
-            x_next = {'l': xoff - width, 'r': xoff + width}
-            if _prev:
-                _prev.pos = (x_prev[direction], y)
-            elif _loop and _next and index == 0:
-                # if first slide is moving to right with direction set to right
-                # or toward left with direction set to left
-                if ((_offset > 0 and direction == 'r') or
-                        (_offset < 0 and direction == 'l')):
-                    # put last_slide before first slide
-                    last_slide.pos = (x_prev[direction], y)
-                    skip_next = True
-            if _current:
-                _current.pos = (xoff, y)
-            if skip_next:
-                return
-            if _next:
-                _next.pos = (x_next[direction], y)
-            elif _loop and _prev and index == no_of_slides:
-                if ((_offset < 0 and direction == 'r') or
-                        (_offset > 0 and direction == 'l')):
-                    first_slide.pos = (x_next[direction], y)
-        if direction in 'tb':
-            yoff = y + _offset
-            y_prev = {'t': yoff - height, 'b': yoff + height}
-            y_next = {'t': yoff + height, 'b': yoff - height}
-            if _prev:
-                _prev.pos = (x, y_prev[direction])
-            elif _loop and _next and index == 0:
-                if ((_offset > 0 and direction == 't') or
-                        (_offset < 0 and direction == 'b')):
-                    last_slide.pos = (x, y_prev[direction])
-                    skip_next = True
-            if _current:
-                _current.pos = (x, yoff)
-            if skip_next:
-                return
-            if _next:
-                _next.pos = (x, y_next[direction])
-            elif _loop and _prev and index == no_of_slides:
-                if ((_offset < 0 and direction == 't') or
-                        (_offset > 0 and direction == 'b')):
-                    first_slide.pos = (x, y_next[direction])
-
-    def on_size(self, *args):
-        size = self.size
-        for slide in self.slides_container:
-            slide.size = size
-        self._trigger_position_visible_slides()
-
-    def on_pos(self, *args):
-        self._trigger_position_visible_slides()
-
-    def on_index(self, *args):
-        self._insert_visible_slides()
-        self._trigger_position_visible_slides()
-        self._offset = 0
-
-    def on_slides(self, *args):
-        if self.slides:
-            self.index = self.index % len(self.slides)
-        self._insert_visible_slides()
-        self._trigger_position_visible_slides()
-
-    def on__offset(self, *args):
-        self._trigger_position_visible_slides()
-        # if reached full offset, switch index to next or prev
-        direction = self.direction[0]
-        _offset = self._offset
-        width = self.width
-        height = self.height
-        index = self.index
-        if self._skip_slide is not None or index is None:
-            return
-
-        # Move to next slide?
-        if (direction == 'r' and _offset <= -width) or \
-                (direction == 'l' and _offset >= width) or \
-                (direction == 't' and _offset <= - height) or \
-                (direction == 'b' and _offset >= height):
-            if self.next_slide:
-                self.index += 1
-
-        # Move to previous slide?
-        elif (direction == 'r' and _offset >= width) or \
-                (direction == 'l' and _offset <= -width) or \
-                (direction == 't' and _offset >= height) or \
-                (direction == 'b' and _offset <= -height):
-            if self.previous_slide:
-                self.index -= 1
-
-        elif self._prev_equals_next:
-            new_value = (_offset < 0) is (direction in 'rt')
-            if self._prioritize_next is not new_value:
-                self._prioritize_next = new_value
-                if new_value is (self._next is None):
-                    self._prev, self._next = self._next, self._prev
-
-    def _start_animation(self, *args, **kwargs):
-        # compute target offset for ease back, next or prev
-        new_offset = 0
-        direction = kwargs.get('direction', self.direction)[0]
-        is_horizontal = direction in 'rl'
-        extent = self.width if is_horizontal else self.height
-        min_move = kwargs.get('min_move', self.min_move)
-        _offset = kwargs.get('offset', self._offset)
-
-        if _offset < min_move * -extent:
-            new_offset = -extent
-        elif _offset > min_move * extent:
-            new_offset = extent
-
-        # if new_offset is 0, it wasnt enough to go next/prev
-        dur = self.anim_move_duration
-        if new_offset == 0:
-            dur = self.anim_cancel_duration
-
-        # detect edge cases if not looping
-        len_slides = len(self.slides)
-        index = self.index
-        if not self.loop or len_slides == 1:
-            is_first = (index == 0)
-            is_last = (index == len_slides - 1)
-            if direction in 'rt':
-                towards_prev = (new_offset > 0)
-                towards_next = (new_offset < 0)
-            else:
-                towards_prev = (new_offset < 0)
-                towards_next = (new_offset > 0)
-            if (is_first and towards_prev) or (is_last and towards_next):
-                new_offset = 0
-
-        anim = Animation(_offset=new_offset, d=dur, t=self.anim_type)
-        anim.cancel_all(self)
-
-        def _cmp(*l):
-            if self._skip_slide is not None:
-                self.index = self._skip_slide
-                self._skip_slide = None
-
-        anim.bind(on_complete=_cmp)
-        anim.start(self)
-
-    def _get_uid(self, prefix='sv'):
-        return '{0}.{1}'.format(prefix, self.uid)
-
-    def on_touch_down(self, touch):
-        if not self.collide_point(*touch.pos):
-            touch.ud[self._get_uid('cavoid')] = True
-            return
-        if self.disabled:
-            return True
-        if self._touch:
-            return super(Carousel, self).on_touch_down(touch)
-        Animation.cancel_all(self)
-        self._touch = touch
-        uid = self._get_uid()
-        touch.grab(self)
-        touch.ud[uid] = {
-            'mode': 'unknown',
-            'time': touch.time_start}
-        self._change_touch_mode_ev = Clock.schedule_once(
-            self._change_touch_mode, self.scroll_timeout / 1000.)
-        self.touch_mode_change = False
-        return True
-
-    def on_touch_move(self, touch):
-        if not self.touch_mode_change:
-            if self.ignore_perpendicular_swipes and \
-                    self.direction in ('top', 'bottom'):
-                if abs(touch.oy - touch.y) < self.scroll_distance:
-                    if abs(touch.ox - touch.x) > self.scroll_distance:
-                        self._change_touch_mode()
-                        self.touch_mode_change = True
-            elif self.ignore_perpendicular_swipes and \
-                    self.direction in ('right', 'left'):
-                if abs(touch.ox - touch.x) < self.scroll_distance:
-                    if abs(touch.oy - touch.y) > self.scroll_distance:
-                        self._change_touch_mode()
-                        self.touch_mode_change = True
-
-        if self._get_uid('cavoid') in touch.ud:
-            return
-        if self._touch is not touch:
-            super(Carousel, self).on_touch_move(touch)
-            return self._get_uid() in touch.ud
-        if touch.grab_current is not self:
-            return True
-        ud = touch.ud[self._get_uid()]
-        direction = self.direction[0]
-        if ud['mode'] == 'unknown':
-            if direction in 'rl':
-                distance = abs(touch.ox - touch.x)
-            else:
-                distance = abs(touch.oy - touch.y)
-            if distance > self.scroll_distance:
-                ev = self._change_touch_mode_ev
-                if ev is not None:
-                    ev.cancel()
-                ud['mode'] = 'scroll'
-        else:
-            if direction in 'rl':
-                self._offset += touch.dx
-            if direction in 'tb':
-                self._offset += touch.dy
-        return True
-
-    def on_touch_up(self, touch):
-        if self._get_uid('cavoid') in touch.ud:
-            return
-        if self in [x() for x in touch.grab_list]:
-            touch.ungrab(self)
-            self._touch = None
-            ud = touch.ud[self._get_uid()]
-            if ud['mode'] == 'unknown':
-                ev = self._change_touch_mode_ev
-                if ev is not None:
-                    ev.cancel()
-                super(Carousel, self).on_touch_down(touch)
-                Clock.schedule_once(partial(self._do_touch_up, touch), .1)
-            else:
-                self._start_animation()
-
-        else:
-            if self._touch is not touch and self.uid not in touch.ud:
-                super(Carousel, self).on_touch_up(touch)
-        return self._get_uid() in touch.ud
-
-    def _do_touch_up(self, touch, *largs):
-        super(Carousel, self).on_touch_up(touch)
-        # don't forget about grab event!
-        for x in touch.grab_list[:]:
-            touch.grab_list.remove(x)
-            x = x()
-            if not x:
-                continue
-            touch.grab_current = x
-            super(Carousel, self).on_touch_up(touch)
-        touch.grab_current = None
-
-    def _change_touch_mode(self, *largs):
-        if not self._touch:
-            return
-        self._start_animation()
-        uid = self._get_uid()
-        touch = self._touch
-        ud = touch.ud[uid]
-        if ud['mode'] == 'unknown':
-            touch.ungrab(self)
-            self._touch = None
-            super(Carousel, self).on_touch_down(touch)
-            return
-
-    def add_widget(self, widget, index=0, canvas=None):
-        container = RelativeLayout(
-            size=self.size, x=self.x - self.width, y=self.y)
-        container.add_widget(widget)
-        super(Carousel, self).add_widget(container, index, canvas)
-        if index != 0:
-            self.slides.insert(index - len(self.slides), widget)
-        else:
-            self.slides.append(widget)
-
-    def remove_widget(self, widget, *args, **kwargs):
-        # XXX be careful, the widget.parent refer to the RelativeLayout
-        # added in add_widget(). But it will break if RelativeLayout
-        # implementation change.
-        # if we passed the real widget
-        slides = self.slides
-        if widget in slides:
-            if self.index >= slides.index(widget):
-                self.index = max(0, self.index - 1)
-            container = widget.parent
-            slides.remove(widget)
-            super(Carousel, self).remove_widget(container)
-            return container.remove_widget(widget, *args, **kwargs)
-        return super(Carousel, self).remove_widget(widget, *args, **kwargs)
-
-    def clear_widgets(self):
-        for slide in self.slides[:]:
-            self.remove_widget(slide)
-        super(Carousel, self).clear_widgets()
-
-
-if __name__ == '__main__':
-    from kivy.app import App
-
-    class Example1(App):
-
-        def build(self):
-            carousel = Carousel(direction='left',
-                                loop=True)
-            for i in range(4):
-                src = "http://placehold.it/480x270.png&text=slide-%d&.png" % i
-                image = Factory.AsyncImage(source=src, allow_stretch=True)
-                carousel.add_widget(image)
-            return carousel
-
-    Example1().run()
--- a/cagou/core/cagou_main.py	Sat Mar 20 13:51:27 2021 +0100
+++ b/cagou/core/cagou_main.py	Sat Mar 20 14:21:15 2021 +0100
@@ -26,8 +26,6 @@
 from sat.core.i18n import _
 from . import kivy_hack
 kivy_hack.do_hack()
-from cagou.backport import do_backport
-do_backport()
 from .constants import Const as C
 from sat.core import log as logging
 from sat.core import exceptions
--- a/setup.py	Sat Mar 20 13:51:27 2021 +0100
+++ b/setup.py	Sat Mar 20 14:21:15 2021 +0100
@@ -24,7 +24,7 @@
 NAME = 'cagou'
 
 install_requires = [
-    'kivy>=1.10.0',
+    'kivy>=2.0.0',
     'kivy_garden.modernmenu',
     'pillow',
     'plyer',