class ChoiceButton(InstanceMacro): """ChoiceButton(choiceval, choiceref, content, background, background_off) [most args optional] displays and permits control of a choice variable stored externally in choiceref, looking like Overlay(background, content) or Overlay(background_off, content) when its own choice value is chosen or unchosen (ie equal or unequal to the stored one), respectively. Most args are optional with useful defaults, or can be given as simpler convenience types (eg colors or text); all args but choiceval can be given as named options, which is useful for customization. (Example: it's useful to put several of these with the same choiceref but different choicevals into a Column. This can be done by mapping one customized variant over a list of choicevals.) The choosing-action occurs on_press of entire thing -- this is not yet changeable (to other kinds of button actions), but should be. #e """ # args choiceval = Arg(Anything) #e declare it as having to be constant per-Instance? Or can it legally vary?? I guess it could; # and I guess it's no error, just weird, for two of these (eg in a column) to share the same choiceval; # in fact, if they're physically separated it's not even weird. sbar_text = Option(str, format_Expr("%s", _self.choiceval)) # mouseover text for statusbar choiceref = ArgOrOption(StateRef) ###k need value-type?? content = ArgOrOption(stubtype, TextRect(format_Expr("%s", _self.choiceval)) ) # Widget2D or something "displayable" in one (eg text or color); defaults to displayed choiceval; # can caller pass a formula in terms of the other options to _self? # Maybe, but not by saying _self! _this(ChoiceButton) == _my? [yes -- _my is now implemented, 061205] background = ArgOrOption(stubtype, Rect(_self.width, _self.height, lightblue) ) # Widget2D, or color (used in Rect a bit larger than content) background_off = ArgOrOption(stubtype, Spacer(_self.width, _self.height)) # ditto, defaults to transparent ##k problem: what we want is to compute an lbox and then use this here in the spacer... or align the content... or .... ##e note that a lot of people find it more convenient to pass around a size, or even pass around a rect, # than to always work with 4 or 6 rect-related attrs... # formulae chosen = eq_Expr( choiceref.value, choiceval) #k ## print "chosen is",chosen ###k assume useful conversions of named options happened already ###e use _value; is it as simple as renaming it delegate and using DelegatingMixin?? Can InstanceMacro do it for us?? # [if we use one of those, be careful not to inherit from Widget2D here, due to its lbox defaults!] _value = Highlightable( Overlay( SpacerFor(Boxed(content)), # kluge to make room around content in _self.width and _self.height, # and make those non-circular; WRONG because won't be properly aligned with backgrounds, # even if content itself would be; # could fix by shifting, but better to figure out how to have better rectlike size options ###e Overlay( ###KLUGE since "Overlay is a stub which only works with exactly two args" If(chosen, background, background_off), content ), ), ## old code: on_press = SetStateRefValue(choiceref, choiceval), # try this code 061211 1113a -- it works, use it: on_press = Set(choiceref.value, choiceval), ##e probably best to say Set(choiceref.value, choiceval), but I think that's nim -- not sure -- # should retest it after Set is revised later today to work with arg1 being lval eg getattr_Expr [061204] sbar_text = sbar_text ) pass # end of class ChoiceButton
class Overlay(InstanceOrExpr, DelegatingMixin): "Overlay has the size of its first arg, but draws all its args in the same place, with the same origin." # Note: we can't inherit from Widget2D, or we'd fail to delegate # e.g. bright to self.delegate, and pick up the default value instead! # I'm not yet sure whether the proper fix is to handle those defaults in some other way # (e.g. as a last-resort delegate of some sort -- maybe we could say right here (to a fancier # version of DelegatingMixin), if you don't find the attr in self.delegate, look in Widget2D). # See also comments in InstanceMacro, about the same issue for it. # [061210 grabbing SimpleColumn's scheme for permitting up to 10 args, though ArgList is nim] a0 = Arg( Widget2D, None ) # so it's not a bug to call it with no args, as when applying it to a list of no elts [061205] a1 = Arg(Widget2D, None) a2 = Arg(Widget2D, None) a3 = Arg(Widget2D, None) a4 = Arg(Widget2D, None) a5 = Arg(Widget2D, None) a6 = Arg(Widget2D, None) a7 = Arg(Widget2D, None) a8 = Arg(Widget2D, None) a9 = Arg(Widget2D, None) a10 = Arg(Widget2D, None) a11 = Arg(Widget2D, None) args = list_Expr( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, # could say or_Expr(a0, Spacer(0)) but here is not where it matters and_Expr(a11, TextRect("too many args in Overlay"))) delegate = or_Expr(a0, Spacer(0)) ## _self.a0 # needed by DelegatingMixin ## args = list_Expr(arg0, arg1) # not sure if [arg0, arg1] would work, but I doubt it -- ###e should make it work sometime, if possible (e.g. by delving inside all literal list ns-values in ExprsMeta) #e add an option to make each element slightly closer, maybe just as a depth increment? makes hover highlighting more complicated... def draw(self): args = self.args # this order is correct since we set glDepthFunc to GL_LEQUAL (not GL_LESS) for a in args: self.drawkid(a) #e We'd like this to work properly for little filled polys drawn over big ones. # We might need something like z translation or depth offset or "decal mode"(??). # [later 070404: "decal mode" is probably unrelated -- GL_DECAL is for combining a texture with a non-textured color/alpha, # not related to using depth buffer when resulting textured object is drawn. Is "decal" used to mean anything else?? #k] # Different depth test would be best [done now -- GL_LEQUAL], but roundoff error might make it wrong... # This is definitely needed for overdrawing like that to work, but it's low priority for now. # Callers can kluge it using Closer, though that's imperfect in perspective mode (or when viewpoint is rotated). # [Or glDepthRange, now used for highlight drawing in GLPane as of 070921.] pass # Overlay
def _expr_for_overlay_imagename(imagename, dx=0, dy=0): # WARNING: this is not optimized (see comment for _expr_for_imagename()). image_expr = _overlay_image(imagename) # NOTE: If the desired dx,dy depends on other settings, # like whether one or two CC buttons are shown, # then it's simplest to make more variants of this expr, # with dx, dy hardcoded differently in each one. # Or if that's not practical, let me know and I'll # revise the code that draws this to accomodate that variability. # Also make sure to revise the code that calls each one # (i.e. a modified copy of _expr_instance_for_overlay_imagename) # to use a different "index" even when using the same imagename. # (For example, it could include dx,dy in the index.) # [bruce 080324] return DrawInCorner(corner=UPPER_RIGHT)(Overlay( Spacer(22 * PIXELS), Translate(image_expr, V_expr(dx * PIXELS, dy * PIXELS, 0)), ))
class PixelTester( InstanceOrExpr, DelegatingMixin ): # ought to be InstanceMacro but trying this alternate style just to see it # args testexpr = Arg(Widget2D) # instantiated right here, hope that's ok testname = Arg(str) # required for now, used to form filename # value filename = format_Expr("/tmp/%s.jpg", testname) delegate = SimpleColumn( TextRect( "saved image from PRIOR session, in blue box" ), # kluge: execute this first, so we read file before writing it Boxed(bordercolor=blue)(Image(filename)), Spacer(0.3), TextRect("live widget, in purple box"), Boxed(bordercolor=purple)(PixelGrabber(testexpr, filename)), ##e and put current session image here, for comparison, or put top on in a tab control for flicker test ) pass # end of class PixelTester
# TODO: import the following from somewhere DX = V(1,0,0) DY = V(0,1,0) ORIGIN = V(0,0,0) from exprs.attr_decl_macros import Instance, State from exprs.Rect import Line from exprs.Rect import Spacer from exprs.If_expr import If_expr from exprs.ExprsConstants import Point NullDrawable = Spacer() # kluge; should refile class test_polyline_drag(State_preMixin, ExampleCommand): # class constants needed by mode API for example commands commandName = 'test_polyline_drag-commandName' featurename = "Prototype: Example Polyline Drag Command" ## PM_class = test_polyline_drag_PM # tracked state rubberBand_enabled = State(bool, False, doc = "whether we're rubberbanding a new segment now") # TODO: ### grab a polyline object from an example file in exprs ### (the one that has a closed boolean and can draw in 3d) ### have a list of them, we're rubberbanding last segment of last one... lastSegmentStart = ORIGIN + 6 * DY # stub lastSegmentEnd = State( Point, doc = "endpoint of last segment; only meaningful when rubberBand_enabled")
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
def _C__constant_gap(self): return Spacer( 0, self.gap ) # this is only constant at a given time -- self.gap itself can be a formula (vary with time), that's ok