Esempio n. 1
0
class MainCommandToolButton(DelegatingInstanceOrExpr): #e rename?
    "Toolbutton for one of the main tools like Features, Build, Sketch -- class hierarchy subject to revision"
    # args
    toolbar = Arg(Toolbar) # our parent - #e rename parent_toolbar? to distinguish from our flyout_toolbar.
    toolname = Arg(str) #e.g. "Build"
    command = Arg(Command, doc = "the command invoked by pressing this toolbutton (might be transient or long lasting)") ###k type ok? 
    subtools = Arg(list_Expr) # list of subtools (for cmenu or flyout), with None as a separator -- or as an ignored missing elt??
        # like menu_spec items?
        # Q: can they contain their own conditions, or just let the list be made using Ifs or filters?
        # A: subtools can contain their own conditions, for being shown, enabled, etc. they are ui elements, not just operations.
    #e also one for its toolbar, esp if it's a mutually exclusive pressed choice -- and ways to cause related cmd/propmgr to be entered
    # state
    pressed = State(bool, False, doc = "whether this button should appear pressed right now")
    # formulae
    plain_bordercolor =       If(pressed, gray, white)
    highlighted_bordercolor = If(pressed, gray, blue)
    pressed_in_bordercolor =  If(pressed, gray, green) # green = going to do something on_release_in
    pressed_out_bordercolor = If(pressed, gray, white) # white = not going to do anything on_release_out
    # appearance
    delegate = Highlightable(
        plain =       Boxed(TextRect(toolname), bordercolor = plain_bordercolor),
        highlighted = Boxed(TextRect(toolname), bordercolor = highlighted_bordercolor), #e submenu is nim
        pressed_in  = Boxed(TextRect(toolname), bordercolor = pressed_in_bordercolor),
        pressed_out = Boxed(TextRect(toolname), bordercolor = pressed_out_bordercolor),
        sbar_text = format_Expr( "%s (click for flyout [nim]; submenu is nim)", toolname ),
        on_release_in = _self.on_release_in,
        cmenu_obj = _self ###IMPLEM cmenu_obj option alias or renaming; or call it cmenu_maker??
    )
    # repr? with self.toolname. Need to recall how best to fit in -- repr_info? ##e
    # actions
    def on_release_in(self):
        if not self.pressed:
            print "on_release_in %s" % self.toolname
            self.pressed = True #e for now -- later we might let main toolbar decide if this is ok
            #e incremental redraw to look pressed right away? or let toolbar decide?
            self.toolbar._advise_got_pressed(self)
        else:
            #### WRONG but not yet another way to unpress:
            self.pressed = False
            print "unpressed -- not normal in real life!"###
        return #e stub
    def cmenu_spec(self, highlightable): ###IMPLEM this simpler cmenu API (if it still seems good)
        return map( self.menuitem_for_subtool, self.subtools ) ###e how can that func tell us to leave out one, or incl a sequence?
    def menuitem_for_subtool(self, subtool):
        # stub, assume not None etc
        return ( subtool.name, subtool.cmd_invoke )
    pass
Esempio n. 2
0
class _cmd_MakeRect_BG(Highlightable):
    """Background event bindings for dragging out new Rects.
    (Not involved with selecting/moving/editing/resizing rects after they're made,
     even if that happens immediately to a rect this code makes from a single drag.)
    """
    # args [needed for sharing state with something]
    world = Option(World)
    #e something to share state with a control panel in the PM - unless we just use prefs for that

    # The drag event handlers modify the following state and/or derived objects (some documented with their defs):
    # self.rubber_rect = None or a newly made Rect we're dragging out right now
    #  (implemented as a rubberband-object drawable -- NOT a model object -- since it has formulae to our state, not its own state)
    #
    # nim: self.newnode = None or a Model Object version of the Rect we're dragging out now, or last dragged out...
    #  in general this may be redundant with "the selection", if dragging out a rect selects it, or shift-dragging it adds it to sel...
    # note, which of these are change-tracked? the ones used in drawing. that means all of them i guess.

    startpoint = State(Point,
                       None,
                       doc="mousedown position; one corner of the rect"
                       )  #e (or its center, for variants of this cmd)
    curpoint = State(
        Point,
        None,
        doc="last mouse position used for the other corner of the rect")
    making_a_rect_now = State(
        bool,
        False,
        doc="whether we're making a rect right now, using the current drag")

    # formulae
    whj = curpoint - startpoint  # contains dims of current rect; only valid while we're making one, or after it's done before next press
    w = whj[
        0]  ###k what if negative?? evidently we make a neg-width Rect and nothing complains and it draws properly... ###REVIEW
    h = whj[1]  # ditto
    color = purple  # for now -- will be a pref or PM control for the color to use for new rects
    rubber_rect = Instance(
        If(making_a_rect_now, Translate(Rect(w, h, color), startpoint)))
    # this Instance is needed, at least by Highlightable
    # appearance -- note, for superclass Highlightable, this is plain, not delegate, and must be an Instance.
    # This means it's not good to subclass Highlightable rather than delegating to it. (Especially in example code!) ###FIX
    ## delegate = _self.rubber_rect
    plain = _self.rubber_rect

    # code for event handlers during the drag.
    def on_press(self):
        self.startpoint = self.current_event_mousepoint()
        self.making_a_rect_now = False  #k probably not needed
        # don't make until we drag!
        self.curpoint = None  #k probably not needed, but might help to catch bugs where whj is used when it shouldn't be
        #e change cursor, someday; sooner, change state to name the desired cursor, and display that cursorname in the PM
        return

    def on_drag(self):
        self.curpoint = self.current_event_mousepoint(plane=self.startpoint)
        self.making_a_rect_now = True
        return

    def on_release(self):
        ##e decide whether to really make one... here, assume we do, as long as we started one:
        if self.making_a_rect_now:
            node_expr = DraggableObject(
                StatefulRect(self.rubber_rect)
            )  #e StatefulMovableRect? StatefulSnapshot(self.rubber_rect)??
            ###k can we just pass the rubber rect and assume StatefulRect can grab its state from it by taking a snapshot??
            ###WRONG for DraggableObject to be here, I think -- it's how we display it -- not sure, at least movability might be here...
            ### the posn is in the rubber_rect since it has Translate... this seems potentially bad/klugy tho...
            # older cmts, related: ... also its state needs to include abs posn...
            # maybe this means, split DraggableObject into event binding part (for this UI) and Movable part (for abs posn state). #k
            self.newnode = self.world.make_and_add(node_expr, type="Rect")
            ###BUG (understood): type = "Rect" is not affecting sbar_text in DraggableObject. Need to add it in StatefulRect itself.
            self.newnode.motion = self.startpoint  ###KLUGE
            self.making_a_rect_now = False  # hide the rubber rect now that the real one is there
        else:
            print "fyi: click with no drag did nothing"  ##e remove after debug
        return

    pass  # end of class _cmd_MakeRect_BG
