Mercurial > mamba
annotate mamba/widgets/base.py @ 189:aeacd8dfeb07
Add grab_focus helper
author | Neil Muller <drnlmuller@gmail.com> |
---|---|
date | Wed, 14 Sep 2011 17:42:47 +0200 |
parents | 5f286cbb15e3 |
children | 51d54026b153 |
rev | line source |
---|---|
55
671d027f1b71
Callbacks for events in widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
43
diff
changeset
|
1 import collections |
671d027f1b71
Callbacks for events in widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
43
diff
changeset
|
2 |
23 | 3 import pygame |
115
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
4 from pygame.constants import K_UP, K_DOWN, K_RETURN |
58 | 5 from pygame.locals import MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN, KEYDOWN |
23 | 6 |
7 | |
8 class Widget(object): | |
9 | |
10 def __init__(self, rect): | |
38 | 11 if not isinstance(rect, pygame.Rect): |
41
3f44c30a1c39
Correct Text placement
Stefano Rivera <stefano@rivera.za.net>
parents:
38
diff
changeset
|
12 rect = pygame.Rect(rect, (0, 0)) |
3f44c30a1c39
Correct Text placement
Stefano Rivera <stefano@rivera.za.net>
parents:
38
diff
changeset
|
13 self.rect = rect |
58 | 14 self.focussable = False |
77 | 15 self.focussed = False |
106 | 16 self.parent = None |
55
671d027f1b71
Callbacks for events in widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
43
diff
changeset
|
17 self.callbacks = collections.defaultdict(list) |
671d027f1b71
Callbacks for events in widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
43
diff
changeset
|
18 |
671d027f1b71
Callbacks for events in widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
43
diff
changeset
|
19 def add_callback(self, eventtype, callback, *args): |
671d027f1b71
Callbacks for events in widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
43
diff
changeset
|
20 self.callbacks[eventtype].append((callback, args)) |
23 | 21 |
59
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
22 def event(self, ev): |
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
23 for callback, args in self.callbacks[ev.type]: |
77 | 24 if callback(ev, self, *args): |
25 return True | |
26 return False | |
23 | 27 |
24
30d4f3e62bcf
Refactor widgets *again*, add container to Habitat
Stefano Rivera <stefano@rivera.za.net>
parents:
23
diff
changeset
|
28 def draw(self, surface): |
23 | 29 "Override me" |
30 pass | |
31 | |
189 | 32 def grab_focus(self): |
33 if self.focussable and self.parent: | |
34 self.parent.defocus() | |
35 self.focussed = True | |
36 | |
23 | 37 |
115
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
38 class Button(Widget): |
137
7fbeeb402685
PEP8 tidy mamba.widgets
Stefano Rivera <stefano@rivera.za.net>
parents:
115
diff
changeset
|
39 |
115
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
40 def event(self, ev): |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
41 if super(Button, self).event(ev): |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
42 return True |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
43 if (ev.type == MOUSEBUTTONDOWN |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
44 or (ev.type == KEYDOWN and ev.key == K_RETURN)): |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
45 for callback, args in self.callbacks['clicked']: |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
46 if callback(ev, self, *args): |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
47 return True |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
48 return False |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
49 |
d5aa5f805f00
Add Button class that buttons inherit from. It implements a 'clicked' callback
Stefano Rivera <stefano@rivera.za.net>
parents:
109
diff
changeset
|
50 |
59
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
51 class Container(Widget): |
23 | 52 |
106 | 53 def __init__(self, rect): |
59
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
54 super(Container, self).__init__(rect) |
23 | 55 self.children = [] |
77 | 56 self.focussed_child = None |
23 | 57 |
59
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
58 def event(self, ev): |
77 | 59 """Push an event down through the tree, and fire our own event as a |
60 last resort | |
61 """ | |
59
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
62 if ev.type in (MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN): |
58 | 63 for child in self.children: |
59
3cc917814579
Container subclasses Widget
Stefano Rivera <stefano@rivera.za.net>
parents:
58
diff
changeset
|
64 if child.rect.collidepoint(ev.pos): |
109
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
65 if ev.type == MOUSEBUTTONDOWN and child.focussable: |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
66 root = self |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
67 while root.parent is not None: |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
68 root = root.parent |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
69 root.defocus() |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
70 widget = child |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
71 while widget.parent is not None: |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
72 parent = widget.parent |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
73 if isinstance(parent, Container): |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
74 idx = parent.children.index(widget) |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
75 parent.focussed_child = idx |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
76 widget = parent |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
77 child.focussed = True |
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
78 |
77 | 79 if child.event(ev): |
80 return True | |
109
dc0b0be7e2f6
Move mouse focussing to within Container
Stefano Rivera <stefano@rivera.za.net>
parents:
106
diff
changeset
|
81 |
184
5f286cbb15e3
Pass other events to container children as well
Neil Muller <drnlmuller@gmail.com>
parents:
179
diff
changeset
|
82 elif ev.type == KEYDOWN: |
58 | 83 for child in self.children: |
77 | 84 if child.focussed: |
85 if child.event(ev): | |
86 return True | |
184
5f286cbb15e3
Pass other events to container children as well
Neil Muller <drnlmuller@gmail.com>
parents:
179
diff
changeset
|
87 else: |
5f286cbb15e3
Pass other events to container children as well
Neil Muller <drnlmuller@gmail.com>
parents:
179
diff
changeset
|
88 # Other events go to all children first |
5f286cbb15e3
Pass other events to container children as well
Neil Muller <drnlmuller@gmail.com>
parents:
179
diff
changeset
|
89 for child in self.children: |
5f286cbb15e3
Pass other events to container children as well
Neil Muller <drnlmuller@gmail.com>
parents:
179
diff
changeset
|
90 if child.event(ev): |
5f286cbb15e3
Pass other events to container children as well
Neil Muller <drnlmuller@gmail.com>
parents:
179
diff
changeset
|
91 return True |
77 | 92 if super(Container, self).event(ev): |
93 return True | |
106 | 94 if (self.parent is None and ev.type == KEYDOWN |
95 and ev.key in (K_UP, K_DOWN)): | |
97
c65046b5bafd
eat focus adjusting key events
Stefano Rivera <stefano@rivera.za.net>
parents:
95
diff
changeset
|
96 return self.adjust_focus(1 if ev.key == K_DOWN else -1) |
23 | 97 |
98 def add(self, widget): | |
77 | 99 widget.parent = self |
23 | 100 self.children.append(widget) |
106 | 101 self.rect = self.rect.union(widget.rect) |
102 | |
179
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
103 def remove(self, widget): |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
104 widget.parent = None |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
105 if self.focussed_child is not None: |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
106 child = self.children[self.focussed_child] |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
107 self.children.remove(widget) |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
108 # We don't update the rect, for reasons of simplificty and laziness |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
109 if self.focussed_child is not None and child in self.children: |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
110 # Fix focus index |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
111 self.focussed_child = self.children.index(child) |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
112 else: |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
113 self.focussed_child = None |
79fdae806ca5
Add remove method to containers
Neil Muller <drnlmuller@gmail.com>
parents:
137
diff
changeset
|
114 |
106 | 115 def defocus(self): |
116 if self.focussed_child is not None: | |
117 child = self.children[self.focussed_child] | |
118 if isinstance(child, Container): | |
119 child.defocus() | |
120 child.focussed = False | |
23 | 121 |
77 | 122 def adjust_focus(self, direction): |
123 """Try and adjust focus in direction (integer) | |
124 """ | |
125 if self.focussed_child is not None: | |
126 child = self.children[self.focussed_child] | |
127 if isinstance(child, Container): | |
128 if child.adjust_focus(direction): | |
129 return True | |
106 | 130 else: |
131 child.focussed = False | |
77 | 132 |
133 current = self.focussed_child | |
134 if current is None: | |
135 current = -1 if direction > 0 else len(self.children) | |
136 if direction > 0: | |
137 possibles = list(enumerate(self.children))[current + 1:] | |
138 else: | |
139 possibles = list(enumerate(self.children))[:current] | |
140 possibles.reverse() | |
141 for i, child in possibles: | |
142 if child.focussable: | |
143 child.focussed = True | |
106 | 144 self.focussed_child = i |
145 return True | |
146 if isinstance(child, Container): | |
147 if child.adjust_focus(direction): | |
77 | 148 self.focussed_child = i |
149 return True | |
150 else: | |
106 | 151 if self.parent is None: |
152 if self.focussed_child is not None: | |
99
2b6626f417f2
Handle root containers where nothing is focussable
Stefano Rivera <stefano@rivera.za.net>
parents:
97
diff
changeset
|
153 # At the end, mark the last one as focussed, again |
2b6626f417f2
Handle root containers where nothing is focussable
Stefano Rivera <stefano@rivera.za.net>
parents:
97
diff
changeset
|
154 child = self.children[self.focussed_child] |
2b6626f417f2
Handle root containers where nothing is focussable
Stefano Rivera <stefano@rivera.za.net>
parents:
97
diff
changeset
|
155 if isinstance(child, Container): |
2b6626f417f2
Handle root containers where nothing is focussable
Stefano Rivera <stefano@rivera.za.net>
parents:
97
diff
changeset
|
156 if child.adjust_focus(-direction): |
2b6626f417f2
Handle root containers where nothing is focussable
Stefano Rivera <stefano@rivera.za.net>
parents:
97
diff
changeset
|
157 return True |
106 | 158 else: |
159 child.focussed = True | |
160 return True | |
95
7a17c5b74148
Correctly handle nested Containers. Set focus on root container at first draw
Stefano Rivera <stefano@rivera.za.net>
parents:
77
diff
changeset
|
161 else: |
7a17c5b74148
Correctly handle nested Containers. Set focus on root container at first draw
Stefano Rivera <stefano@rivera.za.net>
parents:
77
diff
changeset
|
162 self.focussed_child = None |
77 | 163 return False |
164 | |
23 | 165 def draw(self, surface): |
106 | 166 if self.parent is None and not self.focussed: |
95
7a17c5b74148
Correctly handle nested Containers. Set focus on root container at first draw
Stefano Rivera <stefano@rivera.za.net>
parents:
77
diff
changeset
|
167 self.focussed = True |
7a17c5b74148
Correctly handle nested Containers. Set focus on root container at first draw
Stefano Rivera <stefano@rivera.za.net>
parents:
77
diff
changeset
|
168 self.adjust_focus(1) |
23 | 169 for child in self.children: |
24
30d4f3e62bcf
Refactor widgets *again*, add container to Habitat
Stefano Rivera <stefano@rivera.za.net>
parents:
23
diff
changeset
|
170 child.draw(surface) |