class checkbox_pref(InstanceMacro): #e rename -- Checkbox(...), with various kinds of args to say what state it uses in different ways? #e make it one of several prefs controls for other types of pref and control #e generalize to all named state -- e.g. see also LocalVariable_StateRef -- permit passing in the stateref? #e get dflt label from stateref?? # note: this was split out of kluge_dragtool_state_checkbox_expr 061214, # extended here into a def (later renamed checkbox_pref_OLDER), then a class prefs_key = Arg(str) label = Arg(Anything) # string or Widget2D dflt = ArgOrOption(bool, False) sbar_text = Option(str, '') use_label = If(call_Expr(lambda label: type(label) == type(""), label), TextRect(label), label) ## was TextRect(label,1,20) use_sbar_text = or_Expr( sbar_text, If(call_Expr(lambda label: type(label) == type(""), label), label, "")) stateref = Instance(PrefsKey_StateRef(prefs_key, dflt)) # note: without Instance here, next line stateref.value says (correctly): ## AssertionError: compute method asked for on non-Instance <PrefsKey_StateRef#47221(a)> var = stateref.value checkbox = If( var, checkbox_image('mac_checkbox_on.png'), checkbox_image('mac_checkbox_off.png'), ) _value = DisplayListChunk( Highlightable( SimpleRow(CenterY(checkbox), CenterY(use_label)), # align = CenterY is nim ## on_press = Set(debug_evals_of_Expr(stateref.value), not_Expr(var) ), #070119 debug_evals_of_Expr - worked on_press=_self.on_press, # the following works too, but I wanted to intercept it to add some py code [070305]: ## Set( stateref.value, not_Expr(var) ), sbar_text=use_sbar_text)) # note: using DisplayListChunk in _value works & is faster [070103] #070124 comment: the order DisplayListChunk( Highlightable( )) presumably means that the selobj # (which draws the highlightable's delegate) doesn't include the displist; doesn't matter much; # that CenterY(use_label) inside might be ok, or might be a bug which is made up for by the +0.5 I'm adding to drawfont2 # in testdraw.py today -- not sure. incr_drawable = Instance( Boxed(CenterY(checkbox), pixelgap=0, bordercolor=gray, borderwidth=2)) # I tried orange as a warning color -- means the checkbox reflects an intention but not yet the reality. # But it was annoyingly too visible. So I'll try gray. # If all colorboxeds are unpopular, then try an image that has a little spray of lines coming from the center, instead. def on_press(self): self.stateref.value = not self.stateref.value # was, in the expr: Set( stateref.value, not_Expr(var) ) ###e revise this code to use self.draw_incrementally once that's refiled into Highlightable ###e def func(self=self): self.incr_drawable.draw() ## self.draw() # includes the label - probably a waste but who cares self.env.glpane.swapBuffers() # update display [needed] ran_already_flag, funcres = self.run_OpenGL_in_local_coords( func) # this method runs in the Highlightable made in _value assert ran_already_flag return pass # end of class checkbox_pref
class SimpleColumn(Widget2D): #061115 #e or use InstanceMacro using Overlay & Translate? Could work, but slower and not needed... might help in CL with fancier gaps. ## a0 = Arg(Widget2D) # note: this probably doesn't preclude caller from passing None, and even if it does, nothing enforces that yet; # if caller does pass None (to this or to Overlay), best thing is probably to replace this with Pass = Rect(0,0,white) # and use it as a drawable, but have special case to use no gap under it -- or the equiv, as a simple change # to our btop formula so it's 0 if not a0 -- which is already done, so maybe there's no need to worry about a0 = None. # Maybe it should be an optional arg like the others. [061115] a0 = Arg(Widget2D, None) # even the first arg can be missing, 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) # use ArgList here when that works a6 = Arg(Widget2D, None) a7 = Arg(Widget2D, None) a8 = Arg(Widget2D, None) a9 = Arg(Widget2D, None) a10 = Arg(Widget2D, None) a11 = Arg(Widget2D, None) # the 12th arg is 1 too many toomany = Instance(TextRect("too many args to SimpleColumn")) #070212 added Instance to fix mysterious bug manifesting as this debug output: ## getattr_debugprint: <lexenv_ipath_Expr... <TextRect#2331(a)>> has no 'bleft' 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, toomany) ) ## gap = Option(Width, 3 * PIXELS) pixelgap = Option(float, 3) # 070104 int -> float gap = pixelgap * PIXELS print_lbox = Option(bool, False) #061127 for debugging; should be harmless; never tested (target bug got diagnosed in another way) drawables = call_Expr(lambda args: filter(None, args) , args) ## empty = not drawables ###e BUG: needs more Expr support, I bet; as it is, likely to silently be a constant False; not used internally empty = not_Expr(drawables) bleft = call_Expr(lambda drawables: max([arg.bleft for arg in drawables] + [0]) , drawables) # 070211 arg.bleft -> getattr_debugprint(arg, 'bleft') bright = call_Expr(lambda drawables: max([arg.bright for arg in drawables] + [0]) , drawables) height = call_Expr(lambda drawables, gap: sum([arg.height for arg in drawables]) + gap * max(len(drawables)-1,0) , drawables, gap) ## btop = a0 and a0.btop or 0 # bugfix -- use _Expr forms instead; i think this form silently turned into a0.btop [061205] btop = or_Expr( and_Expr( a0, a0.btop), 0) bbottom = height - btop def draw(self): if self.print_lbox: print "print_lbox: %r lbox attrs are %r" % (self, (self.bleft, self.bright, self.bbottom, self.btop)) glPushMatrix() prior = None for a in self.drawables: if prior: # move from prior to a dy = prior.bbottom + self.gap + a.btop glTranslatef(0,-dy,0) # positive is up, but Column progresses down prior = a self.drawkid(a) ## a.draw() glPopMatrix() return pass # end of class SimpleColumn or SimpleColumn_OLD
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
class SimpleRow(Widget2D): # copy of SimpleColumn, but bbottom <-> bright, btop <-> bleft, width <- height, and 0,-dy -> dx,0, basically a0 = Arg(Widget2D, None) a1 = Arg(Widget2D, None) a2 = Arg(Widget2D, None) a3 = Arg(Widget2D, None) a4 = Arg(Widget2D, None) a5 = Arg(Widget2D, None) # use ArgList here when that works a6 = Arg(Widget2D, None) a7 = Arg(Widget2D, None) a8 = Arg(Widget2D, None) a9 = Arg(Widget2D, None) a10 = Arg(Widget2D, None) a11 = Arg(Widget2D, None) toomany = Instance(TextRect("too many args to SimpleRow")) args = list_Expr(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, and_Expr(a11, toomany)) pixelgap = Option(int, 3) gap = pixelgap * PIXELS drawables = call_Expr(lambda args: filter(None, args), args) empty = not_Expr(drawables) btop = call_Expr( lambda drawables: max([arg.btop for arg in drawables] + [0]), drawables) bbottom = call_Expr( lambda drawables: max([arg.bbottom for arg in drawables] + [0]), drawables) width = call_Expr( lambda drawables, gap: sum([arg.width for arg in drawables]) + gap * max(len(drawables) - 1, 0), drawables, gap) bleft = or_Expr(and_Expr(a0, a0.bleft), 0) bright = width - bleft def draw(self): glPushMatrix() prior = None for a in self.drawables: if prior: # move from prior to a dx = prior.bright + self.gap + a.bleft glTranslatef(dx, 0, 0) # positive is right, and Row progresses right prior = a self.drawkid(a) ## a.draw() glPopMatrix() return pass # end of class SimpleRow
class SimpleColumn_NEW(Widget2D): #061115, revised 070321 to use new ArgList -- but disabled 070322, # since too slow for routine use (eg in testexpr_19g), # until we fix that update speed bug which means a change in any element remakes them all. ### fyi: a test that puts too many elts for SimpleColumn_OLD into the MT: _30i, dna x 3, rects x 2 #e or use InstanceMacro using Overlay & Translate? Could work, but slower and not needed... might help in CL with fancier gaps. ## a0 = Arg(Widget2D) # note: this probably doesn't preclude caller from passing None, and even if it does, nothing enforces that yet; # if caller does pass None (to this or to Overlay), best thing is probably to replace this with Pass = Rect(0,0,white) # and use it as a drawable, but have special case to use no gap under it -- or the equiv, as a simple change # to our btop formula so it's 0 if not a0 -- which is already done, so maybe there's no need to worry about a0 = None. # Maybe it should be an optional arg like the others. [061115] args = ArgList(Widget2D, (), doc = "0 or more things (that can participate in 2d layout) to show in a column") def _C_a0(self): args = self.args args[0:] # make sure this works (i.e. args is a sequence) if len(args): return args[0] else: return None pass a0 = _self.a0 # kluge, but legal (due to special case in ExprsMeta, documented in comments there): # get a0 into the class-def namespace for direct use below def _init_instance(self):##### debug only super(SimpleColumn, self)._init_instance() try: args = 'bug' # for use in error message, in case of exception args = self.args len(args) # make sure this works! if debug070321: print "fyi: this SimpleColumn has %d args" % len(args) print "fyi: the args i mentioned are: %r" % (args,) ##### except: print "following exception concerns self = %r, args = %r" % (self, args) raise return ## gap = Option(Width, 3 * PIXELS) pixelgap = Option(float, 3) # 070104 int -> float gap = pixelgap * PIXELS print_lbox = Option(bool, False) #061127 for debugging; should be harmless; never tested (target bug got diagnosed in another way) drawables = call_Expr(lambda args: filter(None, args) , args) ## empty = not drawables ###e BUG: needs more Expr support, I bet; as it is, likely to silently be a constant False; not used internally empty = not_Expr(drawables) bleft = call_Expr(lambda drawables: max([arg.bleft for arg in drawables] + [0]) , drawables) # 070211 arg.bleft -> getattr_debugprint(arg, 'bleft') bright = call_Expr(lambda drawables: max([arg.bright for arg in drawables] + [0]) , drawables) height = call_Expr(lambda drawables, gap: sum([arg.height for arg in drawables]) + gap * max(len(drawables)-1,0) , drawables, gap) ## btop = a0 and a0.btop or 0 # bugfix -- use _Expr forms instead; i think this form silently turned into a0.btop [061205] btop = or_Expr( and_Expr( a0, a0.btop), 0) bbottom = height - btop def draw(self): if self.print_lbox: print "print_lbox: %r lbox attrs are %r" % (self, (self.bleft, self.bright, self.bbottom, self.btop)) glPushMatrix() prior = None for a in self.drawables: if prior: # move from prior to a dy = prior.bbottom + self.gap + a.btop glTranslatef(0,-dy,0) # positive is up, but Column progresses down prior = a self.drawkid(a) ## a.draw() glPopMatrix() return pass # end of class SimpleColumn_NEW or SimpleColumn
class PalletteWell(DelegatingInstanceOrExpr): """A place in the UI which can make copies of its expr for dragging to whereever you want [not fully working yet] """ # experimental, really a stub, 070212 -- but sort of works! (as tested in dna_ribbon_view.py) expr = ArgExpr( Anything ) #e not really Anything, but some interface ##e should really be StateArg world = Option( World ) ###e find in env, instead?? maybe not, since in typical cases we might rather make in some parent in # the model anyway, which depends on which kind of obj we are and which pallette we're in... # maybe we even need a make-function to be passed in. # If it was an arg, it'd be natural as arg1 (since this is like a method on the world); # but since it might become a dynamic env var, I'll use an option for now. # If it was a dynamic var by default but could be some container obj, an option would be good for that too (renamed #e). type = Option( str, doc= "type of thing to tell world we're making [type, api subject to change]" ) ###BUG: passing type to world.make_and_add is nim what_to_make_nickname = or_Expr( type, "something" ) #e classname of expr, after burrowing? some other namelike attr of expr? # (note, hard to have that, unless expr is a new special Instance type of "makable" rather than a bare expr -- # and it probably ought to be. ##e) sbar_text = Option(str, format_Expr("make %s", what_to_make_nickname)) _what_to_make = DraggableObject(_self.expr) ##e rename DraggableObject -> Draggable? I misrecalled it as that... and "object" is arguably redundant. _newobj = State( Anything, None) # set internally to an object we create during _self.on_press delegate = Highlightable( Boxed(expr, borderthickness=2 * PIXELS), # plain ###BUG: Boxed fails with exprs that don't have bleft, with a very hard to decipher exception Boxed(expr, borderthickness=2 * PIXELS, bordercolor=blue), # highlight Boxed(expr, borderthickness=2 * PIXELS, bordercolor=green ), # [pressed] green signifies "going" (mainly, green != blue) on_press=_self.on_press, on_drag=_newobj.on_drag, on_release=_newobj.on_release, sbar_text= sbar_text ###e UI DESIGN: need to also pass sbar text (or a func to get it from selobj) for use during the drag ) def on_press(self): # make a new object self._newobj = self.world.make_and_add(self._what_to_make) ##e also pass the type option, taken from a new _self option? ###e UI DESIGN FLAW: we should probably not actually make the object until the drag starts... # better, make something now, but only a fake, cursor-like object (not placed in the model or its tree) # (maybe a thumbnail image made from expr? maybe use PixelGrabber on self, to get it?? #e) # and only make a real model object when the drag *ends* (in a suitable mouse position -- otherwise cancel the make). if 'kluge 070328, revised 070401': ###e see also comments in class World, 070401 self._newobj.copy_saved_coordsys_from(self.world._coordsys_holder) # start a drag of the new object; first figure out where, in world coordinates, and in the depth plane # in which you want the new object to appear (and move the object there -- without that it'll be at the origin) point_in_newobj_coords = self._newobj.current_event_mousepoint( plane=ORIGIN) ### LOGIC BUG: this seems to work, but it presumbly has this bug: in current implem, self._newobj's local coordsys # can't be available yet, since it's never been drawn! So it presumably gives out a debug message I've been seeing # ("saved modelview_matrix is None, not using it") # and uses global modelview coords, which happen to be the same in the current test (in dna_ribbon_view.py). ###KLUGE: use ORIGIN (which we know) in place of center of view (which we don't) -- only correct when no trackballing self._newobj.motion = point_in_newobj_coords ###KLUGE since it centers new obj on mouse, even if mousedown was not centered on sample obj ###BUG (confirmed): I bet the point would be wrong in perspective view, unless we first corrected the depth plane, # then reasked for point. # trying that 070217 -- But how to fix? To correct the plane, we need to flush the DraggableObject in self._newobj, at least, # before current_event_mousepoint is likely to use correct coords (actually I'm not sure -- ###TEST) # but we can't since not all objects define .move (need to ###FIX sometime). ## self._newobj.flush() # so try this even though it might not work: ## point_in_newobj_coords_2 = self._newobj.current_event_mousepoint(plane = ORIGIN) ### but now, what do we do with this point??? # print "debug note: compare these points:",point_in_newobj_coords, point_in_newobj_coords_2 # result: identical coords. # so it doesn't work and doesn't even make sense yet... i probably can't proceed until the logic bug above is fixed. # There's also the issue of different object size on-screen if it's shown at a different depth. # (Unless that could help us somehow, in showing the depth? doubtful.) ### UI DESIGN FLAWS: the new obj is obscured, and there is no visual indication you "grabbed it", tho you did. # (actually there is one, if you wait long enough and didn't happen to grab it right on the center! but it's subtle -- # and worse, it's arguably due to a bug.) ##e would box border color change help? or box "dissolve"?? (ie temporarily remove the box) # or we might need to hide the pallette well (or make it translucent or not depth-writing) ###e need sbar messages, some message that you grabbed it (which would mitigate the visual flaw above) self._newobj.on_press( ) # needed so its on_drag and on_release behave properly when we delegate to them above return pass # end of class PalletteWell