Esempio n. 3
0
class DraggablyBoxed(
        Boxed
):  # 070316; works 070317 [testexpr_36] before ww,hh State or resizable, and again (_36b) after them
    # inherit args, options, formulae from Boxed
    thing = _self.thing  ###k WONT WORK unless we kluge ExprsMeta to remove this assignment from the namespace -- which we did.
    ###e not sure this is best syntax though. attr = _super.attr implies it'd work inside larger formulae, but it can't;
    # attr = Boxed.attr might be ok, whether it can work is not reviewed; it too might imply what _super does, falsely I think.
    extra1 = _self.extra1
    borderthickness = _self.borderthickness
    rectframe = _self.rectframe  # a pure expr
    # new options
    resizable = Option(bool,
                       False,
                       doc="whether to make it resizable at lower right")
    # works 070317 10pm (testexpr_36b) except for a few ###BUGS [updated info 070318 7pm]:
    # + [fixed] the wrong corner resizes (top right) (logic bug)
    # + [fixed] resizer doesn't move (understood -- wrong expr for its posn; commented below)
    # - negative sizes allowed (missing feature - limit the drag - need new DragBehavior feature)
    # - no clipping to interior of rectframe (missing feature - draw something clipped)
    # - perspective view ought to work, but entirely ###UNTESTED.
    # also, cosmetic bugs:
    # - resizer doesn't follow mouse in rotated coordsys, even in ortho view (though it's still useable).
    #   (This is not surprising -- we're using the wrong kind of DragBehavior as a simple kluge.)
    # - the resizer is ugly, in shape & color.
    clipped = Option(
        bool, False,
        doc="###doc")  #070322 new feature ### make True default after testing?
    # state
    # WARNING: due to ipath persistence, if you revise dflt_expr you apparently need to restart ne1 to see the change.
    ##    ww = State(Width, thing.width  + 2 * extra1) # replaces non-state formula in superclass -- seems to work
    ##    hh = State(Width, thing.height + 2 * extra1)
    ##        # now we just need a way to get a stateref to, effectively, the 3-tuple (ww,hh,set-value-discarder) ... instead, use whj:
    whj = State(Vector,
                V_expr(thing.width + 2 * extra1, -thing.height - 2 * extra1,
                       0))  #e not sure this is sound in rotated coordsys
    translation = State(Vector, ORIGIN)
    # override super formulae
    ww = whj[0]  # seems to work
    hh = neg_Expr(
        whj[1]
    )  # negative is needed since drag down (negative Y direction) needs to increase height
    # (guess: neg_Expr wouldn't be needed if we used an appropriate new DragBehavior in resizer,
    #  rather than our current klugy use of SimpleDragBehavior)
    # appearance
    rectframe_h = Instance(
        Highlightable(
            ## rectframe(bordercolor=green),####### cust is just to see if it works -- it doesn't, i guess i sort of know why
            ##bug: __call__ of <getattr_Expr#8243: (S._self, <constant_Expr#8242: 'rectframe'>)> with: () {'bordercolor': (0.0, 1.0, 0.0)}
            ##AssertionError: getattr exprs are not callable
            TopLeft(rectframe),
            #e different colored hover-highlighted version?? for now, just use sbar_text to know you're there.
            sbar_text=
            "draggable box frame",  # this disappears on press -- is that intended? ###k
            behavior=SimpleDragBehavior(
                # arg1: the highlightable
                _self.rectframe_h,
                # arg2: a write-capable reference to _self.translation
                ## fails - evalled at compile time, not an expr: LvalueFromObjAndAttr( _self, 'translation'),
                ###BUG: why didn't anything complain when that bug caused the state value to be an add_Expr, not a number-array?
                call_Expr(LvalueFromObjAndAttr, _self, 'translation'),
                #e alternate forms for this that we might want to make work:
                #  - getattr_StateRef(_self, 'translation') # simple def of the above
                #  - StateRef_for( _self.translation ) # turns any lvalue into a stateref! Name is not good enough, though.
            )))
    resizer = Instance(
        Highlightable(
            Center(Rect(extra1, extra1)),  #e also try BottomRight
            highlighted=Center(Rect(extra1, extra1, white)),
            pressed=_my.highlighted,
            sbar_text="resize the box frame",
            behavior=SimpleDragBehavior(
                _self.resizer, call_Expr(LvalueFromObjAndAttr, _self, 'whj'))))
    ###BUG: in Boxed, rectframe comes first, so lbox attrs are delegated to it. We should do that too --
    # but right now we draw it later in order to obscure the thing if they overlap. With clipping we won't need that --
    # but without clipping we will. If the latter still matters, we need a version of Overlay with delegation != drawing order,
    # or, to delegate appearance and layout to different instances ourselves. (Or just to define new formulae for lbox -- easiest.) #e
    drawme = Instance(
        Overlay(
            If(
                clipped,
                Clipped(
                    thing,
                    planes=[
                        call_Expr(clip_to_right_of_x0, -thing.bleft - extra1 +
                                  ww - borderthickness),
                        # note: the (- borderthickness) term makes the clipping reach exactly
                        # to the inner rectframe edge. Without it, clipping would reach to the outer edge,
                        # which for 3d objects inside the frame can cause them to obscure it.
                        # (Other interesting values are (- extra1) and (- borderthickness/2.0),
                        #  but they both look worse, IMHO.)
                        call_Expr(clip_below_y0,
                                  thing.btop + extra1 - hh + borderthickness)
                    ]),
                thing,
            ),
            Translate(rectframe_h,
                      V_expr(-thing.bleft - extra1, thing.btop + extra1)),
            If(
                resizable,
                ## Translate( resizer, V_expr( thing.bright + extra1, - thing.bbottom - extra1))
                ###WRONG - this posn is fixed by thing's lbox dims, not affected by ww, hh;
                # will the fix be clearer if we use a TopLeft alignment expr?
                # It'd be hard to use it while maintaining thing's origin for use by external alignment --
                # but maybe there's no point in doing that.
                Translate(
                    resizer,
                    V_expr(-thing.bleft - extra1 + ww,
                           thing.btop + extra1 - hh)))))
    _value = Translate(
        drawme,  ## DisplayListChunk( drawme), ###k this DisplayListChunk might break the Highlightable in rectframe_h #####
        translation)
    pass  # end of class DraggablyBoxed
