From 3c88f476961db2408e7f0b0751f910cd4800f518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natt=C5=8Dsai=20Mit=C5=8D?= Date: Thu, 24 Aug 2023 19:30:33 +0900 Subject: [PATCH] stop using the word 'location' to represents the sizing/positioning properties of widget. --- README.md | 4 +- README_jp.md | 2 +- examples/flutter_style_draggable.py | 6 +- src/kivy_garden/draggable/__init__.py | 2 + src/kivy_garden/draggable/_impl.py | 39 +++++++---- src/kivy_garden/draggable/_utils.py | 34 +++++---- tests/test_import.py | 1 + tests/test_utils_restore_widget_location.py | 77 ++++++++++----------- tests/test_utils_save_widget_location.py | 20 +++--- 9 files changed, 101 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index a3cc2e4..7b38cdb 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ class KXDraggableBehavior: x=ctx.original_pos_win[0], y=ctx.original_pos_win[1], ) - restore_widget_location(self, ctx.original_location) + restore_widget_state(self, ctx.original_state) ``` If you don't need the animation, and want the draggable to go back instantly, overwrite the handler as follows: @@ -128,7 +128,7 @@ If you don't need the animation, and want the draggable to go back instantly, ov ```python class MyDraggable(KXDraggableBehavior, Widget): def on_drag_fail(self, touch, ctx): - restore_widget_location(self, ctx.original_location) + restore_widget_state(self, ctx.original_state) ``` Or if you want the draggable to not go back, and want it to stay the current position, overwrite the handler as follows: diff --git a/README_jp.md b/README_jp.md index 6894a7f..be7fb61 100644 --- a/README_jp.md +++ b/README_jp.md @@ -122,7 +122,7 @@ dragが失敗/成功/中止した時に何をするかは完全にあなたに ```python class MyDraggable(KXDraggableBehavior, Widget): def on_drag_fail(self, touch, ctx): - restore_widget_location(self, ctx.original_location) + restore_widget_state(self, ctx.original_state) ``` また何もせずにその場に残って欲しいなら以下のようにすれば良い。 diff --git a/examples/flutter_style_draggable.py b/examples/flutter_style_draggable.py index 29eeaa2..5becab4 100644 --- a/examples/flutter_style_draggable.py +++ b/examples/flutter_style_draggable.py @@ -9,7 +9,7 @@ from kivy.uix.floatlayout import FloatLayout from kivy.uix.screenmanager import ScreenManager, NoTransition -from kivy_garden.draggable import KXDroppableBehavior, KXDraggableBehavior, restore_widget_location +from kivy_garden.draggable import KXDroppableBehavior, KXDraggableBehavior, restore_widget_state KV_CODE = ''' : @@ -64,9 +64,9 @@ def __on_is_being_dragged(self, value): def on_drag_start(self, touch, ctx): if self.has_screen('childWhenDragging'): - restore_widget_location( + restore_widget_state( self.get_screen('childWhenDragging'), - ctx.original_location, + ctx.original_state, ) return super().on_drag_start(touch, ctx) diff --git a/src/kivy_garden/draggable/__init__.py b/src/kivy_garden/draggable/__init__.py index d1d4dc0..8052073 100644 --- a/src/kivy_garden/draggable/__init__.py +++ b/src/kivy_garden/draggable/__init__.py @@ -1,7 +1,9 @@ __all__ = ( 'KXDraggableBehavior', 'KXDroppableBehavior', 'KXReorderableBehavior', + 'save_widget_state', 'restore_widget_state', 'save_widget_location', 'restore_widget_location', 'ongoing_drags', ) from ._impl import KXDraggableBehavior, KXDroppableBehavior, KXReorderableBehavior, ongoing_drags +from ._utils import save_widget_state, restore_widget_state from ._utils import save_widget_location, restore_widget_location diff --git a/src/kivy_garden/draggable/_impl.py b/src/kivy_garden/draggable/_impl.py index 9bebdef..26b8a24 100644 --- a/src/kivy_garden/draggable/_impl.py +++ b/src/kivy_garden/draggable/_impl.py @@ -6,8 +6,8 @@ from inspect import isawaitable from dataclasses import dataclass from contextlib import nullcontext +from functools import cached_property -from kivy.config import Config from kivy.properties import ( BooleanProperty, ListProperty, StringProperty, NumericProperty, OptionProperty, AliasProperty, ) @@ -20,7 +20,7 @@ from ._utils import ( temp_transform, temp_grab_current, _create_spacer, - save_widget_location, restore_widget_location, + save_widget_state, restore_widget_state, ) @@ -31,13 +31,24 @@ class DragContext: started. (window coordinates). ''' - original_location: dict = None - '''(read-only) Where the draggable came from. This can be passed to ``restore_widget_location()``. ''' + original_state: dict = None + ''' + (read-only) The state of the draggable at the time the drag has started. + This can be passed to ``restore_widget_state()``. + ''' droppable: Union[None, 'KXDroppableBehavior', 'KXReorderableBehavior'] = None '''(read-only) The widget where the draggable dropped to. This is always None on_drag_start/on_drag_cancel, and is always a widget on_drag_succeed, and can be either on_drag_fail/on_drag_end.''' + @cached_property + def original_location(self) -> dict: + ''' + This exists solely for backward compatibility. + Use :attr:`original_state` instead. + ''' + return self.original_state + class KXDraggableBehavior: __events__ = ( @@ -155,10 +166,10 @@ async def _treat_a_touch_as_a_drag(self, touch, *, do_transform=False, touch_rec from kivy.core.window import Window window = Window touch_ud = touch.ud - original_location = save_widget_location(self) + original_state = save_widget_state(self) ctx = DragContext( original_pos_win=original_pos_win, - original_location=original_location, + original_state=original_state, ) # move self under the Window @@ -252,11 +263,11 @@ def on_drag_end(self, touch, ctx: DragContext): pass def on_drag_succeed(self, touch, ctx: DragContext): - original_location = ctx.original_location + original_state = ctx.original_state self.parent.remove_widget(self) - self.size_hint_x = original_location['size_hint_x'] - self.size_hint_y = original_location['size_hint_y'] - self.pos_hint = original_location['pos_hint'] + self.size_hint_x = original_state['size_hint_x'] + self.size_hint_y = original_state['size_hint_y'] + self.pos_hint = original_state['pos_hint'] ctx.droppable.add_widget(self, index=touch.ud.get('kivyx_droppable_index', 0)) async def on_drag_fail(self, touch, ctx: DragContext): @@ -265,10 +276,10 @@ async def on_drag_fail(self, touch, ctx: DragContext): x=ctx.original_pos_win[0], y=ctx.original_pos_win[1], ) - restore_widget_location(self, ctx.original_location) + restore_widget_state(self, ctx.original_state) def on_drag_cancel(self, touch, ctx: DragContext): - restore_widget_location(self, ctx.original_location) + restore_widget_state(self, ctx.original_state) def ongoing_drags(*, window=kivy.core.window.Window) -> List[KXDraggableBehavior]: @@ -376,9 +387,9 @@ async def _watch_touch(self, touch): touch_ud = touch.ud try: - restore_widget_location( + restore_widget_state( spacer, - touch_ud['kivyx_drag_ctx'].original_location, + touch_ud['kivyx_drag_ctx'].original_state, ignore_parent=True) add_widget(spacer) async with ak.watch_touch(self, touch) as is_touch_move: diff --git a/src/kivy_garden/draggable/_utils.py b/src/kivy_garden/draggable/_utils.py index 0d8c2ea..23215a3 100644 --- a/src/kivy_garden/draggable/_utils.py +++ b/src/kivy_garden/draggable/_utils.py @@ -1,5 +1,6 @@ __all__ = ( 'temp_transform', 'temp_grab_current', + 'save_widget_state', 'restore_widget_state', 'save_widget_location', 'restore_widget_location', ) @@ -45,29 +46,29 @@ def __exit__(self, *args): ) -def save_widget_location(widget, *, ignore_parent=False) -> dict: +def save_widget_state(widget, *, ignore_parent=False) -> dict: w = widget.__self__ - location = {name: getattr(w, name) for name in _shallow_copyable_property_names} - location['pos_hint'] = deepcopy(w.pos_hint) + state = {name: getattr(w, name) for name in _shallow_copyable_property_names} + state['pos_hint'] = deepcopy(w.pos_hint) if ignore_parent: - return location + return state parent = w.parent if parent is None: - location['weak_parent'] = None + state['weak_parent'] = None else: - location['weak_parent'] = ref(parent) - location['index'] = parent.children.index(w) - return location + state['weak_parent'] = ref(parent) + state['index'] = parent.children.index(w) + return state -def restore_widget_location(widget, location: dict, *, ignore_parent=False): +def restore_widget_state(widget, state: dict, *, ignore_parent=False): w = widget.__self__ for name in _shallow_copyable_property_names: - setattr(w, name, location[name]) - w.pos_hint = deepcopy(location['pos_hint']) - if ignore_parent or 'weak_parent' not in location: + setattr(w, name, state[name]) + w.pos_hint = deepcopy(state['pos_hint']) + if ignore_parent or 'weak_parent' not in state: return - weak_parent = location['weak_parent'] + weak_parent = state['weak_parent'] if weak_parent is None: parent = None else: @@ -85,7 +86,7 @@ def restore_widget_location(widget, location: dict, *, ignore_parent=False): if parent.parent is parent: parent.add_widget(w) else: - parent.add_widget(w, index=location['index']) + parent.add_widget(w, index=state['index']) def _create_spacer(**kwargs): @@ -106,3 +107,8 @@ def _create_spacer(**kwargs): size=lambda __, value: setattr(rect_inst, 'size', value), ) return spacer + + +# leave the old names for backward compatibility +save_widget_location = save_widget_state +restore_widget_location = restore_widget_state diff --git a/tests/test_import.py b/tests/test_import.py index 66e76b9..b16d202 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -4,5 +4,6 @@ def test_flower(): from kivy_garden.draggable import ( KXDraggableBehavior, KXDroppableBehavior, KXReorderableBehavior, + restore_widget_state, save_widget_state, restore_widget_location, save_widget_location, ongoing_drags, ) diff --git a/tests/test_utils_restore_widget_location.py b/tests/test_utils_restore_widget_location.py index c91202e..1be4e72 100644 --- a/tests/test_utils_restore_widget_location.py +++ b/tests/test_utils_restore_widget_location.py @@ -2,31 +2,31 @@ @pytest.fixture(scope='module') -def location_factory(): +def state_factory(): from copy import deepcopy - loc = { + state = { 'x': 2, 'y': 2, 'width': 2, 'height': 2, 'size_hint_x': 2, 'size_hint_y': 2, 'pos_hint': {'center': [2, 2, ], }, 'size_hint_min_x': 2, 'size_hint_min_y': 2, 'size_hint_max_x': 2, 'size_hint_max_y': 2, } - return lambda: deepcopy(loc) + return lambda: deepcopy(state) @pytest.mark.parametrize('ignore_parent', (True, False, )) -def test_sizing_info(location_factory, ignore_parent): +def test_sizing_info(state_factory, ignore_parent): from kivy.uix.widget import Widget - from kivy_garden.draggable import restore_widget_location + from kivy_garden.draggable import restore_widget_state w = Widget() - loc = location_factory() - restore_widget_location(w, loc, ignore_parent=ignore_parent) - loc['width'] = 0 - loc['x'] = 0 - loc['size_hint_x'] = 0 - loc['pos_hint']['center'][0] = 0 - loc['size_hint_min_x'] = 0 - loc['size_hint_min_y'] = 0 + state = state_factory() + restore_widget_state(w, state, ignore_parent=ignore_parent) + state['width'] = 0 + state['x'] = 0 + state['size_hint_x'] = 0 + state['pos_hint']['center'][0] = 0 + state['size_hint_min_x'] = 0 + state['size_hint_min_y'] = 0 assert w.size == [2, 2, ] assert w.pos == [2, 2, ] assert w.size_hint == [2, 2, ] @@ -37,16 +37,16 @@ def test_sizing_info(location_factory, ignore_parent): @pytest.mark.parametrize('ignore_parent', (True, False, )) @pytest.mark.parametrize('has_parent', (True, False, )) -def test_weak_parent_is_none(location_factory, ignore_parent, has_parent): +def test_weak_parent_is_none(state_factory, ignore_parent, has_parent): from kivy.uix.widget import Widget - from kivy_garden.draggable import restore_widget_location - loc = location_factory() - loc['weak_parent'] = None + from kivy_garden.draggable import restore_widget_state + state = state_factory() + state['weak_parent'] = None w = Widget() if has_parent: parent = Widget() parent.add_widget(w) - restore_widget_location(w, loc, ignore_parent=ignore_parent) + restore_widget_state(w, state, ignore_parent=ignore_parent) if ignore_parent and has_parent: assert w.parent is parent else: @@ -55,17 +55,16 @@ def test_weak_parent_is_none(location_factory, ignore_parent, has_parent): @pytest.mark.parametrize('ignore_parent', (True, False, )) @pytest.mark.parametrize('has_parent', (True, False, )) -def test_weak_parent_not_in_the_keys( - location_factory, ignore_parent, has_parent): +def test_weak_parent_not_in_the_keys(state_factory, ignore_parent, has_parent): from kivy.uix.widget import Widget - from kivy_garden.draggable import restore_widget_location - loc = location_factory() - assert 'weak_parent' not in loc + from kivy_garden.draggable import restore_widget_state + state = state_factory() + assert 'weak_parent' not in state w = Widget() if has_parent: parent = Widget() parent.add_widget(w) - restore_widget_location(w, loc, ignore_parent=ignore_parent) + restore_widget_state(w, state, ignore_parent=ignore_parent) if has_parent: assert w.parent is parent else: @@ -74,22 +73,21 @@ def test_weak_parent_not_in_the_keys( @pytest.mark.parametrize('ignore_parent', (True, False, )) @pytest.mark.parametrize('has_parent', (True, False, )) -def test_weak_parent_is_alive( - location_factory, ignore_parent, has_parent): +def test_weak_parent_is_alive(state_factory, ignore_parent, has_parent): import weakref from kivy.uix.widget import Widget - from kivy_garden.draggable import restore_widget_location + from kivy_garden.draggable import restore_widget_state prev_parent = Widget() - loc = location_factory() - loc['weak_parent'] = weakref.ref(prev_parent) - loc['index'] = 0 + state = state_factory() + state['weak_parent'] = weakref.ref(prev_parent) + state['index'] = 0 w = Widget() if has_parent: parent = Widget() parent.add_widget(w) parent.add_widget(Widget()) assert parent.children.index(w) == 1 - restore_widget_location(w, loc, ignore_parent=ignore_parent) + restore_widget_state(w, state, ignore_parent=ignore_parent) if ignore_parent: if has_parent: assert w.parent is parent @@ -103,17 +101,16 @@ def test_weak_parent_is_alive( @pytest.mark.parametrize('ignore_parent', (True, False, )) @pytest.mark.parametrize('has_parent', (True, False, )) -def test_weak_parent_is_dead( - location_factory, ignore_parent, has_parent): +def test_weak_parent_is_dead(state_factory, ignore_parent, has_parent): import gc import weakref from kivy.uix.widget import Widget - from kivy_garden.draggable import restore_widget_location - loc = location_factory() - loc['weak_parent'] = weakref.ref(Widget()) - loc['index'] = 0 + from kivy_garden.draggable import restore_widget_state + state = state_factory() + state['weak_parent'] = weakref.ref(Widget()) + state['index'] = 0 gc.collect() - assert loc['weak_parent']() is None + assert state['weak_parent']() is None w = Widget() if has_parent: parent = Widget() @@ -121,10 +118,10 @@ def test_weak_parent_is_dead( parent.add_widget(Widget()) assert parent.children.index(w) == 1 if ignore_parent: - restore_widget_location(w, loc, ignore_parent=True) + restore_widget_state(w, state, ignore_parent=True) else: with pytest.raises(ReferenceError): - restore_widget_location(w, loc, ignore_parent=False) + restore_widget_state(w, state, ignore_parent=False) if has_parent: assert w.parent is parent assert parent.children.index(w) == 1 diff --git a/tests/test_utils_save_widget_location.py b/tests/test_utils_save_widget_location.py index 256ed55..92dc607 100644 --- a/tests/test_utils_save_widget_location.py +++ b/tests/test_utils_save_widget_location.py @@ -4,9 +4,9 @@ @pytest.mark.parametrize('ignore_parent', (True, False, )) def test_no_parent(ignore_parent): from kivy.uix.widget import Widget - from kivy_garden.draggable import save_widget_location + from kivy_garden.draggable import save_widget_state w = Widget() - location = save_widget_location(w, ignore_parent=ignore_parent) + state = save_widget_state(w, ignore_parent=ignore_parent) expectation = { 'x': 0, 'y': 0, 'width': 100, 'height': 100, 'size_hint_x': 1, 'size_hint_y': 1, 'pos_hint': {}, @@ -16,19 +16,19 @@ def test_no_parent(ignore_parent): } if ignore_parent: del expectation['weak_parent'] - assert location == expectation + assert state == expectation @pytest.mark.parametrize('ignore_parent', (True, False, )) def test_has_parent(ignore_parent): import weakref from kivy.uix.widget import Widget - from kivy_garden.draggable import save_widget_location + from kivy_garden.draggable import save_widget_state parent = Widget() w = Widget() parent.add_widget(w) parent.add_widget(Widget()) - location = save_widget_location(w, ignore_parent=ignore_parent) + state = save_widget_state(w, ignore_parent=ignore_parent) expectation = { 'x': 0, 'y': 0, 'width': 100, 'height': 100, 'size_hint_x': 1, 'size_hint_y': 1, 'pos_hint': {}, @@ -39,15 +39,15 @@ def test_has_parent(ignore_parent): if ignore_parent: del expectation['weak_parent'] del expectation['index'] - assert location == expectation + assert state == expectation @pytest.mark.parametrize('ignore_parent', (True, False, )) def test_pos_hint_is_deepcopied(ignore_parent): from kivy.uix.widget import Widget - from kivy_garden.draggable import save_widget_location + from kivy_garden.draggable import save_widget_state w = Widget(pos_hint={'center': [.5, .5, ], }) - location = save_widget_location(w, ignore_parent=ignore_parent) - location['pos_hint']['center'][0] = 0 - location['pos_hint']['x'] = 0 + state = save_widget_state(w, ignore_parent=ignore_parent) + state['pos_hint']['center'][0] = 0 + state['pos_hint']['x'] = 0 assert w.pos_hint == {'center': [.5, .5, ], }