Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pynxc/waxy/containers.py
blob: b781ccc25e12abf4b2c85b0c0fd36be84e88d3ec (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# containers.py
# Containers are mixin classes that have a sizer and a number of methods to
# deal with that sizer (add components, etc).  Used to implement fairly
# "basic" controls like Panel and Frame.  When in doubt, derive from those
# two rather than making up your own container construct.

import utils
import waxyobject
import wx
import warnings

class Container(waxyobject.WaxyObject):

    def _create_sizer(self, direction):
        if direction.lower().startswith("h"):
            dir = wx.HORIZONTAL
        elif direction.lower().startswith("v"):
            dir = wx.VERTICAL
        else:
            raise ValueError, "Incorrect direction"
        self.sizer = wx.BoxSizer(dir)
        self._packed = 0

    def Body(self):
        """ Optionally override this to create components.  Add them with the
            AddComponent() method. """

    def AddComponent(self, comp, expand=0, stretch=0, align=None, border=0):
        # expand: expands a component in the direction of the panel/sizer
        # stretch: expands a component in the other direction
        # for example, if we have a horizontal panel, and resize the window
        # both horizontally and vertically, then a component with 'expand'
        # will expand horizontally, and one with 'stretch' will expand
        # vertically. (See simplebuttons.py)

        if isinstance(expand, str):
            expand = expand.lower()
            if expand.startswith("h"):
                if self.GetOrientation() == 'horizontal':
                    expand, stretch = 1, 0
                else:
                    expand, stretch = 0, 1
            elif expand.startswith("v"):
                if self.GetOrientation() == 'horizontal':
                    expand, stretch = 0, 1
                else:
                    expand, stretch = 1, 0
            elif expand.startswith("b"):
                expand = stretch = 1
            elif expand == '':
                expand = stretch = 0
            else:
                raise ValueError, "Invalid value for expand: %r" % (expand,)

        flags = 0
        if stretch:
            flags |= wx.EXPAND
        if align:
            flags |= {
                "t": wx.ALIGN_TOP,
                "c": wx.ALIGN_CENTER,
                "b": wx.ALIGN_BOTTOM,
                "r": wx.ALIGN_RIGHT,
                "l": wx.ALIGN_LEFT,
            }.get(align.lower()[:1], 0)
        if border:
            flags |= wx.ALL

        flags |= wx.FIXED_MINSIZE
        if 0 and hasattr(comp, 'sizer') and hasattr(comp, '_include_sizer'):
            # ugly hack to add nested sizer rather than its frame
            # only for certain quirky controls, like GroupBox!
            self.sizer.Add(comp.sizer, expand, flags)
        else:
            self.sizer.Add(comp, expand, flags, border)
            # comp can be a component or a tuple (width, height), but the
            # Add() method is called just the same, as of wxPython 2.5.1.5

    def AddSpace(self, width, height=None, *args, **kwargs):
        if height is None:
            height = width
        return self.AddComponent((width, height), *args, **kwargs)
    AddSeparator = AddSpace

    def Pack(self):
        if not self._packed:
            self.SetSizerAndFit(self.sizer)
            self._packed = 1

    def Repack(self):
        self.sizer.RecalcSizes()

    def GetOrientation(self):
        """ Return 'horizontal' or 'vertical'. """
        orientation = self.sizer.GetOrientation()
        if orientation == wx.HORIZONTAL:
            return 'horizontal'
        elif orientation == wx.VERTICAL:
            return 'vertical'
        else:
            raise ValueError, "Unknown direction for sizer"

#
# GridContainer (used to implement GridPanel)

class GridContainer(Container):

    _sizerclass = wx.GridSizer

    alignment = {
        'b': wx.ALIGN_BOTTOM,
        'r': wx.ALIGN_RIGHT,
        'l': wx.ALIGN_LEFT,
        'c': wx.ALIGN_CENTER,
        't': wx.ALIGN_TOP,
        'h': wx.ALIGN_CENTER_HORIZONTAL,
        'v': wx.ALIGN_CENTER_VERTICAL,
    }

    def _create_sizer(self, rows, cols, hgap=1, vgap=1):
        self.sizer = self._sizerclass(rows, cols, vgap, hgap)
        self.controls = {}
        for row in range(rows):
            for col in range(cols):
                self.controls[col, row] = None
                # (col, row) allows for (x, y)-like calling
        self._packed = 0

    def AddComponent(self, col, row, obj, expand=1, align='', border=0, stretch=0, proportion=0):
        # make sure the parents are correct
        if stretch:
            warnings.warn("stretch is deprecated and has no effect here")

        if self.controls[col, row]:
            raise ValueError, "A control has already been set for position (%s, %s)" % (row, col)
        self.controls[col, row] = {'obj': obj, 'expand': expand, 'align': align,
                                   'border': border, 'proportion': proportion}

    def Pack(self):
        if not self._packed:
            controls = self._AllControls()
            self.sizer.AddMany(controls)
            self.SetSizerAndFit(self.sizer) # is this still necessary?
            self._packed = 1

    def __setitem__(self, index, value):
        col, row = index
        self.AddComponent(col, row, value)

    def __getitem__(self, index):
        col, row = index
        return self.controls[col, row]['obj']  # may raise KeyError

    def _AllControls(self):
        controls = []
        for row in range(self.sizer.GetRows()):
            for col in range(self.sizer.GetCols()):
                d = self.controls[col, row]
                if d is None:
                    from panel import Panel
                    p = Panel(self) # hack up a dummy panel
                    controls.append((p, 0, wx.EXPAND|wx.FIXED_MINSIZE))
                else:
                    obj = d['obj']

                    # set alignment
                    align = d['align'].lower()
                    alignment = 0
                    for key, value in self.__class__.alignment.items():
                        if key in align:
                            alignment |= value

                    z = d['expand'] and wx.EXPAND
                    z |= alignment
                    border = d['border']
                    #if border and not alignment:
                    if border:
                        z |= wx.ALL
                    z |= wx.FIXED_MINSIZE
                    proportion = d['proportion']
                    controls.append((obj, proportion, z, border))

        return controls

    # XXX maybe a Wax version of AddMany() would be useful?

#
# FlexGridContainer

class FlexGridContainer(GridContainer):
    _sizerclass = wx.FlexGridSizer

    def AddGrowableRow(self, row):
        self.sizer.AddGrowableRow(row)

    def AddGrowableCol(self, col):
        self.sizer.AddGrowableCol(col)

#
# OverlayContainer

class OverlaySizer(wx.PySizer):
    def __init__(self):
        wx.PySizer.__init__(self)

    def CalcMin(self):
        maxx, maxy = 0, 0
        for win in self.GetChildren():
            x, y = win.CalcMin()
            maxx = max(maxx, x)
            maxy = max(maxy, y)
        return wx.Size(maxx, maxy)

    def RecalcSizes(self):
        pos = self.GetPosition()
        size = self.GetSize()
        for win in self.GetChildren():
            win.SetDimension(pos, size)


class OverlayContainer(Container):
    """ Container that takes an arbitrary number of windows, and stacks them
        on top of each other.  Controls are hidden by default. """

    def _create_sizer(self):
        self.sizer = OverlaySizer()
        self.windows = []
        self._packed = 0

    def AddComponent(self, window, expand=0, stretch=0, align=None, border=0):
        """
        # make sure the parents are correct
        if waxconfig.WaxConfig.check_parent:
            if window.GetParent() is not self:
                utils.parent_warning(window, self)

        flags = 0
        if stretch:
            flags |= wx.EXPAND
        if align:
            flags |= {
                "t": wx.ALIGN_TOP,
                "c": wx.ALIGN_CENTER,
                "b": wx.ALIGN_BOTTOM,
                "r": wx.ALIGN_RIGHT,
                "l": wx.ALIGN_LEFT,
            }.get(align.lower()[:1], 0)
        if border:
            flags |= wx.ALL
        flags |= wx.FIXED_MINSIZE

        self.sizer.Add(window, expand, flags, border)
        """
        Container.AddComponent(self, window, expand, stretch, align, border)
        self.windows.append(window)
        window.Hide()   # default is hiding

    

#
# GroupBoxContainer

class GroupBoxContainer(Container):

    def _create_sizer(self, groupbox, direction='h'):
        direction = direction.lower()
        if direction.startswith('v'):
            dir = wx.VERTICAL
        elif direction.startswith('h'):
            dir = wx.HORIZONTAL
        else:
            raise ValueError, "Unknown direction: %s" % (direction,)

        self.sizer = wx.StaticBoxSizer(groupbox, dir)
        self._packed = 0

#
# PlainContainer

class PlainContainer(Container):
    """ A container without a sizer.  Controls must be added at a given position.
        Size can be specified as well.
    """

    def _create_sizer(self):
        self.sizer = None
        self._packed = 0    # included for compatibility

    def AddComponent(self, x, y, comp):
        # make sure the parents are correct

        comp.SetPosition((x, y))

    def AddComponentAndSize(self, x, y, sx, sy, comp):
        # make sure the parents are correct

        comp.SetPosition((x, y))
        comp.SetSize((sx, sy))

    def Pack(self):
        self._packed = 1    # useless, but included for compatibility

        if len(self.Children) == 1:
            import panel
            dummy = panel.Panel(self, size=(0,0))
            dummy.Position = 0, 0
            self.__dummy = dummy

    def Repack():
        pass