Esempio n. 4
0
class DraggableObject(DelegatingInstanceOrExpr):
    """DraggableObject(obj) is a wrapper which makes any model object draggable (###doc the details),
    and also helps provides a context menu specific to obj.
    [##e It may be extended to make obj click-selectable or even region-selectable, at the proper times, too.]
       WARNING: Experimental -- API/organization will surely change,
    integrating not only rotation, but click to select, etc.
    The resulting wrapper will typically be applied by model->view macros.
       In fact, it's more complicated than that: the selection-click controller will wrap single objects,
    but the draggability wrapper is more likely to be organized something like this,
    where the named localvars refer to sets whose membership depends on selection:
      visibles = DisplayListChunk(fixed_stuff) + distortedly_moving_stuff +
        DraggableObject(DisplayListChunk(dragging_as_a_unit_stuff)).
    The distortedly_moving_stuff includes things like external bonds between fixed and being-dragged atoms,
    which have to stretch in individual ways during the drag.
    """
    # args
    obj = Arg(ModelObject)

    # options
    #e selectable = Option(bool, True, doc = "whether to let this object be click-selectable in the standard way") [see selected]
    rotatable = Option(
        bool,
        True,
        doc=
        "whether to let this object rotate about its center using MMB/Alt/Option drags"
    )
    # This is intended to implement an initial subset of the "New motion UI" [070225 new feature]
    # [###e default will change to False after testing]
    # WARNING: as an optim, we might require that this be True initially, or even always (i.e. be a constant),
    # if it will ever be True during the Instance's lifetime -- not sure. If so, this requirement must at least be documented,
    # and preferably error-detected. ###FIX (if we do require that)
    # experimental kluge 070314
    _kluge_drag_handler = Option(
        Anything,
        _self,
        doc=
        "object to receive our on_press/on_drag/on_release events, in place of _self"
    )

    # state
    selected = State(bool,
                     False)  ###KLUGE test stub, only set when debug070209
    translation = Option(
        Vector,
        V(0, 0, 0),  #070404
        doc=
        "initial translation [WARNING: might merge with misnamed self.motion (a State attr) to make a StateOption]"
    )
    motion = State(
        Vector, _self.translation
    )  # publicly visible and settable (but only with =, not +=).
    ##e rename to translation? (by making it a StateOption)
    ##e (or deprecate the concept of StateOption but make any State settable initially by a special option not just with same name?
    ##   eg either initial_attr or initial_data = [something with dict or attr access to the data] ??)
    ##e NOTE [070404]: I miscoded translation as Arg rather than Option, and said StateArg rather than StateOption in docstring,
    # though intending only named uses of it -- is this evidence that Arg / Option / Parameter should be the same,
    # that Option should be the default meaning, and positional arglists should be handled differently and as an extra thing
    # (eg like the old _args feature -- which leads to clearer code when subclassing)?? Guess: quite possibly, but needs more thought.
    # WARNING: use of += has two distinct bugs, neither error directly detectable:
    # - changes due to += (or the like) would not be change tracked.
    #   (But all changes to this need to be tracked, so our drawing effects are invalidated when it changes.)
    # - value might be a shared Numeric array -- right now use of = to set this doesn't copy the array to make us own it.
    rotation = State(Quat,
                     Q(1, 0, 0,
                       0))  #070225 new feature -- applied around object center

    # experiment 070312: works (see test_StateArrayRefs_2) ###doc ##e clean up ##k is it making the usual case slow in a significant way??
    delta_stateref = Option(StateRef,
                            call_Expr(LvalueFromObjAndAttr, _self, 'motion'),
                            doc="#doc")
    use_motion = delta_stateref.value

    # geometric attrs should delegate to obj, but be translated by motion as appropriate.
    ##e Someday we need to say that in two ways:
    # - the attrs in the "geometric object interface" delegate as a group (rather than listing each one of them here)
    # - but when they do, they get passed through a change-of-coords boundary, and they know their own coordsystems,
    #   so the right thing happens.
    # But for now we have no way to say either thing, so we'll add specific formulas for specific attrs as needed. [070208]
    ##e Note that before the obj types know how to translate due to type, the interface (which knows the attrs indivly)
    # could know it. So, delegation of all attrs in an interface can be done by special glue code which also knows
    # how to transform them in useful ways, by knowing about those attrs and what transforms are useful.
    # This is useful enough to keep, even once its default transforms can come from declared attr types &
    # values knowing their coordsys. It adds value to that since interfaces can always know special cases about specific attrs.

    if 0:
        # update 070209 late: try doing this in Translate below, with the other involved exprs delegating as usual... ####k
        center = obj.center + motion
        # following comments are from when the above was 'if 1' a day or two ago -- still relevant since general [##e refile??]:

        # Problem: won't work for objs with no center! Solution for now: don't try to eval the self attr then.
        # Not perfect, since what ought to be AttributeError will turn into some other exception.
        ##e One better solution would involve declared interfaces for obj, and delegation of all attrs in interfaces
        # of a certain kind (geometric), so if obj met more interfaces and had more attrs, those would be included,
        # but if not, we would not have them either.
        ##e Or alternatively, we could provide an easy way to modify the above formula
        # to specify a condition under which center should seem to exist here, with that cond being whether it exists on obj.
        ### A potential problem with both solutions: misleasing AttributeError messages, referring to self rather than obj,
        # would hurt debugging. So we probably want to reraise the original AttributeError in cases like that, whatever
        # the way in which we ask for that behavior. That means one construct for "passing along attr missingness",
        # but *not* a composition of one construct for saying when this attr is there, and one for asking whether another is.

        # Note: can't we delegate center (& other geometry) through the display delegate below, if Highlightable passes it through
        # and Translate does the coordinate transformation? ###e

    # appearance

    obj_name = call_Expr(node_name, obj)  #070216
    # Note: node_name is used in MT_try2; it's better than using _e_model_type_you_make (for our use in sbar_text, below).
    # BTW, node_name is a helper function associated with ModelTreeNodeInterface (informal so far).
    #
    # If you want to wrap an object with extra info which specifies its node_name, use ... what? ###k hmm, I forget if there
    # is a way partway through being implemented...
    # maybe WithAttributes( Center(Rect(0.4, 0.4, green)), mt_name = "green rect #n" )... i guess yes, ### TRY IT
    # should clean this situation up, use Adaptor term for that pattern
    # of interface conversion, etc... [070404 updated comment]

    # (Note [070216]: I had a bug when I had a comma after the above def. This made obj_name, included directly in another expr,
    #  turn into a singleton tuple of the call_Expr value, but when included as _self.obj_name (normally equivalent to obj_name),
    #  turn into something else (since eval of a tuple must not descend inside it -- guess, might have been a tuple_Expr).
    #  I'm not sure how to detect this error except to stop permitting tuple(expr) to be allowed as abbrev for a tuple_Expr --
    #  which seems too inconvenient -- or to figure out a way for the formula scanner to detect it (and make it illegal as the
    #  rhs of an assignment into a class namespace -- probably ok to make illegal). ##DOIT sometime)

    obj_drawn = If(
        selected,
        Overlay(obj, Rect(
            1, 1,
            blue)),  ##### WRONG LOOK for selected, but should work [070209]
        #BUG: Rect(1,lightblue) is gray, not light blue -- oh, it's that failure to use type to guess which arg it is!
        obj)

    sbar_text_for_maybe_selected = If(selected, " (selected)", "")

    delegate = Highlightable(
        # Note 070317: since Highlightable is outside of RotateTranslate, its coordsys doesn't change during a drag,
        # thus avoiding, here in DraggableObject, the bug that came up in the first implem of DraggablyBoxed,
        # whose highlightable rectframe was moving during the drag, but was also being used to supply the coordsys
        # for the drag events. This bug is actually in SimpleDragBehavior above, and the fix will be confined to that class.
        #
        # plain appearance
        RotateTranslate(obj_drawn, rotation, use_motion),
        # hover-highlighted appearance (also used when dragging, below)
        highlighted=RotateTranslate(
            DisplayListChunk(
                # This inner DisplayListChunk, in theory, might help make up for current implem of disabling them inside WarpColors...
                # in my tests, it didn't make a noticeable difference (probably since obj is fast to draw). [070216 2pm]
                #
                # Note: if obj has its own DisplayListChunk, does that notice the value of whatever dynenv var is altered by WarpColors??
                # We'll have to make it do so somehow -- perhaps by altering the displist name by that, or turning off displists due to it.
                # For this initial implem [070215 4pm], we did the latter.

                ## WarpColors( obj_drawn, lambda color: ave_colors( 0.3, white, color ) ), # whiten the color -- ugly
                ## WarpColors( obj_drawn, lambda color: yellow ), # "ignore color, use yellow" -- even uglier
                ## WarpColors( obj_drawn, lambda color: ave_colors( 0.2, white, color ) ), # whiten, but not as much -- less ugly
                WarpColors(
                    obj_drawn, lambda color: ave_colors(0.1, white, color)
                ),  # whiten, even less -- even less ugly [best so far]
                ## WarpColors( obj_drawn, lambda color: ave_colors( 0.2, gray, color ) ), # gray-end instead of whiten -- not quite as good
                ## WarpColors( obj_drawn, lambda color: (color[1],color[2],color[0]) ), # permute the hues...
            ),
            rotation,
            use_motion),
        pressed=_my.highlighted,  # pressed_in and pressed_out appearance
        ###BUG (when we gave pressed_in and pressed_out separately -- ###UNTESTED since then):
        # this pressed_out appearance seems to work for DNA cyls but not for draggable PalletteWell items! [070215 4pm]
        ## sbar_text = format_Expr( "Draggable %r", obj ),
        ##e should use %s on obj.name or obj.name_for_sbar, and add those attrs to ModelObject interface
        # (they would delegate through viewing wrappers on obj, if any, and get to the MT-visible name of the model object itself)
        ##e [Can we implem something like try_Expr( try1, try2, try3) which evals to the first one evalling without an exception??
        # But that doesn't seem safe unless you have to list the permissible exceptions (like in Python try/except).
        # The use of this here (temporary) would be to look for obj.name, then try a different format_Expr if that fails.
        # getattr(obj, 'name', dflt) would get us by, but would not as easily permit alternate format_Exprs in the two cases.]
        ##        # older highlighted or pressed_in appearance (not sure which it was before I inserted the args above this) -- zapping it 070216 late
        ##        If( eval_Expr(constant_Expr(constant_Expr(debug070209))),
        ##                ###e need option or variant of If to turn off warning that cond is a constant: warn_if_constant = False??
        ##                # also make the printed warning give a clue who we are -- even if we have to pass an option with the text of the clue??
        ##            Translate( Boxed(obj), motion),
        ##                #######070209 TEST THIS KLUGE -- note it does not include selected appearance
        ##                    # (but HL might incl it anyway? sometimes yes sometimes no, not sure why that would be -- ah, it depends on whether
        ##                    # mouse is over the moved object (which is silly but i recall it as happening in regular ne1 too -- ###BUG)
        ##                #e not good highlight form
        ##                ####BUG: the layout attrs (lbox attrs, eg bleft) are apparently not delegated, so the box is small and mostly obscured
        ##            Translate( obj, motion)
        ##         ),
        ## _obj_name = call_Expr(node_name, obj), #070216
        # that can't work yet -- it tries to define a new attr in an object (this Highlightable) from outside,
        # accessible in other option formulae as _this(Highlightable)._obj_name...
        # instead, I moved this def into _self (far above) for now.
        sbar_text=format_Expr("%s%s (can be dragged)", obj_name,
                              sbar_text_for_maybe_selected),  # revised 070216
        # This adds some info to sbar_text about what we can do with obj (drag, select, etc)...
        #e someday, maybe the dynenv wants to control how info of several kinds turns into actual sbar_text.
        ##        on_press = _self.on_press,
        ##        on_drag = _self.on_drag,
        ##        on_release = _self.on_release,
        on_press=_kluge_drag_handler.on_press,
        on_drag=_kluge_drag_handler.on_drag,
        on_release=_kluge_drag_handler.on_release,
        cmenu_maker=
        obj  ###e 070204 experimental, API very likely to be revised; makes Highlightable look for obj.make_selobj_cmenu_items
    )

    ### DESIGN Q: do we also include the actual event binding (on_press and on_drag) -- for now, we do --
    # or just supply the Draggable interface for moving self.obj
    # and let the caller supply the binding to our internal "cmd" drag_from_to?? ###e

    # has Draggable interface (see demo_polygon.py for explan) for changing self.motion

    def _cmd_drag_from_to(
            self, p1,
            p2):  #e rename drag_hitpoint_from_to? (in the Draggable Interface)
        """[part of the Draggable Interface; but this interface
        is not general enough if it only has this method -- some objects need more info eg a moving mouseray, screenrect, etc.
        Either this gets passed more info (eg a dragevent obj),
        or we keep the kluge of separate self dynenv queries (like for mousepoint and screenrect),
        or we provide glue code to look for this method but use more general ones if it's not there. ###e
        BTW, that interface is a myth at present; all actual dragging so far is done using on_press/on_drag/on_release,
        with this method at best used internally on some objs, like this one. [as of 070313]]
        """
        if self._delegate.altkey:
            assert 0, "should no longer be called"
