async def job(w, t): if actually_ended: with pytest.raises(MotionEventAlreadyEndedError): async for __ in ak.rest_of_touch_moves(w, t): pass else: async for __ in ak.rest_of_touch_moves(w, t): pass
async def draw_rect(self, touch): from kivy.graphics import Line, Color, Rectangle, InstructionGroup from kivy.utils import get_random_color inst_group = InstructionGroup() self.canvas.add(inst_group) inst_group.add(Color(*get_random_color())) line = Line(width=2) inst_group.add(line) ox, oy = self.to_local(*touch.opos) on_touch_move_was_fired = False async for __ in ak.rest_of_touch_moves(self, touch): # Don't await anything during this async-for-loop on_touch_move_was_fired = True x, y = self.to_local(*touch.pos) min_x = min(x, ox) min_y = min(y, oy) max_x = max(x, ox) max_y = max(y, oy) line.rectangle = [min_x, min_y, max_x - min_x, max_y - min_y] if on_touch_move_was_fired: inst_group.add(Color(*get_random_color(alpha=.3))) inst_group.add( Rectangle( pos=(min_x, min_y), size=( max_x - min_x, max_y - min_y, ), )) else: self.canvas.remove(inst_group)
async def _test(w, t): n = 0 async for __ in ak.rest_of_touch_moves(w, t): n += 1 assert n == n_touch_moves nonlocal done done = True
async def _watch_touch(self, touch): spacer = self._inactive_spacers.pop() self._active_spacers.append(spacer) # assigning to a local variable might improve performance collide_point = self.collide_point get_drop_insertion_index_move = self.get_drop_insertion_index_move remove_widget = self.remove_widget add_widget = self.add_widget touch_ud = touch.ud try: restore_widget_location( spacer, touch_ud['kivyx_draggable'].drag_context.original_location, ignore_parent=True) add_widget(spacer) async for __ in ak.rest_of_touch_moves(self, touch): x, y = touch.pos if collide_point(x, y): new_idx = get_drop_insertion_index_move(x, y, spacer) if new_idx is not None: remove_widget(spacer) add_widget(spacer, index=new_idx) else: del touch_ud[self.__ud_key] return if 'kivyx_droppable' not in touch_ud: touch_ud['kivyx_droppable'] = self touch_ud['kivyx_droppable_index'] = self.children.index(spacer) finally: self.remove_widget(spacer) self._inactive_spacers.append(spacer) self._active_spacers.remove(spacer)
async def draw_rect(self, touch): from kivy.graphics import Line, Color, Rectangle, InstructionGroup from kivy.utils import get_random_color inst_group = InstructionGroup() self.canvas.add(inst_group) inst_group.add(Color(*get_random_color())) line = Line(width=2) inst_group.add(line) ox, oy = x, y = self.to_local(*touch.opos) async for __ in ak.rest_of_touch_moves(self, touch, stop_dispatching=True): # Don't await anything during the iteration x, y = self.to_local(*touch.pos) min_x, max_x = (x, ox) if x < ox else (ox, x) min_y, max_y = (y, oy) if y < oy else (oy, y) line.rectangle = ( min_x, min_y, max_x - min_x, max_y - min_y, ) if x == ox and y == oy: self.canvas.remove(inst_group) else: inst_group.add(Color(*get_random_color(alpha=.3))) inst_group.add( Rectangle( pos=(min_x, min_y), size=( max_x - min_x, max_y - min_y, ), ))
async def _true_when_a_touch_ended_false_when_it_moved_too_much( self, touch): drag_distance = self.drag_distance ox, oy = touch.opos async for __ in ak.rest_of_touch_moves(self, touch): dx = abs(touch.x - ox) dy = abs(touch.y - oy) if dy > drag_distance or dx > drag_distance: return False return True
async def _watch_touch_movement(self, touch): draggable = touch.ud['kivyx_draggable'] collide_point = self.collide_point self.dispatch('on_drag_enter', touch, draggable) try: async for __ in ak.rest_of_touch_moves(self, touch): if not collide_point(*touch.pos): return finally: self.dispatch('on_drag_leave', touch, draggable) del touch.ud[self.__ud_key]
async def _test(w, t): import weakref nonlocal n_touch_moves weak_w = weakref.ref(w) assert weak_w not in t.grab_list async for __ in ak.rest_of_touch_moves(w, t): assert weak_w in t.grab_list n_touch_moves += 1 if n_touch_moves == 2: break assert weak_w not in t.grab_list await ak.event(w, 'on_touch_up')
async def _test(w, t): import weakref nonlocal n_touch_moves weak_w = weakref.ref(w) assert weak_w not in t.grab_list if holds_a_ref: agen = ak.rest_of_touch_moves(w, t) async for __ in agen: assert weak_w in t.grab_list n_touch_moves += 1 if n_touch_moves == 2: break else: async for __ in ak.rest_of_touch_moves(w, t): assert weak_w in t.grab_list n_touch_moves += 1 if n_touch_moves == 2: break assert weak_w not in t.grab_list # fails if holds_a_ref is True await ak.event(w, 'on_touch_up') nonlocal done done = True
async def _true_when_a_touch_ended_false_when_it_moved_too_much( self, touch): # assigning to a local variable might improve performance abs_ = abs drag_distance = self.drag_distance ox, oy = touch.opos async for __ in ak.rest_of_touch_moves(self, touch): dx = abs_(touch.x - ox) dy = abs_(touch.y - oy) if dy > drag_distance or dx > drag_distance: return False return True
async def _watch_touch_events(self): from asynckivy import animate, rest_of_touch_moves, event, MotionEventAlreadyEndedError, cancel_protection def accepts_touch(w, t) -> bool: return w.collide_point(*t.opos) and (not t.is_mouse_scrolling) # 'itertools.cycle()' is no use here because it cannot react to # the property changes. There might be a better way than this, though. def color_iter(w): while True: yield w.border_color2 yield w.border_color1 color_iter = color_iter(self) def change_border_color(dt): self._border_color = next(color_iter) blink_ev = Clock.create_trigger(change_border_color, .1, interval=True) collide_point = self.collide_point dispatch = self.dispatch try: while True: __, touch = await event(self, 'on_touch_down', filter=accepts_touch, stop_dispatching=True) dispatch('on_press') blink_ev() try: async for __ in rest_of_touch_moves(self, touch, stop_dispatching=True): if collide_point(*touch.pos): blink_ev() else: blink_ev.cancel() self._border_color = self.border_color1 except MotionEventAlreadyEndedError: blink_ev.cancel() self._border_color = self.border_color1 continue if collide_point(*touch.pos): async with cancel_protection(): await animate(self, _scaling=.9, d=.05) await animate(self, _scaling=1, d=.05) dispatch('on_release') blink_ev.cancel() self._border_color = self.border_color1 finally: blink_ev.cancel() self._border_color = self.border_color1
async def _async_main(self): from asynckivy import rest_of_touch_moves, event from kivy.graphics import Line, Color, Rectangle, InstructionGroup from kivy.utils import get_random_color def will_accept_touch(w, t) -> bool: return w.collide_point(*t.opos) and (not t.is_mouse_scrolling) while True: __, touch = await event(self, 'on_touch_down', filter=will_accept_touch, stop_dispatching=True) inst_group = InstructionGroup() self.canvas.add(inst_group) inst_group.add(Color(*get_random_color())) line = Line(width=2) inst_group.add(line) ox, oy = x, y = self.to_local(*touch.opos) async for __ in rest_of_touch_moves(self, touch, stop_dispatching=True): # Don't await anything during the iteration x, y = self.to_local(*touch.pos) min_x, max_x = (x, ox) if x < ox else (ox, x) min_y, max_y = (y, oy) if y < oy else (oy, y) line.rectangle = ( min_x, min_y, max_x - min_x, max_y - min_y, ) if x == ox and y == oy: self.canvas.remove(inst_group) else: inst_group.add(Color(*get_random_color(alpha=.3))) inst_group.add( Rectangle( pos=(min_x, min_y), size=( max_x - min_x, max_y - min_y, ), ))
async def _handle_touch(self, touch): from asynckivy import animate, Event, rest_of_touch_moves, start self._ctx = True self.dispatch('on_press') try: flag_proceed_blinking = Event() flag_proceed_blinking.set() coro_blink = start(self._blink(flag_proceed_blinking)) async for __ in rest_of_touch_moves(self, touch): if self.collide_point(*touch.pos): flag_proceed_blinking.set() else: flag_proceed_blinking.clear() if self.collide_point(*touch.pos): await animate(self, _scaling=.9, d=.05) await animate(self, _scaling=1, d=.05) self.dispatch('on_release') finally: coro_blink.close() self._ctx = None
async def main(cls, *, widgets, ctx): from kivy.graphics import Line, Color, InstructionGroup import asynckivy as ak abs_ = abs target = widgets['target'] to_local = target.to_local is_inside = _is_inside while True: __, touch = await ak.event(target, 'on_touch_down', filter=is_inside, stop_dispatching=True) target.canvas.add(ig := InstructionGroup()) ig.add(Color(*ctx.line_color)) ig.add(line := Line(width=ctx.line_width, points=[*to_local(*touch.opos)])) precision = ctx.freehand_precision last_x, last_y = touch.opos async for __ in ak.rest_of_touch_moves(target, touch): if abs_(last_x - touch.x) + abs_(last_y - touch.y) > precision: p = line.points p.extend(to_local(*touch.pos)) line.points = p last_x, last_y = touch.pos
async def main(cls, *, widgets, ctx): from kivy.graphics import Line, Color, InstructionGroup import asynckivy as ak target = widgets['target'] to_local = target.to_local shape_name = cls._shape_name is_inside = _is_inside while True: __, touch = await ak.event(target, 'on_touch_down', filter=is_inside, stop_dispatching=True) ox, oy = x, y = to_local(*touch.opos) target.canvas.add(ig := InstructionGroup()) ig.add(Color(*ctx.line_color)) ig.add(line := Line(width=ctx.line_width)) async for __ in ak.rest_of_touch_moves(target, touch): x, y = to_local(*touch.pos) min_x, max_x = (x, ox) if x < ox else (ox, x) min_y, max_y = (y, oy) if y < oy else (oy, y) setattr(line, shape_name, (min_x, min_y, max_x - min_x, max_y - min_y, )) if x == ox and y == oy: target.canvas.remove(ig)
async def main(cls, *, widgets, ctx): from kivy.graphics import Color, InstructionGroup import kivy.graphics import asynckivy as ak target = widgets['target'] shape_cls = getattr(kivy.graphics, cls._shape_name) to_local = target.to_local is_inside = _is_inside while True: __, touch = await ak.event(target, 'on_touch_down', filter=is_inside, stop_dispatching=True) ox, oy = x, y = to_local(*touch.opos) target.canvas.add(ig := InstructionGroup()) ig.add(Color(*ctx.fill_color)) ig.add(shape := shape_cls(size=(0, 0))) async for __ in ak.rest_of_touch_moves(target, touch): x, y = to_local(*touch.pos) min_x, max_x = (x, ox) if x < ox else (ox, x) min_y, max_y = (y, oy) if y < oy else (oy, y) shape.pos = min_x, min_y shape.size = max_x - min_x, max_y - min_y if x == ox and y == oy: target.canvas.remove(ig)
async def _test(parent, t): async for __ in ak.rest_of_touch_moves( parent, t, stop_dispatching=stop_dispatching): pass
async def _test(w, t): n = 0 async for __ in ak.rest_of_touch_moves(w, t): n += 1 assert n == n_touch_moves
async def _test(parent, t): async for __ in ak.rest_of_touch_moves(parent, t, eat_touch=eat_touch): pass nonlocal done done = True
async def _treat_a_touch_as_a_drag(self, touch, *, do_transform=False): self.is_being_dragged = True try: # NOTE: I don't know the difference from 'get_root_window()' window = self.get_parent_window() touch_ud = touch.ud original_pos_win = self.to_window(*self.pos) original_location = save_widget_location(self) self._drag_ctx = ctx = DragContext( original_pos_win=original_pos_win, original_location=original_location, ) if do_transform: touch.push() touch.apply_transform_2d(self.parent.to_widget) offset_x = touch.ox - self.x offset_y = touch.oy - self.y if do_transform: touch.pop() # move self under the Window self.parent.remove_widget(self) self.size_hint = ( None, None, ) self.pos_hint = {} self.pos = ( original_pos_win[0] + touch.x - touch.ox, original_pos_win[1] + touch.y - touch.oy, ) window.add_widget(self) # mark the touch so that the other widgets can react to the drag touch_ud['kivyx_drag_cls'] = self.drag_cls touch_ud['kivyx_draggable'] = self self.dispatch('on_drag_start', touch) async for __ in ak.rest_of_touch_moves(self, touch): self.x = touch.x - offset_x self.y = touch.y - offset_y # we need to give the other widgets the time to react to # 'on_touch_up' await ak.sleep(-1) ctx.droppable = droppable = touch_ud.get('kivyx_droppable', None) failed = droppable is None or \ not droppable.accepts_drag(touch, self) r = self.dispatch('on_drag_fail' if failed else 'on_drag_success', touch) if isawaitable(r): await r # I cannot remember why this 'ak.sleep()' exists. # It might be unnecessary. await ak.sleep(-1) except GeneratorExit: ctx.cancelled = True raise finally: self.dispatch('on_drag_end', touch) self.is_being_dragged = False self._drag_ctx = None touch_ud['kivyx_droppable'] = None del touch_ud['kivyx_drag_cls'] del touch_ud['kivyx_draggable']
async def _treat_a_touch_as_a_drag(self, touch, *, do_transform=False, drag_from=None): if drag_from is None: drag_from = self self.is_being_dragged = True try: # NOTE: I don't know the difference from 'get_root_window()' window = drag_from.get_parent_window() touch_ud = touch.ud original_pos_win = drag_from.to_window(*drag_from.pos) original_location = save_widget_location( drag_from, ignore_parent=(drag_from is not self)) original_location.setdefault('weak_parent', None) self._drag_ctx = ctx = DragContext( original_pos_win=original_pos_win, original_location=original_location, ) if do_transform: touch.push() touch.apply_transform_2d(drag_from.parent.to_widget) offset_x = touch.ox - drag_from.x offset_y = touch.oy - drag_from.y if do_transform: touch.pop() # move self under the Window if self.parent is None: # more like (drag_from is not self) restore_widget_location(self, original_location, ignore_parent=True) else: self.parent.remove_widget(self) self.size_hint = ( None, None, ) self.pos_hint = {} self.pos = ( original_pos_win[0] + touch.x - touch.ox, original_pos_win[1] + touch.y - touch.oy, ) window.add_widget(self) # mark the touch so that other widgets can react to the drag touch_ud['kivyx_drag_cls'] = self.drag_cls touch_ud['kivyx_draggable'] = self self.dispatch('on_drag_start', touch) async for __ in ak.rest_of_touch_moves(self, touch): self.x = touch.x - offset_x self.y = touch.y - offset_y # wait for other widgets to react to 'on_touch_up' await ak.sleep(-1) ctx.droppable = droppable = touch_ud.get('kivyx_droppable', None) if droppable is None or (not droppable.accepts_drag(touch, self)): ctx.state = 'failed' r = self.dispatch('on_drag_fail', touch) else: ctx.state = 'succeeded' r = self.dispatch('on_drag_success', touch) async with ak.cancel_protection(): if isawaitable(r): await r # I cannot remember why this 'ak.sleep()' exists. # It might be unnecessary. await ak.sleep(-1) except GeneratorExit: ctx.state = 'cancelled' self.dispatch('on_drag_cancel', touch) raise finally: self.dispatch('on_drag_end', touch) self.is_being_dragged = False self._drag_ctx = None touch_ud['kivyx_droppable'] = None del touch_ud['kivyx_drag_cls'] del touch_ud['kivyx_draggable']