summaryrefslogtreecommitdiffstats
path: root/Lib/stdwin/Sliders.py
blob: a07c3c4b19eee56e00f7c9ffdd663151320f9ed2 (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
# Module 'Sliders'


import stdwin
from stdwinevents import *
import rect
from Buttons import *
from HVSplit import HSplit


# Field indices in event detail
#
_HV = 0
_CLICKS = 1
_BUTTON = 2
_MASK = 3


# DragSlider is the simplest possible slider.
# It looks like a button but dragging the mouse left or right
# changes the controlled value.
# It does not support any of the triggers or hooks defined by Buttons,
# but defines its own setval_trigger and setval_hook.
#
class DragSliderReactivity() = BaseReactivity():
	#
	def mouse_down(self, detail):
		h, v = hv = detail[_HV]
		if self.enabled and self.mousetest(hv):
			self.anchor = h
			self.oldval = self.val
			self.active = 1
	#
	def mouse_move(self, detail):
		if self.active:
			h, v = detail[_HV]
			self.setval(self.oldval + (h - self.anchor))
	#
	def mouse_up(self, detail):
		if self.active:
			h, v = detail[_HV]
			self.setval(self.oldval + (h - self.anchor))
			self.active = 0
	#

class DragSliderAppearance() = ButtonAppearance():
	#
	# INVARIANTS maintained by the setval method:
	#
	#	self.min <= self.val <= self.max
	#	self.text = self.pretext + `self.val` + self.postext
	#
	# (Notice that unlike Python ranges, the end point belongs
	# to the range.)
	#
	def init_appearance(self):
		ButtonAppearance.init_appearance(self)
		self.min = 0
		self.val = 0
		self.max = 100
		self.hook = 0
		self.pretext = self.postext = ''
		self.recalctext()
	#
	# The 'get*' and 'set*' methods belong to the generic slider interface
	#
	def getval(self): return self.val
	#
	def sethook(self, hook):
		self.hook = hook
	#
	def setminvalmax(self, (min, val, max)):
		self.min = min
		self.max = max
		self.setval(val)
	#
	def settexts(self, (pretext, postext)):
		self.pretext = pretext
		self.postext = postext
		self.recalctext()
	#
	def setval(self, val):
		val = min(self.max, max(self.min, val))
		if val <> self.val:
			self.val = val
			self.recalctext()
			self.trigger()
	#
	def trigger(self):
		if self.hook:
			self.hook(self)
	#
	def recalctext(self):
		self.settext(self.pretext + `self.val` + self.postext)
	#

class DragSlider() = DragSliderReactivity(), DragSliderAppearance(), Define():
	def definetext(self, (parent, text)):
		raise RuntimeError, 'DragSlider.definetext() not supported'


# Auxiliary class for PushButton incorporated in ComplexSlider
#
class _StepButton() = PushButton():
	def define(self, parent):
		self = PushButton.define(self, parent)
		self.step = 0
		return self
	def setstep(self, step):
		self.step = step
	def definetextstep(self, (parent, text, step)):
		self = self.definetext(parent, text)
		self.setstep(step)
		return self
	def init_reactivity(self):
		PushButton.init_reactivity(self)
		self.parent.need_timer(self)
	def step_trigger(self):
		self.parent.setval(self.parent.getval() + self.step)
	def down_trigger(self):
		self.step_trigger()
		self.parent.settimer(5)
	def timer(self):
		if self.hilited:
			self.step_trigger()
		if self.active:
			self.parent.settimer(1)


# A complex slider is an HSplit initialized to three buttons:
# one to step down, a dragslider, and one to step up.
#
class ComplexSlider() = HSplit():
	#
	# Override Slider define() method
	#
	def define(self, parent):
		self = self.create(parent) # HSplit
		#
		self.downbutton = _StepButton().definetextstep(self, '-', -1)
		self.dragbutton = DragSlider().define(self)
		self.upbutton = _StepButton().definetextstep(self, '+', 1)
		#
		return self
	#
	# Override HSplit methods
	#
	def getminsize(self, (m, (width, height))):
		w1, h1 = self.downbutton.getminsize(m, (0, height))
		w3, h3 = self.upbutton.getminsize(m, (0, height))
		w1 = max(w1, h1)
		w3 = max(w3, h3)
		w2, h2 = self.dragbutton.getminsize(m, (width-w1-w3, height))
		return w1+w2+w3, max(h1, h2, h3)
	#
	def setbounds(self, bounds):
		(left, top), (right, bottom) = self.bounds = bounds
		size = bottom - top
		self.downbutton.setbounds((left, top), (left+size, bottom))
		self.dragbutton.setbounds((left+size, top), \
						(right-size, bottom))
		self.upbutton.setbounds((right-size, top), (right, bottom))
	#
	# Pass other Slider methods on to dragbutton
	#
	def getval(self): return self.dragbutton.getval()
	def sethook(self, hook): self.dragbutton.sethook(hook)
	def setminvalmax(self, args): self.dragbutton.setminvalmax(args)
	def settexts(self, args): self.dragbutton.settexts(args)
	def setval(self, val): self.dragbutton.setval(val)
	def enable(self, flag):
		self.downbutton.enable(flag)
		self.dragbutton.enable(flag)
		self.upbutton.enable(flag)