##            ###KLUGE, just a hack for testing Highlightable.altkey [070224]; later, do rotation instead (per "New motion UI")
##            # (Is it also a ###KLUGE to detect altkey within this method, rather than caller detecting it and passing a flag
##            #  or calling a different method? YES.)
##            ## self.motion = self.motion + (p2 - p1) * -1
##            # change self.rotation... by a quat which depends on p2 - p1 projected onto the screen... or the similar mouse x,y delta...
##            ###KLUGE: assume DZ is toward screen and scale is standard....
##            # wait, ###BUG, we don't even have enough info to do this right, or not simply, starting from p1, rather than startpoint...
##            dx,dy,dz = p2 - p1
##            rotby = Q(p1,p2) ###WRONG but ought to be legal and visible and might even pretend to be a trackball in some cases and ways
##            self.rotation = self.rotation + rotby
##            # print "%r motion = %r rotation = %r" % (self, self.motion, self.rotation)
        else:
            ## self.motion = self.motion + (p2 - p1)
            self.delta_stateref.value = self.delta_stateref.value + (p2 - p1)
        return

    ##e something to start & end the drag? that could include flush if desired...

    # can push changes into the object

    def flush(self, newmotion=V(0, 0, 0)):
        self.delegate.move(
            self.use_motion + newmotion
        )  ###k ASSUMES ModelObject always supports move (even if it's a noop) ###IMPLEM
        # note, nothing wrong with modelobjects usually having one coordsys state which this affects
        # and storing the rest of their data relative to that, if they want to -- but only some do.
        ## self.motion = V(0,0,0)
        self.delta_stateref.value = V(0, 0, 0)

    # if told to move, flush at the same time

    def move(self, motion):
        self.flush(motion)
        return

    # on_press etc methods are modified from demo_polygon.py class typical_DragCommand

    #e note: it may happen that we add an option to pass something other than self to supply these methods.
    # then these methods would be just the default for when that was not passed
    # (or we might move them into a helper class, one of which can be made to delegate to self and be the default obj). [070313]

    def on_press(self):
        point = self.current_event_mousepoint(
        )  # the touched point on the visible object (hitpoint)
        # (this method is defined in the Highlightable which is self.delegate)
        self.oldpoint = self.startpoint = point
        # decide type of drag now, so it's clearly constant during drag, and so decision code is only in one place.
        # (but note that some modkey meanings might require that changes to them during the same drag are detected [nim].)
        if self._delegate.altkey:
            self._this_drag = 'free x-y rotate'
            #e more options later, and/or more flags like this (maybe some should be booleans)
            ###e or better, set up a function or object which turns later points into their effects... hmm, a DragCommand instance!
            ##e or should that be renamed DragOperation??
            self._screenrect = (ll, lr, ur,
                                ul) = self.screenrect(self.startpoint)
            # these points should be valid in our delegate's coords == self's coords
            self._dx = _dx = norm(lr - ll)
            self._dy = _dy = norm(ur - lr)
            self._dz = cross(
                _dx, _dy
            )  # towards the eye (if view is ortho) (but alg is correct whether or not it is, i think)
            ###k check cross direction sign
            self._scale = min(vlen(lr - ll), vlen(ur - lr)) * 0.4
            # New motion UI suggests that 40% of that distance means 180 degrees of rotation.
            # We'll draw an axis whose length is chosen so that dragging on a sphere of that size
            # would have the same effect. (Maybe.)
            self._objcenter = self._delegate.center
            self.startrot = +self.rotation
        else:
            self._this_drag = 'free x-y translate'
        if debug070209:
            self.ndrags = 0
        return

    def on_drag(self):
        # Note: we can assume this is a "real drag", since the caller (ultimately a selectMode method in testmode, as of 070209)
        # is tracking mouse motion and not calling this until it becomes large enough, as the debug070209 prints show.
        oldpoint = self.oldpoint  # was saved by prior on_drag or by on_press
        point = self.current_event_mousepoint(plane=self.startpoint)
        if debug070209:
            self.ndrags += 1


