summaryrefslogtreecommitdiff
path: root/cad/src/exprs/DragBehavior.py
blob: 472f6c9e995e786fc0358e6e64cd858e72a02ba5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# Copyright 2007-2008 Nanorex, Inc.  See LICENSE file for details.
"""
DragBehavior.py - the DragBehavior API and some useful specific behaviors

@author: Bruce
@version: $Id$
@copyright: 2007-2008 Nanorex, Inc.  See LICENSE file for details.

A DragBehavior

is a behavior in response to drag events

that a Drawable (called a "drag handle") can be given
(in addition to whatever highlighting and cursor behavior it has)

and that determines how drag events affect a position or position-determining state

(one which maps to a position, perhaps constrained to a line or plane, and/or a region, and/or quantized)

which this behavior is used to drag, and which usually (in part)
determines the drawn position of the drag handle.
"""

from exprs.instance_helpers import InstanceOrExpr
from exprs.attr_decl_macros import Arg, Instance
from exprs.__Symbols__ import Anything
from exprs.ExprsConstants import StateRef
from exprs.Highlightable import SavedCoordsys


class DragBehavior(InstanceOrExpr):
    """
    abstract class
    [#doc, maybe more defaults]
    """
    # default implems
    def on_press(self):
        pass
    def on_drag(self):
        pass
    def on_release(self):
        pass
    def on_release_in(self):
        self.on_release()
        return
    def on_release_out(self):
        self.on_release()
        return
    pass

# ==

class SimpleDragBehavior(DragBehavior): #works circa 070317; revised 070318
    """
    the simplest kind of DragBehavior -- translate the passed state
    just like the mouse moves (screen-parallel)
    [#doc]
    """
    ##e rename to indicate what it does -- translate, 3d, screen-parallel
    # (no limits, no grid, no constraint -- could add opts for those #e)

    # note: for now, it probably doesn't matter if we are remade per drag event, or live through many of them --
    # we store state during a drag (and left over afterwards) but reset it all when the next one starts --
    # ASSUMING we actually get an on_press event for it -- we don't detect the error of not getting that!
    # OTOH, even if we're newly made per drag, we don't detect or tolerate a missing initial on_press. ###BUG I guess

    # args:
    # a stateref to the translation state we should modify,
    # and something to ask about the drag event (for now, highlightable, but later, a DragEvent object)
    # (best arg order unclear; it may turn out that the DragEvent to ask is our delegate in the future -- so let it come first)

    highlightable = Arg(Anything)
        # for current_event_mousepoint (coordsys) -- will this always be needed? at least a DragEvent will be!
    translation_ref = Arg(StateRef,
                          doc = "ref to translation state, e.g. call_Expr( LvalueFromObjAndAttr, some_obj, 'translation')" )

    # state:
    saved_coordsys = Instance( SavedCoordsys() ) # provides transient state for saving a fixed coordsys to use throughout a drag

    def current_event_mousepoint(self, *args, **kws): #e zap this and inline it, for clarity? or move it into DragBehavior superclass??
        return self.saved_coordsys.current_event_mousepoint(*args, **kws)

    # note: the following methods are heavily modified from the ones in DraggableObject.
    # [They are the methods of some interface, informal so far,
    #  but I don't know if it's exactly the one I've elsewhere called Draggable.]

    def on_press(self):
        self.saved_coordsys.copy_from( self.highlightable) # needed, since self.highlightable's coordsys changes during the drag!
        point = self.current_event_mousepoint() # the touched point on the visible object (hitpoint)
        self.oldpoint = self.startpoint = point

    def on_drag(self):
        # Note (for any on_drag method -- really about the interface it's in [###e refile]):
        # we can assume this is a "real drag",
        # not one which is too short to count (and is therefore treated as a click instead),
        # since the caller is responsible for not calling on_drag until it decides this is a real drag.
        oldpoint = self.oldpoint # was saved by prior on_drag or by on_press
        point = self.current_event_mousepoint(plane = self.startpoint)
        self.translation_ref.value = self.translation_ref.value + (point - oldpoint)
        self.oldpoint = point

    def on_release(self):
        pass

    pass # end of class SimpleDragBehavior

# end