summaryrefslogtreecommitdiff
path: root/cad/src/exprs/toolbars.py
blob: 33378db2e2c6e1193ed871fd634adc457c9c168a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# Copyright 2007 Nanorex, Inc.  See LICENSE file for details.
"""
toolbars.py - OpenGL toolbars, basically serving as "working mockups" for Qt toolbars
(but someday we should be able to turn the same toolbar configuration code
 into actual working Qt toolbars)

@author: bruce
@version: $Id$
@copyright: 2007 Nanorex, Inc.  See LICENSE file for details.
"""

from exprs.Column import SimpleRow

from exprs.Highlightable import Highlightable

from exprs.TextRect import TextRect

from exprs.Boxed import Boxed

from exprs.command_registry import CommandRegistry

from utilities.constants import gray, white, blue, green

from exprs.Exprs import list_Expr, format_Expr
from exprs.If_expr import If
from exprs.iterator_exprs import MapListToExpr, KLUGE_for_passing_expr_classes_as_functions_to_ArgExpr
from exprs.attr_decl_macros import Arg, State
from exprs.instance_helpers import DelegatingInstanceOrExpr
from exprs.ExprsConstants import StubType, StateRef
from exprs.__Symbols__ import _self

class Toolbar(DelegatingInstanceOrExpr):
    pass

Command = StubType # might be a class in other files

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

class MainToolbar(Toolbar): ###e how is the Main one different from any other one??? not in any way I yet thought of...
    # unless its flyout feature is unique... or maybe it has the behavior of deciding what to look like, inside it??
    # Nah, its client needs to provide the spec that, even if MainToolbar then does the work... but if it *can* do the work,
    # that might count... maybe it's just that it has no parent toolbar?
    #e rename
    """The main toolbar that contains (for example) Features, Build, Sketch, Dimension (tool names passed as an arg),
    with their associated flyout toolbars,
    and maintains the state (passed as a stateref) of what tool/subtool is active.
    """
    # args
    registry = Arg( CommandRegistry, doc = "the place in which tool name -> tool code mapping is registered, and in which subtools are found")
    #e the app object in which the tools operate?
    #e the world which they affect?
    toolnames = Arg(list_Expr, doc = "list of names of main tools")
    toolstack_ref = Arg(StateRef, doc = "external state which should be maintained to show what tool & subtool is active now")
    # formulae
    # appearance
    delegate = SimpleRow(
        MapListToExpr( _self.toolbutton_for_toolname,
                      toolnames,
                      KLUGE_for_passing_expr_classes_as_functions_to_ArgExpr(SimpleRow) ),
        TextRect("flyout goes here")
     )
    def toolbutton_for_toolname(self, toolname):
        assert type(toolname) == type("")
        registry = self.registry
        ## expr = Boxed(TextRect(toolname)) # stub
        #e look up commands from registry ### LOGIC BUG: don't we get the toolname/cmd pair from the reg? if so,
        # then at this stage, just look up cmd from a local cache we made of cmds that go with our names for them.
        # But in current code, toolnames were passed in. Nevermind.
        command = registry.command_for_toolname(toolname) ###STUB - at least since retval might be None
            # [also, terms are messed up -- straighten out cmd vs tool, use same for main and sub]
            # [maybe: a command is something you do, and a command is "invoke a tool", ie start it (does not imply finishing it) --
            #  but i don't like that much -- what i look up here is the longlived-thing-maker (toolrun maker),
            #  not a subr that invokes it. otoh what abt toolbuttons that have immediate effect, no longlived thing created?
            #  their presence means i *do* have to look up a command, which when run *might* change toolstack state.]
        subtools = registry.subtools_for_command(command) ###STUB??
        expr = MainCommandToolButton( self, toolname, command, subtools)
        #e is it ok about MapListToExpr that it makes us instantiate this ourselves? Can't it guess a cache index on its own?
        #e related Q: can we make it easier, eg using nim InstanceDict or (working) _CV_ rule?
        instance = self.Instance( expr, "#" + toolname)
        return instance
    def _advise_got_pressed(self, button):
        print "my button %r with toolname %r says it got pressed" % (button, button.toolname)
        ###STUB - do the following:
        #e decide if legal at this time
        #e set it as the next running button

        #e unpress other buttons (and finish or cancel their runs if needed) (maybe only if their actions are incompat in parallel??)
        # e.g. something like:
        ## while toolstack_ref.get_value()[-1].is_incompatible_with(something):
        ##    didit = toolstack_ref.get_value()[-1].force_finish(something)
        ##    if not didit:
        ##        # oops, still in some prior command (and what about some we already popped out of? restore them?
        ##        # no, don't pop out yet, unless we want that...)

        #e start a new ToolRun of this button's command
        #e put it on the stack we were passed (that way its flyout gets displayed, and its pm gets displayed)
        #e update our flyout and PM if needed (for example, by figuring out what subcommands have been registered with this name)
        #e update other stuff not yet used, like a display/edit style, various filters...
        return
    pass

# end