##            if (self.ndrags == 1) or 1:
##                print "drag event %d, model distance = %r, pixel dist not computed" % (self.ndrags, vlen(oldpoint - point),)
        if self._this_drag == 'free x-y rotate':
            # rotate using New motion UI
            #  [probably works for this specific kind of rotation, one of 4 that UI proposes;
            #   doesn't yet have fancy cursors or during-rotation graphics; add those only after it's a DragCommand]
            # two implem choices:
            # 1. know the eye direction and the screen dims in plane of startpoint, in model coords; compute in model coords
            # 2. get the mouse positions (startpoint and point) and screen dims in window x,y coords, compute rotation in eye coords,
            #   but remember to reorient it to correspond with model if model coords are rotated already.
            # Not sure which one is better.
            #   In general, placing user into model coords (or more precisely, into object local coords) seems more general --
            # for example, what if there were several interacting users, each visible to the others?
            # We'd want each user's eye & screen to be visible! (Maybe even an image of their face & screen, properly scaled and aligned?)
            # And we'd want their posns to be used in the computations here, all in model coords.
            # (Even if zoom had occurred, which means, even the user's *size* is quite variable!)
            #   I need "user in model coords" for other reasons too, so ok, I'll do it that way.
            #
            # [Hey, I might as well fix the bug in current_event_mousepoint which fakes the center of view, at the same time.
            # (I can't remember its details right now, but I think it assumed the local origin was the cov, which is obviously wrong.)
            # (But I didn't look at that code or fix that bug now.)]
            vec = point - self.startpoint
            uvec = norm(vec)  #k needed??
            axisvec = cross(
                self._dz, uvec
            )  # unit length (suitable for glRotate -- but we need to use it to make a quat!)
            axisvec = norm(
                axisvec)  # just to be sure (or to reduce numerical errors)
            scale = self._scale
            draw_axisvec = axisvec * scale  #e times some other length constant too?
            center = self._objcenter
            self.axisends = (center - axisvec, center + axisvec
                             )  # draw a rotation axis here ###e
            self.degrees = degrees = vlen(
                vec
            ) / scale * 180.0  # draw a textual indicator with degrees (and axisvec angle too) ###e
            ###e or print that info into sbar? or somewhere fixed in glpane? or in glpane near mouse?
            # now set self.rotation to a quat made from axisvec and degrees
            theta = degrees / 360.0 * 2 * pi
            # print "axisvec %r, degrees %r, theta %r" % (axisvec ,degrees,theta)
            rot = Q(axisvec, theta)
            self.rotation = self.startrot + rot  # note use of self.startrot rather than self.rotation on rhs
            # avoid += to make sure it gets changed-tracked -- and since it would be the wrong op!

        elif self._this_drag == 'free x-y translate':
            self._cmd_drag_from_to(
                oldpoint, point)  # use Draggable interface cmd on self
        else:
            assert 0
        self.oldpoint = point
        return

    def on_release(self):
        #e here is where we'd decide if this was really just a "click", and if so, do something like select the object,
        # if we are generalized to become the wrapper which handles that too.
        if debug070209:
            if not self.ndrags:
                # print "release (no drags)" # ie a click
                self.selected = not self.selected  ###KLUGE test stub
            else:
                pass  # print "release after %d drags" % self.ndrags
            self.ndrags = 0
        pass

    pass  # end of class DraggableObject
