Remove the promise of ducks
[tabakrolletjie.git] / tabakrolletjie / widgets.py
1 # Simple widgets
2 # This just unifies some logic.
3 # There is no implied container / window system (yet)
4
5 import pygame.locals as pgl
6 import pygame.mouse
7 import pygame.surface
8
9 from .loader import loader
10 from .constants import FONTS, COLOURS
11 from .transforms import NullTransform, Multiply
12
13
14 class Tooltip(object):
15     def __init__(self, textparts, font='sans', size=12, padding=5, distance=10):
16         self._font = loader.load_font(FONTS[font], size=size)
17         self._text = [self._font.render(text, True, COLOURS["white"]) for text in textparts]
18         self._padding = padding
19         self._distance =  distance
20
21     def render(self, surface, tool_pos, tool_width):
22         width = max(t.get_size()[0] for t in self._text) + 2 * self._padding
23         height = sum(t.get_size()[1] for t in self._text) + 2 * self._padding
24
25         tooltip = pygame.surface.Surface(
26             (width, height), pgl.SWSURFACE).convert_alpha()
27         tooltip.fill((0, 0, 0, 172))
28
29         ty = 0
30         for t in self._text:
31             tooltip.blit(t, (self._padding, self._padding + ty), None)
32             ty += self._font.get_height()
33
34         x = tool_pos[0] - width/2 + tool_width/2
35         y = tool_pos[1] - height - self._distance
36
37         surface.blit(tooltip, (x, y), None)
38         
39
40 class Button(object):
41
42     def __init__(self, size, name=None, pos=None, padding=10, tooltip=None):
43         self._size = size
44         self._padding = padding
45         self.position = pos
46         self.name = name
47         self.enabled = True
48         self.tooltip = tooltip
49
50     def enable(self):
51         self.enabled = True
52
53     def disable(self):
54         self.enabled = False
55
56     @property
57     def position(self):
58         return self._pos
59
60     @position.setter
61     def position(self, pos):
62         self._pos = pos
63         if pos is not None:
64             self._min_x = pos[0] - self._padding
65             self._max_x = pos[0] + self._size[0] + self._padding
66             self._min_y = pos[1] - self._padding
67             self._max_y = pos[1] + self._size[1] + self._padding
68
69     def get_width(self):
70         return self._size[0]
71
72     def get_height(self):
73         return self._size[1]
74
75     def render(self, surface):
76         pass
77
78     def pressed(self, ev):
79         if not self.enabled:
80             return False
81         if self._pos is None:
82             # Unplaced buttons can't be pressed
83             return False
84         if ev.type == pgl.MOUSEBUTTONDOWN and ev.button == 1:
85             if self._min_x < ev.pos[0] < self._max_x:
86                 if self._min_y < ev.pos[1] < self._max_y:
87                     return True
88         return False
89
90     def mouseover(self):
91         if not self.enabled:
92             return False
93         if self._pos is None:
94             # Unplaced buttons can't be moused over
95             return False
96
97         mpos = pygame.mouse.get_pos()
98         
99         if self._min_x < mpos[0] < self._max_x:
100             if self._min_y < mpos[1] < self._max_y:
101                 return True
102         return False
103
104
105 class SpacerButton(Button):
106     """ Add a nothing object which can be used to space other things,
107         but is never pressed """
108
109     def pressed(self, ev):
110         return False
111
112
113 class TextButton(Button):
114
115     def __init__(
116             self, text, colour, name=None, pos=None, padding=10, size=24,
117             font='sans', centre=False, tooltip=None):
118         font = loader.load_font(FONTS[font], size=size)
119         self._text = font.render(text, True, colour)
120         if centre:
121             w, h = self._text.get_size()
122             pos = pos[0] - (w / 2), pos[1] - (h / 2)
123         self._disabled_text = self._text.copy()
124         Multiply(colour=(80, 80, 80)).apply(self._disabled_text)
125         super(TextButton, self).__init__(self._text.get_size(), name,
126                                          pos, padding, tooltip)
127
128     def render(self, surface):
129         if self.enabled:
130             surface.blit(self._text, self._pos, None)
131             if self.mouseover() and self.tooltip:
132                 self.tooltip.render(surface, self._pos, self.get_width())
133         else:
134             surface.blit(self._disabled_text, self._pos, None)
135
136
137 class ImageButton(Button):
138
139     def __init__(self, *imgparts, **kwargs):
140         transform = kwargs.pop("transform", NullTransform())
141         self._img = kwargs.pop("image", None)
142         if self._img is None:
143             self._img = loader.load_image(*imgparts, transform=transform)
144         self._disabled_img = self._img.copy()
145         disabled_colour = kwargs.pop("disabled_colour", (80, 80, 80))
146         Multiply(colour=disabled_colour).apply(self._disabled_img)
147         name = kwargs.get('name')
148         pos = kwargs.get('pos')
149         padding = kwargs.get('padding', 0)
150         tooltip = kwargs.get('tooltip')
151         super(ImageButton, self).__init__(self._img.get_size(), name,
152                                           pos, padding, tooltip)
153
154     def update_image(self, *imgparts, **kwargs):
155         transform = kwargs.pop("transform", NullTransform())
156         self._img = loader.load_image(*imgparts, transform=transform)
157
158     def render(self, surface):
159         if self.enabled:
160             surface.blit(self._img, self._pos, None)
161             if self.mouseover() and self.tooltip:
162                 self.tooltip.render(surface, self._pos, self.get_width())
163         else:
164             surface.blit(self._disabled_img, self._pos, None)