diff options
Diffstat (limited to 'unrpyc/renpy/display/dragdrop.py')
-rw-r--r-- | unrpyc/renpy/display/dragdrop.py | 731 |
1 files changed, 0 insertions, 731 deletions
diff --git a/unrpyc/renpy/display/dragdrop.py b/unrpyc/renpy/display/dragdrop.py deleted file mode 100644 index 0b99e63..0000000 --- a/unrpyc/renpy/display/dragdrop.py +++ /dev/null @@ -1,731 +0,0 @@ -# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us> -# -# 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. - -# TODO: Use overlap (rather than simple pointer location) to determine -# drag and drop. - -import renpy.display -from renpy.display.render import render, Render, redraw -from renpy.display.core import absolute -from renpy.display.behavior import map_event, run - -import pygame - -def default_drag_group(): - """ - Gets the default drag group. If it doesn't exist yet, creates it. - """ - - sls = renpy.game.context().scene_lists - - rv = sls.drag_group - - if rv is None: - rv = DragGroup() - sls.drag_group = rv - - return rv - -def default_drag_joined(drag): - return [ (drag, 0, 0) ] - -class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject): - """ - :doc: drag_drop class - :args: (d=None, drag_name=None, draggable=True, droppable=True, drag_raise=True, dragged=None, dropped=None, drag_handle=(0.0, 0.0, 1.0, 1.0), drag_joined=..., clicked=None, hovered=None, unhovered=None, **properties) - - A displayable that represents an object that can be dragged around - its enclosing area. A Drag can also represent an area that - other Drags can be dropped on. - - A Drag can be moved around inside is parent. Generally, its parent - should be either a :func:`Fixed` or :class:`DragGroup`. - - A Drag has one child. The child's state reflects the status - of the drag and drop operation: - - * ``selected_hover`` - when it is being dragged. - * ``selected_idle`` - when it can be dropped on. - * ``hover`` - when the draggable will be dragged when the mouse is - clicked. - * ``idle`` - otherwise. - - The drag handle is a rectangle inside the child. The mouse must be over - a non-transparent pixel inside the drag handle for dragging or clicking - to occur. - - A newly-created draggable is added to the default DragGroup. A draggable - can only be in a single DragGroup - if it's added to a second group, - it's removed from the first. - - When a Drag is first rendered, if it's position cannot be determined - from the DragGroup it is in, the position of its upper-left corner - is computed using the standard layout algorithm. Once that position - - - `d` - If present, the child of this Drag. Drags use the child style - in preference to this, if it's not None. - - `drag_name` - If not None, the name of this draggable. This is available - as the `name` property of draggable objects. If a Drag - with the same name is or was in the DragGroup, the starting - position of this Drag is taken from that Draggable. - - `draggable` - If true, the Drag can be dragged around the screen with - the mouse. - - `droppable` - If true, other Drags can be dropped on this Drag. - - `drag_raise` - If true, this Drag is raised to the top when it is dragged. If - it is joined to other Drags, all joined drags are raised. - - `dragged` - A callback (or list of callbacks) that is called when the Drag - has been dragged. It is called with two arguments. The first is - a list of Drags that are being dragged. The second is either - a Drag that is being dropped onto, or None of a drop did not - occur. If the callback returns a value other than None, that - value is returned as the result of the interaction. - - `dropped` - A callback (or list of callbacks) that is called when this Drag - is dropped onto. It is called with two arguments. The first - is the Drag being dropped onto. The second is a list of Drags that - are being dragged. If the callback returns a value other than None, - that value is returned as the result of the interaction. - - When a dragged and dropped callback are triggered for the same - event, the dropped callback is only called if dragged returns - None. - - `clicked` - A callback this is called, with no arguments, when the Drag is - clicked without being moved. A droppable can also be focused - and clicked. If the callback returns a value othe than None, - that value is returned as the result of the interaction. - - `drag_handle` - A (x, y, width, height) tuple, giving the position of the drag - handle within the child. In this tuple, integers are considered - to be a literal number of pixels, while floats are relative to - the size of the child. - - `drag_joined` - This is called with the current Drag as an argument. It's - expected to return a list of [ (drag, x, y) ] tuples, giving - the draggables to drag as a unit. `x` and `y` are the offsets - of the drags relative to each other, they are not relative - to the corner of this drag. - - Except for `d`, all of the parameters are available as fields (with - the same name) on the Drag object. In addition, after the drag has - been rendered, the following fields become available: - - `x`, `y` - The position of the Drag relative to its parent, in pixels. - - `w`, `h` - The width and height of the Drag's child, in pixels. - """ - - def __init__(self, - d=None, - drag_name=None, - draggable=True, - droppable=True, - drag_raise=True, - dragged=None, - dropped=None, - drag_handle=(0.0, 0.0, 1.0, 1.0), - drag_joined=default_drag_joined, - clicked=None, - hovered=None, - unhovered=None, - replaces=None, - **properties): - - super(Drag, self).__init__(self, **properties) - - self.drag_name = drag_name - self.draggable = draggable - self.droppable = droppable - self.drag_raise = drag_raise - self.dragged = dragged - self.dropped = dropped - self.drag_handle = drag_handle - self.drag_joined = drag_joined - self.clicked = clicked - self.hovered = hovered - self.unhovered = unhovered - - self.child = None - - # Add us to a drag group on creation. - if drag_name: - self.drag_group = default_drag_group() - - # The current x and y coordinates of this displayable. - self.x = None - self.y = None - - # The width and height of the child. - self.w = None - self.h = None - - # The width and height of our parent. - self.parent_width = None - self.parent_height = None - - # The target x and y coordinates of this displayable. (The - # coordinates that we're snapping to.) - self.target_x = None - self.target_y = None - - # The offset from the location of the mouse to the "grab point", - # which is where the things that are being moved are offset from. - self.grab_x = None - self.grab_y = None - - # x and y from the last time we rendered. - self.last_x = None - self.last_y = None - - # The abs_x and abs_y from when we started the grab. - self.start_x = 0 - self.start_y = 0 - - # The last time we were shown, using the animation timebases. - self.at = 0 - - # The (animation timebase) time at which we should reach - # the target coordinates. - self.target_at = 0 - - # The displayable we were last dropping on. - self.last_drop = None - - # Did we move over the course of this drag? - self.drag_moved = False - - if replaces is not None: - self.x = replaces.x - self.y = replaces.y - self.at = replaces.at - self.target_x = replaces.target_x - self.target_y = replaces.target_y - self.target_at = replaces.target_at - - if d is not None: - self.add(d) - - - def snap(self, x, y, delay=0): - """ - :doc: drag_drop method - - Changes the position of the drag. If the drag is not showing, - then the position change is instantaneous. Otherwise, the - position change takes `delay` seconds, and is animated as a - linear move. - """ - - self.target_x = x - self.target_y = y - - if self.x is not None: - self.target_at = self.at + delay - else: - self.target_at = self.at - self.x = x - self.y = y - - redraw(self, 0) - - def set_style_prefix(self, prefix, root): - super(Drag, self).set_style_prefix(prefix, root) - - if self.child is not None: - self.child.set_style_prefix(prefix, False) - - def add(self, d): - if self.child is not None: - raise Exception("Drag expects either zero or one children.") - - self.child = renpy.easy.displayable(d) - - def set_child(self, d): - """ - :doc: drag_drop method - - Changes the child of this drag to `d`. - """ - - d.per_interact() - self.child = renpy.easy.displayable(d) - - def top(self): - """ - :doc: drag_drop method - - Raises this displayable to the top of its drag_group. - """ - - if self.drag_group is not None: - self.drag_group.raise_children([ self ]) - - def visit(self): - return [ self.child ] - - def focus(self, default=False): - super(Drag, self).focus(default) - - rv = None - - if not default: - rv = run(self.hovered) - - return rv - - def unfocus(self, default=False): - super(Drag, self).unfocus(default) - - if not default: - run(self.unhovered) - - def render(self, width, height, st, at): - - child = self.style.child - if child is None: - child = self.child - - self.parent_width = width - self.parent_height = height - - cr = render(child, width, height, st, at) - cw, ch = cr.get_size() - - rv = Render(cw, ch) - rv.blit(cr, (0, 0)) - - self.w = cw - self.h = ch - - # If we don't have a position, then look for it in a drag group. - if (self.x is None) and (self.drag_group is not None) and (self.drag_name is not None): - if self.drag_name in self.drag_group.positions: - self.x, self.y = self.drag_group.positions[self.drag_name] - - # If we don't have a position, run the placement code and use - # that to compute our placement. - if self.x is None: - self.x, self.y = self.place(None, 0, 0, width, height, rv) - self.x = int(self.x) - self.y = int(self.y) - - if self.target_x is None: - self.target_x = self.x - self.target_y = self.y - self.target_at = at - - # Determine if we need to do the snap animation. - if at >= self.target_at: - self.x = self.target_x - self.y = self.target_y - else: - done = (at - self.at) / (self.target_at - self.at) - self.x = absolute(self.x + done * (self.target_x - self.x)) - self.y = absolute(self.y + done * (self.target_y - self.y)) - redraw(self, 0) - - if self.draggable or self.clicked is not None: - - fx, fy, fw, fh = self.drag_handle - - if isinstance(fx, float): - fx = int(fx * cw) - - if isinstance(fy, float): - fy = int(fy * ch) - - if isinstance(fw, float): - fw = int(fw * cw) - - if isinstance(fh, float): - fh = int(fh * ch) - - rv.add_focus(self, None, fx, fy, fw, fh, fx, fy, cr.subsurface((fx, fy, fw, fh))) - - self.last_x = self.x - self.last_y = self.y - self.at = at - - return rv - - def event(self, ev, x, y, st): - - if not self.is_focused(): - return self.child.event(ev, x, y, st) - - # if not self.draggable: - # return self.child.event(ev, x, y, st) - - # Mouse, in parent-relative coordinates. - par_x = self.last_x + x - par_y = self.last_y + y - - grabbed = (renpy.display.focus.get_grab() is self) - - if grabbed: - joined_offsets = self.drag_joined(self) - joined = [ i[0] for i in joined_offsets ] - - elif self.draggable and map_event(ev, "drag_activate"): - - joined_offsets = self.drag_joined(self) - joined = [ i[0] for i in joined_offsets ] - - if not joined: - raise renpy.display.core.IgnoreEvent() - - renpy.display.focus.set_grab(self) - - self.grab_x = x - self.grab_y = y - - # If we're not the only thing we're joined with, we - # might need to adjust our grab point. - for i, xo, yo in joined_offsets: - if i is self: - self.grab_x += xo - self.grab_y += yo - break - - self.drag_moved = False - self.start_x = par_x - self.start_y = par_y - - grabbed = True - - # Handle clicking on droppables. - if not grabbed: - if self.clicked is not None and map_event(ev, "drag_deactivate"): - rv = run(self.clicked) - if rv is not None: - return rv - - raise renpy.display.core.IgnoreEvent() - - return self.child.event(ev, x, y, st) - - # Handle moves by moving things relative to the grab point. - if ev.type in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN): - - if not self.drag_moved and (self.start_x != par_x or self.start_y != par_y): - self.drag_moved = True - - # We may not be in the drag_joined group. - self.set_style_prefix("idle_", True) - - # Set the style. - for i in joined: - i.set_style_prefix("selected_hover_", True) - - # Raise the joined items. - if self.drag_raise and self.drag_group is not None: - self.drag_group.raise_children(joined) - - if self.drag_moved: - for i, xo, yo in joined_offsets: - - new_x = par_x - self.grab_x + xo - new_y = par_y - self.grab_y + yo - new_x = max(new_x, 0) - new_x = min(new_x, i.parent_width - i.w) - new_y = max(new_y, 0) - new_y = min(new_y, i.parent_height - i.h) - - if i.drag_group is not None and i.drag_name is not None: - i.drag_group.positions[i.drag_name] = (new_x, new_y) - - i.x = new_x - i.y = new_y - i.target_x = new_x - i.target_y = new_y - i.target_at = self.at - redraw(i, 0) - - if (self.drag_group is not None) and self.drag_moved: - drop = self.drag_group.get_best_drop(joined) - else: - drop = None - - if drop is not self.last_drop: - - if self.last_drop is not None: - self.last_drop.set_style_prefix("idle_", True) - - if drop is not None: - drop.set_style_prefix("selected_idle_", True) - - self.last_drop = drop - - if map_event(ev, 'drag_deactivate'): - renpy.display.focus.set_grab(None) - - if drop is not None: - drop.set_style_prefix("idle_", True) - - for i in joined: - i.set_style_prefix("idle_", True) - - self.set_style_prefix("hover_", True) - - self.grab_x = None - self.grab_y = None - self.last_drop = None - - if self.drag_moved: - - # Call the drag callback. - drag = joined[0] - if drag.dragged is not None: - rv = run(drag.dragged, joined, drop) - if rv is not None: - return rv - - # Call the drop callback. - if drop is not None and drop.dropped is not None: - rv = run(drop.dropped, drop, joined) - if rv is not None: - return rv - - else: - - # Call the clicked callback. - if self.clicked: - rv = run(self.clicked) - if rv is not None: - return rv - - raise renpy.display.core.IgnoreEvent() - - - def get_placement(self): - - if self.x is not None: - return self.x, self.y, 0, 0, 0, 0, True - else: - return super(Drag, self).get_placement() - - def per_interact(self): - self.set_style_prefix("idle_", True) - super(Drag, self).per_interact() - - -class DragGroup(renpy.display.layout.MultiBox): - """ - :doc: drag_drop class - - Represents a group of Drags. A Drag is limited to the boundary of - its DragGroup. Dropping only works between Drags that are in the - same DragGroup. Drags may only be raised when they are inside a - DragGroup. - - A DragGroup is laid out like a :func:`Fixed`. - - All positional parameters to the DragGroup constructor should be - Drags, that are added to the DragGroup. - """ - - _list_type = renpy.python.RevertableList - - def __init__(self, *children, **properties): - properties.setdefault("style", "fixed") - properties.setdefault("layout", "fixed") - - replaces = properties.pop("replaces", None) - - super(DragGroup, self).__init__(**properties) - - if replaces is not None: - self.positions = renpy.python.RevertableDict(replaces.positions) - self.sensitive = replaces.sensitive - else: - self.positions = renpy.python.RevertableDict() - self.sensitive = True - - for i in children: - self.add(i) - - - def add(self, child): - """ - :doc: drag_drop method - - Adds `child`, which must be a Drag, to this DragGroup. - """ - - if not isinstance(child, Drag): - raise Exception("Only drags can be added to a drag group.") - - child.drag_group = self - super(DragGroup, self).add(child) - - def remove(self, child): - """ - :doc: drag_drop method - - Removes `child` from this DragGroup. - """ - - - if not isinstance(child, Drag): - raise Exception("Only drags can be removed from a drag group.") - - child.x = None - super(DragGroup, self).remove(child) - - - def event(self, ev, x, y, st): - - if not self.sensitive: - return None - - return super(DragGroup, self).event(ev, x, y, st) - - def raise_children(self, l): - """ - Raises the children in `l` to the top of this drag_group, using the - order given in l for those children. - """ - - s = set(l) - - offset_map = { } - - children = [ ] - offsets = [ ] - - for i, c in enumerate(self.children): - if i < len(self.offsets): - o = self.offsets[i] - else: - o = (0, 0) - - if c not in s: - children.append(c) - offsets.append(o) - else: - offset_map[c] = o - - for c in l: - if c in offset_map: - children.append(c) - offsets.append(offset_map[c]) - - self.children = self._list_type(children) - self.offsets = self._list_type(offsets) - - - def get_best_drop(self, joined): - """ - Returns the droppable that the members of joined overlap the most. - """ - - max_overlap = 0 - rv = 0 - - joined_set = set(joined) - - for d in joined: - - r1 = (d.x, d.y, d.w, d.h) - - for c in self.children: - if c in joined_set: - continue - - if not c.droppable: - continue - - r2 = (c.x, c.y, c.w, c.h) - - overlap = rect_overlap_area(r1, r2) - - if overlap >= max_overlap: - rv = c - max_overlap = overlap - - if max_overlap <= 0: - return None - else: - return rv - - def get_children(self): - """ - Returns a list of Drags that are the children of - this DragGroup. - """ - - return renpy.python.RevertableList(self.children) - - def get_child_by_name(self, name): - """ - :doc: drag_drop method - - Returns the first child of this DragGroup that has a drag_name - of name. - """ - - for i in self.children: - if i.drag_name == name: - return i - - return None - - -def rect_overlap_area(r1, r2): - """ - Returns the number of pixels by which rectangles r1 and r2 overlap. - """ - - x1, y1, w1, h1 = r1 - x2, y2, w2, h2 = r2 - - maxleft = max(x1, x2) - minright = min(x1 + w1, x2 + w2) - maxtop = max(y1, y2) - minbottom = min(y1 + h1, y2 + h2) - - if minright < maxleft: - return 0 - - if minbottom < maxtop: - return 0 - - return (minright - maxleft) * (minbottom - maxtop) - - |