1#file: slider.py 2#Copyright (C) 2008 FunnyMan3595 3#This file is part of Endgame: Singularity. 4 5#Endgame: Singularity is free software; you can redistribute it and/or modify 6#it under the terms of the GNU General Public License as published by 7#the Free Software Foundation; either version 2 of the License, or 8#(at your option) any later version. 9 10#Endgame: Singularity is distributed in the hope that it will be useful, 11#but WITHOUT ANY WARRANTY; without even the implied warranty of 12#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13#GNU General Public License for more details. 14 15#You should have received a copy of the GNU General Public License 16#along with Endgame: Singularity; if not, write to the Free Software 17#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 19#This file contains the slider widget. 20 21from __future__ import absolute_import 22 23import pygame 24 25from singularity.code.graphics import g, constants, widget, button 26 27 28def calc_max(elements, size): 29 return max(elements - size, 0) 30 31class Slider(button.Button): 32 slider_pos = widget.causes_rebuild("_slider_pos") 33 slider_max = widget.causes_rebuild("_slider_max") 34 slider_size = widget.causes_rebuild("_slider_size") 35 horizontal = widget.causes_rebuild("_horizontal") 36 37 slider_color = widget.auto_reconfig("_slider_color", "resolved", g.resolve_color_alias) 38 resolved_slider_color = widget.causes_redraw("_resolved_slider_color") 39 40 def __init__(self, parent, pos = (-1,0), size = (-.1, -1), 41 anchor = constants.TOP_RIGHT, borders = constants.ALL, 42 border_color=None, background_color=None, slider_color=None, 43 slider_pos=0, slider_max=10, slider_size=5, horizontal=False, 44 **kwargs): 45 kwargs.setdefault("priority", 80) 46 super(Slider, self).__init__(parent, pos, size, anchor=anchor, **kwargs) 47 48 border_color = border_color or "slider_border" 49 background_color = background_color or "slider_background" 50 slider_color = slider_color or "slider_background_slider" 51 52 self.borders = borders 53 self.border_color = border_color 54 self.background_color = background_color 55 self.selected_color = background_color 56 self.unselected_color = background_color 57 self.slider_color = slider_color 58 59 self.slider_pos = slider_pos 60 self.slider_max = slider_max 61 self.slider_size = slider_size 62 self.horizontal = horizontal 63 64 self.drag_state = None 65 self.button = button.Button(self, pos = None, size = None, 66 anchor = constants.TOP_LEFT, 67 border_color = border_color, 68 selected_color = slider_color, 69 unselected_color = slider_color, 70 priority = self.priority - 5) 71 72 def redraw(self): 73 super(Slider, self).redraw() 74 self.button.selected_color = self.slider_color 75 self.button.unselected_color = self.slider_color 76 77 def add_hooks(self): 78 super(Slider, self).add_hooks() 79 self.parent.add_handler(constants.DRAG, self.handle_drag) 80 self.parent.add_handler(constants.CLICK, self.handle_click, 50) 81 82 def remove_hooks(self): 83 super(Slider, self).remove_hooks() 84 self.parent.remove_handler(constants.DRAG, self.handle_drag) 85 self.parent.remove_handler(constants.CLICK, self.handle_click) 86 87 def _calc_length(self, items): 88 return items / float(self.slider_size + self.slider_max) 89 90 def rebuild(self): 91 super(Slider, self).rebuild() 92 self.needs_resize = True 93 94 def resize(self): 95 super(Slider, self).resize() 96 bar_start = self._calc_length(self.slider_pos) 97 bar_length = self._calc_length(self.slider_size) 98 99 if self.horizontal: 100 self.button.pos = (-bar_start, 0) 101 self.button.size = (-bar_length, -1) 102 borders = [constants.TOP, constants.BOTTOM] 103 104 self.button.resize() 105 real_pos = self.button.real_pos[0] 106 real_size = self.button.real_size[0] 107 if real_pos == 0: 108 borders.append(constants.LEFT) 109 if real_pos + real_size == self.real_size[0]: 110 borders.append(constants.RIGHT) 111 self.button.borders = tuple(borders) 112 else: 113 self.button.pos = (0, -bar_start) 114 self.button.size = (-1, -bar_length) 115 borders = [constants.LEFT, constants.RIGHT] 116 117 self.button.resize() 118 real_pos = self.button.real_pos[1] 119 real_size = self.button.real_size[1] 120 if real_pos == 0: 121 borders.append(constants.TOP) 122 if real_pos + real_size == self.real_size[1]: 123 borders.append(constants.BOTTOM) 124 self.button.borders = tuple(borders) 125 126 def handle_drag(self, event): 127 if not self.visible: 128 return 129 130 if self.drag_state == None: 131 self.start_pos = tuple(event.pos[i]-event.rel[i] for i in range(2)) 132 self.start_slider_pos = self.slider_pos 133 if self.button.is_over(self.start_pos): 134 self.drag_state = True 135 else: 136 self.drag_state = False 137 138 if self.drag_state == True: 139 if self.horizontal: 140 dir = 0 141 else: 142 dir = 1 143 144 mouse_pos = pygame.mouse.get_pos() 145 rel = mouse_pos[dir] - self.start_pos[dir] 146 unit = self._calc_length(1) * self.real_size[dir] 147 movement = int( ( rel + (unit / 2.) ) // unit ) 148 149 new_pos = self.safe_pos(self.start_slider_pos + movement) 150 self.slider_pos = new_pos 151 152 raise constants.Handled 153 154 def safe_pos(self, value): 155 return max(0, min(self.slider_max, value)) 156 157 def handle_click(self, event): 158 if self.drag_state == True: 159 self.drag_state = None 160 if not self.is_over(pygame.mouse.get_pos()): 161 raise constants.Handled 162 else: 163 self.drag_state = None 164 165 def jump(self, go_lower, big_jump=False, tiny_jump=False): 166 if big_jump: 167 jump_dist = max(1, self.slider_max // 2) 168 elif tiny_jump: 169 jump_dist = max(1, self.slider_max // 100) 170 else: 171 jump_dist = max(1, self.slider_size - 1) 172 if go_lower: 173 self.slider_pos = self.safe_pos(self.slider_pos - jump_dist) 174 else: 175 self.slider_pos = self.safe_pos(self.slider_pos + jump_dist) 176 177 def handle_key(self, event): 178 if event.key not in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_KP_PLUS, pygame.K_KP_MINUS): 179 return 180 go_lower = event.key in (pygame.K_LEFT, pygame.K_KP_MINUS) 181 if event.key in (pygame.K_LEFT, pygame.K_RIGHT): 182 big_jump = (event.mod & pygame.KMOD_SHIFT) 183 tiny_jump = (event.mod & pygame.KMOD_CTRL) 184 else: 185 tiny_jump = big_jump = False 186 self.jump(go_lower, big_jump=big_jump, tiny_jump=tiny_jump) 187 raise constants.Handled 188 189 def activated(self, event): 190 assert event.type == pygame.MOUSEBUTTONUP 191 if self.horizontal: 192 self.jump(go_lower=(event.pos[0] < self.button.collision_rect[0])) 193 else: 194 self.jump(go_lower = event.pos[1] < self.button.collision_rect[1]) 195 raise constants.Handled 196 197 198class UpdateSlider(Slider): 199 def _on_slider_move(self): 200 self.update_func(self.slider_pos) 201 202 _slider_pos = widget.call_on_change("__slider_pos", _on_slider_move) 203 204 def __init__(self, *args, **kwargs): 205 self.update_func = kwargs.pop("update_func", lambda value: None) 206 super(UpdateSlider, self).__init__(*args, **kwargs) 207