summaryrefslogtreecommitdiffstats
path: root/Lib/stdwin/Sliders.py
blob: ca67f7930181e619285c6c7b864c47cc5b419826 (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
# Module 'Sliders'
#
# XXX Should split caller interface, appearance and reactivity better


import stdwin
from stdwinevents import *
import rect
from minmax import min, max
from Buttons import *


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


# A dragslider is the simplest possible slider.
# It looks like a button but dragging the mouse left or right
# changes the controlled value.
#
class DragSliderReactivity() = NoReactivity():
	#
	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():
	#
	def define(self, (win, bounds)):
		self.min = 0
		self.val = -1 # Changed by next setval call
		self.max = 100
		self.setval_hook = 0
		self.pretext = self.postext = ''
		self = ClassicButton.define(self, (win, bounds, ''))
		self.setval(50)
		return self
	#
	# INVARIANTS maintained by the setval method:
	#
	#	self.min <= self.val <= self.max
	#	self.text = self.pretext + `self.val` + self.postext
	#
	# (Notice that unlike in Python ranges, the end point belongs
	# to the range.)
	#
	def setval(self, val):
		val = min(self.max, max(self.min, val))
		if val <> self.val:
			self.val = val
			self.setval_trigger()
			# (The trigger may change val, pretext and postext)
			self.settext(self.pretext + `self.val` + self.postext)
	#
	def setval_trigger(self):
		if self.setval_hook:
			self.setval_hook(self)
	#

class DragSlider() = DragSliderReactivity(), DragSliderAppearance(): pass


# Auxiliary class for DragSlider incorporated in ComplexSlider
#
class _SubDragSlider() = DragSlider():
	def define(self, (win, bounds, parent)):
		self.parent = parent
		return DragSlider.define(self, (win, bounds))
	def setval_trigger(self):
		self.parent.val = self.val
		self.parent.setval_trigger()

# Auxiliary class for ClassicButton incorporated in ComplexSlider
#
class _SubClassicButton() = ClassicButton():
	def define(self, (win, bounds, text, step, parent)):
		self.parent = parent
		self.step = step
		return ClassicButton.define(self, (win, bounds, text))
	def down_trigger(self):
		self.parent.setval(self.parent.val + self.step)
		self.delay = 5
		self.win.settimer(self.delay)
	def move_trigger(self):
		self.win.settimer(self.delay)
	def timer_trigger(self):
		self.delay = 1
		self.parent.setval(self.parent.val + self.step)
		self.win.settimer(self.delay)

# A complex slider is a wrapper around three buttons:
# One to step down, a dragslider, and one to step up.
#
class ComplexSlider() = LabelAppearance(), NoReactivity():
	#
	def define(self, (win, bounds)):
		#
		self.win = win
		self.bounds = bounds
		self.setval_hook = 0
		#
		(left, top), (right, bottom) = bounds
		size = bottom - top
		#
		downbox = (left, top), (left+size, bottom)
		sliderbox = (left+size, top), (right-size, bottom)
		upbox = (right-size, top), (right, bottom)
		#
		self.downbutton = \
			_SubClassicButton().define(win, downbox, '-', -1, self)
		#
		self.sliderbutton = \
			_SubDragSlider().define(win, sliderbox, self)
		#
		self.upbutton = \
			_SubClassicButton().define(win, upbox, '+', 1, self)
		#
		self.min = self.sliderbutton.min
		self.val = self.sliderbutton.val
		self.max = self.sliderbutton.max
		self.pretext = self.sliderbutton.pretext
		self.postext = self.sliderbutton.postext
		#
		self.children = \
			[self.downbutton, self.sliderbutton, self.upbutton]
		#
		return self
	#
	def mouse_down(self, detail):
		for b in self.children:
			b.mouse_down(detail)
	#
	def mouse_move(self, detail):
		for b in self.children:
			b.mouse_move(detail)
	#
	def mouse_up(self, detail):
		for b in self.children:
			b.mouse_up(detail)
	#
	def timer(self):
		for b in self.children:
			b.timer()
	#
	def draw(self, area):
		for b in self.children:
			b.draw(area)
	#
	def setval(self, val):
		self.sliderbutton.min = self.min
		self.sliderbutton.max = self.max
		self.sliderbutton.pretext = self.pretext
		self.sliderbutton.postext = self.postext
		self.sliderbutton.setval(val)
	#
	def setval_trigger(self):
		if self.setval_hook:
			self.setval_hook(self)
	#