Esempio n. 5
0
class _MT_try2_node_helper(DelegatingInstanceOrExpr):
    """
    [private helper expr class for MT_try2]
    One MT item view -- specific to one node, one whole MT, and one (possibly time-varying) position with it.
    """
    # args ####e REORDER THEM
    node = Arg(ModelNode, doc = "any node that needs to be displayed in this MT")
        ###e NOTE: type coercion to this is nim; while that's true, we use helper functions like node_name(node) below;
        # once type coercion is implemented
        # (or simulated by hand by wrapping this arg with a helper expr like ModelTreeNode_trivial_glue),
        #  we could instead use node.mt_name, etc.)
    mt = Arg(MT_try2, doc = "the whole MT view, in which we store MT items for nodes, and keep other central state or prefs if needed")
    name_suffix = Option(str, "")
    initial_open = Option(bool, False, doc = "initial value of boolean state 'open'; only used when this item is first created")
        ##e should ask the node itself for the initial value of open (e.g. so new groups, trying to start open, can do so),
        # and also advise it when we open/close it, in case it wants to make that state persistent in some manner
        
    # WARNING: compare to MT_try1 -- lots of copied code after this point
    # WARNING: the comments are also copied, and not yet reviewed much for their new context! (so they could be wrong or obs) ###k
    
    # state refs
    open = State(bool, initial_open)
    
    # other formulae
    ###e optim: some of these could have shared instances over this class, since they don't depend on _self; should autodetect this
    # Note, + means openable (ie closed), - means closable (ie open) -- this is the Windows convention (I guess; not sure about Linux)
    # and until now I had them reversed. This is defined in two files and in more than one place in one of them. [bruce 070123]
    open_icon   = Overlay(Rect(0.4), TextRect('-',1,1))
    closed_icon = Overlay(Rect(0.4), TextRect('+',1,1))
    openclose_spacer = Spacer(0.4)
        #e or Invisible(open_icon); otoh that's no simpler, since open_icon & closed_icon have to be same size anyway

    # the openclose icon, when open or close is visible (i.e. for openable nodes)
    openclose_visible = Highlightable(
        If( open, open_icon, closed_icon ),
        on_press = Set(open, not_Expr(open)),
        sbar_text = getattr_Expr( _self, '_e_serno') #070301 this permits finding out how often MT gets remade/shared
            # (results as of 070301: remade when main instance is, even if going back to a prior testexpr, out of _19i & _30i)
     )
    
    openclose_slot = If( call_Expr(node_openable, node), openclose_visible, openclose_spacer )


    if 0:
        # cross-highlighting experiment, 070210, but disabled since approach seems wrong (as explained in comment)
        yellow = DZ = 'need to import these'
        indicator_over_obj_center = Center(Rect(0.4, 0.4, yellow))
        position_over_obj_center = node.center + DZ * 3 ###BUG: DZ does not point towards screen if trackballing was done
            ###STUB:
            # - should be drawn in a fixed close-to-screen plane, or cov plane (if obscuring is not an issue),
            #   - so indicator size is constant in pixels, even in perspective view (I guess),
            #   - also so it's not obscured (especially by node itself) -- or, draw it in a way visible behind obscuring things (might be a better feature)
            # - what we draw here should depend on what node is
            # - we also want to draw a line from type icon to node indicator (requires transforming coords differently)
            # - needs to work if node.center is not defined (use getattr_Expr - but what dflt? or use some Ifs about it)
        pointer_to_obj = DrawInCenter( Translate( indicator_over_obj_center, position_over_obj_center))
            #bug: Translate gets neutralized by DrawInCorner [fixed now]
            ###BUG: fundamentally wrong -- wrong coord system. We wanted DrawInAbsCoords or really DrawInThingsCoords,
            # but this is not well-defined (if thing drawn multiply) or easy (see comments about the idea in projection.py).
    else:
        # What we want instead is to set a variable which affects how the obj is drawn.
        # If this was something all objs compared themselves to, then all objs would track its use (when they compared)
        # and therefore want to redraw when we changed it! Instead we need only the involved objs (old & new value) to redraw,
        # so we need a dict from obj to this flag (drawing prefs set by this MT). Maybe the app would pass this dict to MT_try2
        # as an argument. It would be a dict of individually trackable state elements. (Key could be node_id, I guess.)
        # ### TRY IT SOMETIME -- for now, cross-highlighting experiment is disabled.
        pointer_to_obj = None

    # selection indications can use this
    node_is_selected = call_Expr( mt_node_selected, node)
    kluge_icon_color = If( node_is_selected, blue, green)
    sbar_format_for_name = If( node_is_selected, "%s (selected)", "%s")
    
    ###STUB for the type_icon ##e the Highlightable would be useful on the label too
    icon = Highlightable(
        Rect(0.4, 0.4, kluge_icon_color), ##stub; btw, would be easy to make color show hiddenness or type, bfr real icons work
        Overlay( Rect(0.4, 0.4, ave_colors(0.1, white, kluge_icon_color)),
                 #070216 mix white into the color like DraggableObject does
                 pointer_to_obj ),
        sbar_text = format_Expr( sbar_format_for_name, call_Expr(node_name, node) )
     )
    
    ##e selection behavior too

    label = DisplayListChunk(
        # added DisplayListChunk 070213 late -- does it speed it up? not much; big new-item slowness bug remains. retain, since doesn't hurt.
        TextRect( call_Expr(node_name, node) + name_suffix )
     )
        ###e will need revision to Node or proxy for it, so node.name is usage/mod-tracked
        ##e selection behavior too --
        #e probably not in these items but in the surrounding Row (incl invis bg? maybe not, in case model appears behind it!)
        ##e italic for disabled nodes
        ##e support cmenu
    
    delegate = SimpleRow(
        CenterY(openclose_slot),
        SimpleColumn(
            SimpleRow(CenterY(icon), CenterY(label)),
                #070124 added CenterY, hoping to improve text pixel alignment (after drawfont2 improvements) -- doesn't work
            If( open,
                _MT_try2_kids_helper( call_Expr(node_kids, node) , _self.mt ), # 070218 added _self.mt -- always intended, first used now
                None
                    # Note: this None used to be Spacer(0), due to a bug mentioned in a comment in ToggleShow.py
                    # (but unfortunately not explained there -- it just says "I wanted None here, but it exposes a logic bug,
                    # not trivial to fix, discuss in If or Column" -- my recollected bug-theory is described just below).
                    # On 070302 I confirmed that None seems to work (even in testexpr_18i with a group of 2 chunks, plus two more below).
                    # I don't fully know why it works, since I thought the bug was that SimpleColumn's None specialcase
                    # didn't run, since the element was not None but the If, and then delegating lbox attrs to None didn't work.
                    # (Fixable by using the newer If that evals, but for some reason that's not yet standard, I guess just because
                    # I didn't have time to test it enough or think it through fully re ipath or instance caching or something.)
                    # But as long as it works, use it -- ask Qs later. A recent perhaps-related change: None is allowed in drawkid.
                    # (A memory scrap -- does instantiating None conceivably produce a spacer?? ###k)
             )
         )
     )
    pass # end of class _MT_try2_node_helper
Esempio n. 6
0
class main_ui_layout(DelegatingInstanceOrExpr):
    #e rename? is it not only the ui, but the entire app? (selection, files, etc)
    #e merge in the App obj from test.py, and the _recent_tests system in some form?

    # args (none yet)

    # internal state - permanent
    ###e note: when we reload and remake this instance, we'd prefer it if the world state stayed unchanged (as i presume it does)
    # but if the default_tool instance and toolstack state got remade. The lack of the latter has been confusing me
    # since changes to ui code aren't working. I think this is a difference between a ui and operations layer (should change)
    # vs model data layer (should not change even tho the op methods on it can change). So when I can put these things into layers
    # (not only State, but even Instance or attrs within them) and make those sensitive to reload, that will help.
    # In the meantime -- if I could kluge Instance and State to take an option to control this
    # (like index = exprs_globals.reload_counter)
    # it might help.... #####TRYIT SOMETIME, and BE CAREFUL UNTIL I DO.
    world = Instance(World())
    default_tool = Instance(DefaultToolRun())

    # internal state - varying
    toolstack = State(list_Expr, [
        default_tool
    ])  # always has at least one tool on it; a stack of Instances not exprs
    # maybe the better term for this is something like command & subcommand
    current_tool = toolstack[
        -1]  # last tool on the stack is current; exiting it will pop the stack (Instance not expr)
    ##e (add a type-assertion (as opposed to type-coercion) primitive, so I can say "this is an Instance" in the code?)
    # NOTE: this is not strictly speaking a tool, but ONE RUN of a tool. That might be important enough to rename it for,
    # to ToolRun or maybe ActiveTool or RunningTool or ToolInUse or ToolBeingUsed...
    # [but note, obj might remain around on history or in Undo stack, even when no longer being used],
    # since we also have to deal with Tools in the sense of Tool Run Producers, eg toolbuttons. ###e

    # parts of the appearance
    registry = find_or_make_global_command_registry(
    )  ## None ###STUB, will fail --
    ## AttributeError: 'NoneType' object has no attribute 'command_for_toolname'
    toolstack_ref = None  ###STUB
    toolbar = Instance(
        MainToolbar(registry, ["Features", "Build", "Sketch"],
                    toolstack_ref))  #e arg order?
    ###e args/opts for what tools to show -- maybe their cmdnames & it loads them from elsewhere
    #e add row of tool buttons, and flyout toolbar; use ChoiceRow?? the things should probably look pressed...
    # they might need cmenus (find out what the deal is with the cmenus i see in the ui mockup - related to flyouts?
    #    yes, it's like this: main tools have cmenus with subtools, and if you pick one, main tool and its subtool both look pressed
    # I'll need new specialized controls.py classes for these; new super Control for all kinds of controls?? (not sure why...)
    propmgr = SimpleColumn(
        TextRect(
            "(property manager)"),  #e possibly to become a tab control tab
        DebugPrintAttrs(
            current_tool.property_manager
        )  # must be None if we don't want one visible; otherwise an Instance
        ###BUG: DebugPrintAttrs shows that it's a spacer -- I guess IorE turns None into one when it instantiates? Make it a false one??
    )
    mt = SimpleColumn(
        TextRect(
            "(model tree)"
        ),  #e possibly to become a tab control tab, but only when we're in the left channel
        MT_try2(world)  #e rename to  "Feature Manager" ??
        ##e soon, MT should be not on whole world but on model or cur. part, a specific obj in the world
    )
    graphics_area = _self.world
    ##e ditto for what we show here, except it might not be the exact same object, and it will really be shown in a way
    # that depends on both the current display style and the current tool (command & subcommand)
    graphics_area_topright_buttons = current_tool.graphics_area_topright_buttons
    # overall appearance
    delegate = Overlay(
        # stuff in the corners -- note, these don't use the corner constants for standalone tests like PM_CORNER
        DrawInCorner(corner=UPPER_LEFT)(
            SimpleColumn(
                toolbar,
                #e add tab control
                SimpleRow(
                    If(
                        current_tool.property_manager, Top(propmgr), None
                    ),  #k None?? prob ok now, see demo_MT comment 070302 ###k
                    Top(mt)
                ),  #e actually we'd then put a splitter & glpane-like-thing...
                #e anything just below the propmgr?
            )),
        DrawInCorner(corner=UPPER_RIGHT)
        (  ##e of graphics area, not entire screen...
            graphics_area_topright_buttons  ### WRONG, these should go under the main toolbar area on the right
            # (but we don't yet have any 2dwidgets which expand to fill the available space, except DrawInCorner of entire screen)
            # (this won't matter once the toolbar is done entirely in Qt, so we don't need to correct it for now)
        ),
        #e other corners? "... an area (view) on the right side
        # of the main window for accessing the part library, on-line documentation, etc"
        # the main graphics area
        #e [this too ought to go under the toolbar and to the right of the propmgr, but that can wait until they're fully in Qt]
        graphics_area)
    pass
Esempio n. 7
0
class GraphDrawDemo_FixedToolOnArg1(
        InstanceMacro
):  # see also class World_dna_holder -- need to unify them as a ui-provider framework
    # args
    background = Arg(Widget2D, Rect(10))
    # testexpr_19a, 061207 morn -- see if arb objects work here, and try drawing on a curved surface --
    # works! (when the surface was the default here -- now also tested/works when it's the arg supplied by that testexpr)
    #
    # ... except for desirable improvements:
    # - we should replace DZ with "local perp to surface" to make the drawn things more visible.
    # - And we *might* want to replace depth with "computed true depth", since for a sphere, as we rotate the view
    #   the drawn radius slightly changes due to where the triangle faces are located, and this can bury the drawing of marks
    #   if they are really close to the sphere --
    # - either that, or record their posns relative to the sphere surface,
    #   which might be most correct anyway -- and especially useful if we change the radius of the sphere! (making it balloon-like)
    # - Also those things are oriented in global coords rather than coords based on the clicked surface.
    #   Fixing that would also make them "look oriented" and help you perceive their depth when they're over a curved surface.
    #
    # Note that all these ideas would require asking the object for the surface orientation, and for how to store coords (relative
    # to what), and in the given eg, getting quite different answers (incl about how to transform coords for storage, re scaling)
    # depending on whether the rect or sphere was clicked --
    # which the current code does not even detect, since it gives them the same glname. ###e
    # options
    highlight_color = Option(
        Color, None
    )  # suggest: ave_colors(0.9,gray,white)) # use this only if background takes a color option
    use_VertexView = Option(
        bool, False
    )  # 070105 so I can try out new code w/o breaking old code #### TRYIT
    world = Option(
        World, World(),
        doc="the set of model objects")  # revised 070228 for use in _19j
    test_background_object = Option(
        bool, False,
        doc="test the new testmode._background_object feature")  #070322
    hide_background_object = Option(bool, False)
    # internals
    highlightable_background = \
        Highlightable( background, #######   WAIT A MINUTE,   how can we do that -- background is already an instance?!? ######@@@@@@
                       ## background(color=green),####KLUGE, causes various bugs or weirdnesses... not yet fully understood,
                       ## e.g. AssertionError: compute method asked for on non-Instance <Rect#10415(a)>
                       ## [GLPane_overrider.py:455] [Highlightable.py:275] [Rect.py:52]
                       ##Rect(5,5,green),###KLUGE2 - works now that highlightable is not broken by projection=True [also works 061213]
                       ## background.copy(color=green), # oops i mean:
                       ## call_Expr(background.copy,)( color=green), # oops, i have to include eval_Expr:
                       If( highlight_color,
                          eval_Expr( call_Expr(background.copy,)( color = highlight_color) ),
                               # can't work unless background is simple like a Rect,
                               # but does work then! (edit _19d to include _19 not _19b)
                          background ## None -- I hoped None would be equivalent, noticed in HL and replaced, but that would be hard,
                           # would require it to notice each time whether the opt was suppied or not, do default each time
                           # rather than per-time, also require supplying None to be same as not supplying the arg (not true now)
                           # (maybe passing some other symbol should be same as that?? and permitted each time? But If->None is so
                           #  easy, by leaving out the arg... ###e decide)
                        ),
                       #e want this to work too: call_Expr(background.copy, color=green),
                       # -- just let copy take **kws and pass them on, or let it call a customize helper
                       # review of the kluges:
                       # - the explicit call_Expr remains annoying but the lack of simple fix still seems true; not sure;
                       #   could we experiment by turning off that check within certain classes? not sure if that's possible
                       #   since it happens during expr-building. ##k
                       # - the need for eval_Expr seems wrong somehow, in fact i'm not sure I understand why we need it.
                       #   Will it go away in new planned eval/instantiation scheme? not sure. I think so.
                       # - copy needs **kws.
                       # - does the .copy itself need to be explicit? that is, could supplying args/opts to an instance copy it implicitly??
                       #   this might fit with other instances doing other stuff when those were supplied.... ###e
                       # - should Overlay take color option and pass it on into leaves (subexprs)? what if some leaves won't take it?
                       #   that has been discussed elsewhere... i forget if dynenv or _optional_options = dict() seemed best.
                       on_press = _self.on_press_bg,
                       on_drag = _self.on_drag_bg,
                       on_release = _self.on_release_bg,
                       sbar_text = "gray bg"
                       )
    use_highlightable_background = If(
        test_background_object,
        BackgroundObject(highlightable_background,
                         hide=hide_background_object),
        DisplayListChunk(  # new feature as of 070103; works, and seems to be faster (hard to be sure)
            highlightable_background))
    ## world = Instance( World() ) # maintains the set of objects in the model
    _value = Overlay(
        use_highlightable_background,
        If(
            _self.use_VertexView,
            DisplayListChunk(WithViewerFunc(world, viewerfunc)),
            # try DisplayListChunk, 070103 later -- works, doesn't break dragging of contained old nodes.
            DisplayListChunk(world)  ##, debug_prints = "World")
            ## world # zap DisplayListChunk to see if it fixes new 070115 bug about dragging old nodes -- nope
        ))

    newnode = None  # note: name conflict(?) with one of those not yet used Command classes

    def on_press_bg(self):
        if 0:
            print "compare:"
            print self, self.delegate, self.delegate.delegate, self.delegate.delegate.plain  # the background
            print self.background
            print "hl.highlighted =", self.delegate.delegate.highlighted
            # self.background is the same as the .plain printed above, which means, as of 061208 941pm anyway,
            # instantiating an instance gives exactly that instance. (Reasonable for now...)

        point = self.current_event_mousepoint()
        #e note: current_event_mousepoint is defined only on Highlightable, for now (see comments there for how we need to fix that),
        # but works here because we delegate ultimately to a Highlightable, without changing local coords as we do.
        # note: lots of devel scratch & debug comments removed 061207; see cvs rev 1.5 for them.

        # for initial test, don't use those Command classes above, just do a side effect right here ###kluge

        newpos = point + DZ * DZFUZZ  # kluge: move it slightly closer so we can see it in spite of bg
        ###e needs more principled fix -- not yet sure what that should be -- is it to *draw* closer? (in a perp dir from surface)
        #e or just to create spheres (or anything else with thickness in Z) instead? (that should not always be required)

        ###BUG: DZ is not always the right direction! [more comment on that in demo_draw_on_surface.py]

        if not self.use_VertexView:
            # old code
            ## print "make node in old way (not using VertexView)" # still running as of 070115 at least in testexpr_19f
            node_expr = Vertex(
                newpos,
                Center(
                    Rect(
                        0.2,
                        0.2,
                        ## 'green', -- now we cycle through several colors: (colors,...)[counter % 6]
                        ## tuple_Expr(green,yellow,red,blue,white,black)[mod_Expr(_this(Vertex).ipath[0],6)]
                        red  # the above worked ok until tested 070121 morn -- ipath now starts with string.
                        # it was a kluge anyway, so disable it until we can rework it to be sensible.
                    )))
        else:
            # new code, being written 070105, just getting started -- mostly nim
            node_expr = Vertex_new(
                newpos,
                # cycle through several colors: (colors,...)[counter % 6]
                color=tuple_Expr(green, yellow, red, blue, white,
                                 black)[mod_Expr(_this(Vertex).ipath[0], 6)])
            pass

        ## draggable_node_expr = Highlightable(node_expr, on_drag = _self.on_drag_node, sbar_text = "dne")
        ###BUG: this breaks dragging of the new node; it fails to print the call message from on_drag_node;
        # if you try to drag an old node made this way, it doesn't work but says
        # debug fyi: len(names) == 2 (names = (268L, 269L))
        # Guess: limitation in current rendering code makes it not work for any nested glnames, but just print this instead...
        # (note: even after reload, the node objects in the world have their old Vertex class, and the old expr used to make them)
        #
        # [later 061213:] IIRC the status was: I made GLPane_overrider so I could fix that 2-glname issue in it,
        # but never got to that yet. Meanwhile I commented out the use of this expr, and thus on_drag_node is never used...
        # and Vertexes dragged directly do nothing -- they're highlightable but with no actions.
        # And World could probably draw them highlightable even if they weren't, but it doesn't.
        # BTW the disabled nonworking draggable_node_expr is not well-designed -- it does not add a Vertex to World, it adds a
        # draggable one -- but even World is not perfect, since it contains Vertexes (not just their data)
        # and they inherently have (a lack of) action bindings since they are Highlightable.
        # Probably better would be if World contained data-nodes and had access to (or had its own) display rules for them
        # which added commands/actions based on the currently active tools. That would help with tool-code-reloading too.
        # Probably some other comments here say this too.
        #
        # So does a World need a formula or function arg for how to map its data objects to display objects, at the moment?
        # Or is there some scheme of a global map for that, to be applied when "drawing" any data object?
        # And do some data objs have their own positions, or is that always supplied by the world or other data obj they're in?
        # In theory, we might display atoms at posns unrelated to atom.pos, e.g. as a row in a table which includes their coords.
        # So it's more like we have ways of "drawing a set of things" which can say "at posns given by func(thing)"
        # or "at successive posns in a column", corresponding to two display forms with different exprs,
        # with the map from thing to individual display form also needing to be specified.
        # So a World is more like a set of things, and it can have a display mode (or more than one), given a thing-display-function.
        # We can ask it or anything else how it recommends displaying itself given display style options,
        # but we can choose to use that display function (from it to a more directly displayable object) or use another one.
        # Or we can probably just "draw it" and have it pick up the current display style from the env (including the
        # currently active tools). Is there any reason not to permit both? (draw using current style, draw using given style,
        # give me function from you to drawables using given style, use specific function and draw the results -- all possible.)
        #
        # If a thing in a world has standard mouse actions of its own, can it also have "grabbable areas" for use in dragging it
        # when it has a posn as displayed in some world? Or did that world have to explicitly turn it into a draggable thing?
        # Answer: both. The world turns it into that by adding a drag binding for those "overall handles" the thing has.
        # It might draw them with glnames in some set it knows... ie as named subobjs of itself. The overall thing might also
        # have a single name. Then we have a sequence of two glnames meaning obj/subobj which we want to use to determine the action.
        # For some subobjs that's within the object and supplied by it (perhaps depending on tool); for others,
        # it's supplied by the World it's in (also dep on a tool) and handled by it (eg move the obj, select the obj).
        #
        # For the simple things we have there, there are no subobjects, and no actions except drag or later select the whole thing.
        # A simple model is "one thing was hit, but some things are specified by a specific series of two or more glnames".
        # In general the outer name decides how to interpret (or whether to ignore) the inner names.
        # It can map the inner ones somehow... not sure how. This will relate a lot to DisplayListChunk when we have that.
        # Mere nested Highlightables might push two names but both would be unique. Outer name might just defer to inner one then.

        if 0:
            ## MAKE THIS WORK:
            draggable_node_expr = 'define this'
            newnode = self.world.make_and_add(draggable_node_expr,
                                              type="Vertex")
        else:
            newnode = self.world.make_and_add(
                node_expr, type="Vertex")  #070206 added type = "Vertex"

        self.newnode = newnode  ###KLUGE that we store it directly in self; might work tho; we store it only for use by on_drag_bg
        return  # from on_press_bg


##    def on_drag_node(self):
##        print "on_drag_node called -- how can we know *which* node it was called on??"
##        # 070103 status guess: this is not called; old cmts above seem to say that the only problem with it working is nested glnames.
##        return

    def on_drag_bg(self):
        # note: so far, anyway, called only for drag after click on empty space, not from drag after click on existing node
        point = self.current_event_mousepoint()
        lastnode = self.newnode  # btw nothing clears this on mouseup, so in theory it could be left from a prior drag
        ##        try:
        ##            lastipath = lastnode.ipath[0]
        ##        except:
        ##            lastipath = -1
        ##        # print "on_drag_bg %d" % lastipath, point###  # this shows no error in retaining correct lastnode -- that's not the bug
        ## print "on_drag_bg"
        newpos = point + DZ * DZFUZZ  # used for different things, depending

        what = kluge_dragtool_state()  ###IMPLEM better
        if what == 'draw':
            # make a blue dot showing the drag path, without moving the main new node (from the click)
            node_expr = Vertex(newpos, Center(Rect(0.1, 0.1, blue)))
            self.world.make_and_add(
                node_expr, type="dot"
            )  #070206 added type = "dot" -- note, not deducible from the expr!!
        elif what == 'polyline':
            if not lastnode:
                print "bug: no self.newnode!!!"
            else:
                if not isinstance(lastnode, polyline):
                    lastnode = self.newnode = self.world.make_and_add(
                        polyline(lastnode), type="polyline")
                lastnode.add_point(newpos)
        elif what == 'drag':
            # drag the new node made by the click
            if not lastnode:
                print "bug: no self.newnode!!!"
            else:
                lastnode.pos = newpos
            pass
        return

    def on_release_bg(self):  #070223 new hack
        import foundation.env as env  #FIX
        if isinstance(self.newnode, polyline) and env.prefs.get(
                kluge_dragtool_state_prefs_key + "bla2", False):
            self.newnode._closed_state = True  ####KLUGE, I'd rather say .closed but that won't work until I have OptionState
        return

    # == methods make, make_and_add have been moved from here into class World [070202]

    pass  # end of class GraphDrawDemo_FixedToolOnArg1