*** empty log message ***
diff --git a/Lib/lib-stdwin/Buttons.py b/Lib/lib-stdwin/Buttons.py
index 5395f88..3ad5605 100644
--- a/Lib/lib-stdwin/Buttons.py
+++ b/Lib/lib-stdwin/Buttons.py
@@ -1,10 +1,8 @@
 # Module 'Buttons'
 
 
-from Resize import *
-
-
-# Import module 'rect' renamed as '_rect'
+# Import module 'rect' renamed as '_rect' to avoid exporting it on
+# 'from Buttons import *'
 #
 import rect
 _rect = rect
@@ -28,61 +26,69 @@
 	#
 	# Initialization
 	#
-	def init_appearance(self, (win, bounds)):
-		self.win = win
-		self.bounds = bounds
+	def init_appearance(self):
+		self.bounds = _rect.empty
 		self.enabled = 1
 		self.hilited = 0
 		self.selected = 0
 		self.text = ''
-		self.limbo = 1
-		self.recalc()
-		self.win.change(self.bounds)
-		# While the limbo flag is set, redraw calls are ignored.
-		# It is cleared by the first draw event.
-		# This is intended to avoid duplicate drawing during
-		# initialization.
+	#
+	# Size enquiry
+	#
+	def minsize(self, m):
+		try:
+			self.text = self.text
+		except NameError:
+			self.text = ''
+		return m.textwidth(self.text) + 6, m.lineheight() + 6
+	#
+	def getbounds(self):
+		return self.bounds
 	#
 	# Changing the parameters
 	#
 	def settext(self, text):
 		self.text = text
-		self.recalctextpos()
-		self.redraw()
+		if self.bounds <> _rect.empty:
+			self.recalctextpos()
+			self.redraw()
 	#
 	def setbounds(self, bounds):
-		# This delays drawing until after all buttons are moved
-		self.win.change(self.bounds)
+		if self.bounds <> _rect.empty:
+			self.parent.change(self.bounds)
 		self.bounds = bounds
-		self.recalc()
-		self.win.change(bounds)
+		if self.bounds <> _rect.empty:
+			self.recalc()
+			self.parent.change(bounds)
 	#
 	# Changing the state bits
 	#
 	def enable(self, flag):
 		if flag <> self.enabled:
 			self.enabled = flag
-			if not self.limbo:
-				self.flipenable(self.win.begindrawing())
+			if self.bounds <> _rect.empty:
+				self.flipenable(self.parent.begindrawing())
 	#
 	def hilite(self, flag):
 		if flag <> self.hilited:
 			self.hilited = flag
-			if not self.limbo:
-				self.fliphilite(self.win.begindrawing())
+			if self.bounds <> _rect.empty:
+				self.fliphilite(self.parent.begindrawing())
 	#
 	def select(self, flag):
 		if flag <> self.selected:
 			self.selected = flag
-			self.redraw()
+			if self.bounds <> _rect.empty:
+				self.redraw()
 	#
 	# Recalculate the box bounds and text position.
 	# This can be overridden by buttons that draw different boxes
 	# or want their text in a different position.
 	#
 	def recalc(self):
-		self.recalcbounds()
-		self.recalctextpos()
+		if self.bounds <> _rect.empty:
+			self.recalcbounds()
+			self.recalctextpos()
 	#
 	def recalcbounds(self):
 		self.hilitebounds = _rect.inset(self.bounds, (3, 3))
@@ -90,20 +96,19 @@
 	#
 	def recalctextpos(self):
 		(left, top), (right, bottom) = self.bounds
-		d = self.win.begindrawing()
-		h = (left + right - d.textwidth(self.text)) / 2
-		v = (top + bottom - d.lineheight()) / 2
+		m = self.parent.beginmeasuring()
+		h = (left + right - m.textwidth(self.text)) / 2
+		v = (top + bottom - m.lineheight()) / 2
 		self.textpos = h, v
 	#
-	# Generic drawing mechanism.
+	# Generic drawing interface.
 	# Do not override redraw() or draw() methods; override drawit() c.s.
 	#
 	def redraw(self):
-		if not self.limbo:
-			self.draw(self.win.begindrawing(), self.bounds)
+		if self.bounds <> _rect.empty:
+			self.draw(self.parent.begindrawing(), self.bounds)
 	#
 	def draw(self, (d, area)):
-		self.limbo = 0
 		area = _rect.intersect(area, self.bounds)
 		if area = _rect.empty:
 			return
@@ -161,6 +166,10 @@
 #
 class CheckAppearance() = LabelAppearance():
 	#
+	def minsize(self, m):
+		width, height = m.textwidth(self.text) + 6, m.lineheight() + 6
+		return width + height + m.textwidth(' '), height
+	#
 	def drawpict(self, d):
 		d.box(self.boxbounds)
 		if self.selected: _xorcross(d, self.boxbounds)
@@ -173,10 +182,10 @@
 		self.hilitebounds = self.boxbounds
 	#
 	def recalctextpos(self):
-		d = self.win.begindrawing()
+		m = self.parent.beginmeasuring()
 		(left, top), (right, bottom) = self.boxbounds
-		h = right + d.textwidth(' ')
-		v = top + (self.size - d.lineheight()) / 2
+		h = right + m.textwidth(' ')
+		v = top + (self.size - m.lineheight()) / 2
 		self.textpos = h, v
 	#
 
@@ -199,24 +208,31 @@
 	#
 
 
-# NoReactivity ignores mouse and timer events.
+# NoReactivity ignores mouse events.
+#
+class NoReactivity():
+	def init_reactivity(self): pass
+
+
+# BaseReactivity defines hooks and asks for mouse events,
+# but provides only dummy mouse event handlers.
 # The trigger methods call the corresponding hooks set by the user.
 # Hooks (and triggers) mean the following:
 # down_hook	called on some mouse-down events
 # move_hook	called on some mouse-move events
 # up_hook	called on mouse-up events
 # on_hook	called for buttons with on/off state, when it goes on
-# timer_hook	called on timer events
 # hook		called when a button 'fires' or a radiobutton goes on
 # There are usually extra conditions, e.g., hooks are only called
 # when the button is enabled, or active, or selected (on).
 #
-class NoReactivity():
+class BaseReactivity():
 	#
 	def init_reactivity(self):
 		self.down_hook = self.move_hook = self.up_hook = \
-		  self.on_hook = self.off_hook = self.timer_hook = \
-		  self.hook = self.active = 0
+			self.on_hook = self.off_hook = \
+			self.hook = self.active = 0
+		self.parent.need_mouse(self)
 	#
 	def mousetest(self, hv):
 		return _rect.pointinrect(hv, self.bounds)
@@ -230,9 +246,6 @@
 	def mouse_up(self, detail):
 		pass
 	#
-	def timer(self):
-		pass
-	#
 	def down_trigger(self):
 		if self.down_hook: self.down_hook(self)
 	#
@@ -248,18 +261,14 @@
 	def off_trigger(self):
 		if self.off_hook: self.off_hook(self)
 	#
-	def timer_trigger(self):
-		if self.timer_hook: self.timer_hook(self)
-	#
 	def trigger(self):
 		if self.hook: self.hook(self)
 
 
 # ToggleReactivity acts like a simple pushbutton.
 # It toggles its hilite state on mouse down events.
-# Its timer_trigger method is called for all timer events while hilited.
 #
-class ToggleReactivity() = NoReactivity():
+class ToggleReactivity() = BaseReactivity():
 	#
 	def mouse_down(self, detail):
 		if self.enabled and self.mousetest(detail[_HV]):
@@ -276,10 +285,6 @@
 			self.up_trigger()
 			self.active = 0
 	#
-	def timer(self):
-		if self.hilited:
-			self.timer_trigger()
-	#
 	def down_trigger(self):
 		if self.hilited:
 			self.on_trigger()
@@ -292,7 +297,7 @@
 # TriggerReactivity acts like a fancy pushbutton.
 # It hilites itself while the mouse is down within its bounds.
 #
-class TriggerReactivity() = NoReactivity():
+class TriggerReactivity() = BaseReactivity():
 	#
 	def mouse_down(self, detail):
 		if self.enabled and self.mousetest(detail[_HV]):
@@ -315,10 +320,6 @@
 			self.active = 0
 			self.hilite(0)
 	#
-	def timer(self):
-		if self.active and self.hilited:
-			self.timer_trigger()
-	#
 
 
 # CheckReactivity handles mouse events like TriggerReactivity,
@@ -356,13 +357,22 @@
 
 
 # Auxiliary class for 'define' method.
+# Call the initializers in the right order.
 #
-class Define() = NoResize():
+class Define():
 	#
-	def define(self, (win, bounds, text)):
-		self.init_appearance(win, bounds)
+	def define(self, parent):
+		self.parent = parent
+		parent.addchild(self)
+		self.init_appearance()
 		self.init_reactivity()
-		self.init_resize()
+		return self
+	#
+	def destroy(self):
+		self.parent = 0
+	#
+	def definetext(self, (parent, text)):
+		self = self.define(parent)
 		self.settext(text)
 		return self
 
@@ -380,11 +390,10 @@
 	d.xorline((left, bottom), (right, top))
 
 
-# Ready-made button classes
+# Ready-made button classes.
 #
-class BaseButton() = NoReactivity(), LabelAppearance(), Define(): pass
 class Label() = NoReactivity(), LabelAppearance(), Define(): pass
-class ClassicButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
+class PushButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
 class CheckButton() = CheckReactivity(), CheckAppearance(), Define(): pass
 class RadioButton() = RadioReactivity(), RadioAppearance(), Define(): pass
-class Toggle() = ToggleReactivity(), ButtonAppearance(), Define(): pass
+class ToggleButton() = ToggleReactivity(), ButtonAppearance(), Define(): pass
diff --git a/Lib/lib-stdwin/Histogram.py b/Lib/lib-stdwin/Histogram.py
index f469272..505bbaf 100644
--- a/Lib/lib-stdwin/Histogram.py
+++ b/Lib/lib-stdwin/Histogram.py
@@ -1,41 +1,36 @@
 # Module 'Histogram'
 
 from Buttons import *
-from Resize import Resize
-
 
 # A Histogram displays a histogram of numeric data.
-# It reacts to resize events by resizing itself,
-# leaving the same amount of space around the borders.
-# (This is geometry management, and should really be implemented
-# by a different group of classes, but for now this hack is OK.)
 #
-class HistogramAppearance() = Resize(), LabelAppearance():
+class HistogramAppearance() = LabelAppearance(), Define():
 	#
-	def define(self, (win, bounds, ydata, scale)):
-		self.init_appearance(win, bounds)
-		self.init_resize()
-		self.ydata = ydata
-		self.scale = scale # (min, max)
+	def define(self, parent):
+		Define.define(self, (parent, ''))
+		self.ydata = []
+		self.scale = (0, 100)
 		return self
 	#
 	def setdata(self, (ydata, scale)):
 		self.ydata = ydata
 		self.scale = scale # (min, max)
-		self.win.change(self.bounds)
+		self.parent.change(self.bounds)
 	#
-	def drawit(self, d):
-		ydata = self.ydata
+	def drawpict(self, d):
 		(left, top), (right, bottom) = self.bounds
 		min, max = self.scale
 		size = max-min
 		width, height = right-left, bottom-top
-		for i in range(len(ydata)):
-			h0 = left + i * width/len(ydata)
-			h1 = left + (i+1) * width/len(ydata)
-			v0 = top + height - (self.ydata[i]-min)*height/size
-			v1 = top + height
+		ydata = self.ydata
+		npoints = len(ydata)
+		v1 = top + height	# constant
+		h1 = left		# changed in loop
+		for i in range(npoints):
+			h0 = h1
+			v0 = top + height - (ydata[i]-min)*height/size
+			h1 = left + (i+1) * width/npoints
 			d.paint((h0, v0), (h1, v1))
 	#
 
-class Histogram() = HistogramAppearance(), NoReactivity(): pass
+class Histogram() = NoReactivity(), HistogramAppearance(): pass
diff --git a/Lib/lib-stdwin/Sliders.py b/Lib/lib-stdwin/Sliders.py
index b142ef4..f655e77 100644
--- a/Lib/lib-stdwin/Sliders.py
+++ b/Lib/lib-stdwin/Sliders.py
@@ -1,13 +1,11 @@
 # Module 'Sliders'
-#
-# XXX Should split caller interface, appearance and reactivity better
 
 
 import stdwin
 from stdwinevents import *
 import rect
 from Buttons import *
-from Resize import *
+from HVSplit import HSplit
 
 
 # Field indices in event detail
@@ -18,11 +16,13 @@
 _MASK = 3
 
 
-# A dragslider is the simplest possible slider.
+# 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() = NoReactivity():
+class DragSliderReactivity() = BaseReactivity():
 	#
 	def mouse_down(self, detail):
 		h, v = hv = detail[_HV]
@@ -43,136 +43,133 @@
 			self.active = 0
 	#
 
-class DragSliderAppearance() = NoResize(), 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
+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 in Python ranges, the end point belongs
+	# (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.setval_trigger()
-			# (The trigger may change val, pretext and postext)
-			self.settext(self.pretext + `self.val` + self.postext)
+			self.recalctext()
+			self.trigger()
 	#
-	def setval_trigger(self):
-		if self.setval_hook:
-			self.setval_hook(self)
+	def trigger(self):
+		if self.hook:
+			self.hook(self)
+	#
+	def recalctext(self):
+		self.settext(self.pretext + `self.val` + self.postext)
 	#
 
-class DragSlider() = DragSliderReactivity(), DragSliderAppearance(): pass
+class DragSlider() = DragSliderReactivity(), DragSliderAppearance(), Define():
+	def definetext(self, (parent, text)):
+		raise RuntimeError, 'DragSlider.definetext() not supported'
 
 
-# Auxiliary class for DragSlider incorporated in ComplexSlider
+# Auxiliary class for PushButton 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
+class _StepButton() = PushButton():
+	def define(self, parent):
+		self = PushButton.define(self, parent)
+		self.step = 0
+		return self
+	def setstep(self, step):
 		self.step = step
-		return ClassicButton.define(self, (win, bounds, text))
+	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.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)
+		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 a wrapper around three buttons:
-# One to step down, a dragslider, and one to step up.
+
+# A complex slider is an HSplit initialized to three buttons:
+# one to step down, a dragslider, and one to step up.
 #
-class ComplexSlider() = NoResize(), LabelAppearance(), NoReactivity():
+class ComplexSlider() = HSplit():
 	#
-	def define(self, (win, bounds)):
+	# Override Slider define() method
+	#
+	def define(self, parent):
+		self = self.create(parent) # HSplit
 		#
-		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]
+		self.downbutton = _StepButton().definetextstep(self, '-', -1)
+		self.dragbutton = DragSlider().define(self)
+		self.upbutton = _StepButton().definetextstep(self, '+', 1)
 		#
 		return self
 	#
-	def mouse_down(self, detail):
-		for b in self.children:
-			b.mouse_down(detail)
+	# Override HSplit methods
 	#
-	def mouse_move(self, detail):
-		for b in self.children:
-			b.mouse_move(detail)
+	def minsize(self, m):
+		w1, h1 = self.downbutton.minsize(m)
+		w2, h2 = self.dragbutton.minsize(m)
+		w3, h3 = self.upbutton.minsize(m)
+		height = max(h1, h2, h3)
+		w1 = max(w1, height)
+		w3 = max(w3, height)
+		return w1+w2+w3, height
 	#
-	def mouse_up(self, detail):
-		for b in self.children:
-			b.mouse_up(detail)
+	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))
 	#
-	def timer(self):
-		for b in self.children:
-			b.timer()
+	# Pass other Slider methods on to dragbutton
 	#
-	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)
-	#
+	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)
diff --git a/Lib/lib-stdwin/StripChart.py b/Lib/lib-stdwin/StripChart.py
index 2021cc4..ac767c3 100644
--- a/Lib/lib-stdwin/StripChart.py
+++ b/Lib/lib-stdwin/StripChart.py
@@ -1,17 +1,18 @@
 # Module 'StripChart'
 
-
 import rect
-from Buttons import *
-from Resize import *
+from Buttons import LabelAppearance, NoReactivity
 
+# A StripChart doesn't really look like a label but it needs a base class.
+# LabelAppearance allows it to be disabled and hilited.
 
-class StripChart() = LabelAppearance(), NoReactivity(), NoResize():
+class StripChart() = LabelAppearance(), NoReactivity():
 	#
-	def define(self, (win, bounds, scale)):
-		self.init_appearance(win, bounds)
+	def define(self, (parent, scale)):
+		self.parent = parent
+		parent.addchild(self)
+		self.init_appearance()
 		self.init_reactivity()
-		self.init_resize()
 		self.ydata = []
 		self.scale = scale
 		self.resetbounds()
@@ -37,18 +38,18 @@
 		excess = len(self.ydata) - self.width
 		if excess > 0:
 			del self.ydata[:excess]
-			if not self.limbo:
-				self.win.scroll(self.bounds, (-excess, 0))
-		if not self.limbo:
+			if self.bounds <> rect.empty:
+				self.parent.scroll(self.bounds, (-excess, 0))
+		if self.bounds <> rect.empty:
 			(left, top), (right, bottom) = self.bounds
 			i = len(self.ydata)
 			area = (left+i-1, top), (left+i, bottom)
-			self.draw(self.win.begindrawing(), area)
+			self.draw(self.parent.begindrawing(), area)
 	#
 	def draw(self, (d, area)):
-		self.limbo = 0
 		area = rect.intersect(area, self.bounds)
 		if area = rect.empty:
+			print 'mt'
 			return
 		d.cliprect(area)
 		d.erase(self.bounds)
diff --git a/Lib/lib-stdwin/VUMeter.py b/Lib/lib-stdwin/VUMeter.py
index 65c018b..59fc36f 100644
--- a/Lib/lib-stdwin/VUMeter.py
+++ b/Lib/lib-stdwin/VUMeter.py
@@ -10,8 +10,9 @@
 	#
 	# Override define() and timer() methods
 	#
-	def define(self, (win, bounds)):
-		self = StripChart.define(self, (win, bounds, 128))
+	def define(self, parent):
+		self = StripChart.define(self, (parent, 128))
+		self.parent.need_timer(self)
 		self.sampling = 0
 		self.rate = 3
 		self.enable(0)
@@ -31,7 +32,7 @@
 			audio.start_recording(size)
 			self.sampling = 1
 		if self.sampling:
-			self.win.settimer(1)
+			self.parent.settimer(1)
 	#
 	# New methods: start() and stop()
 	#
diff --git a/Lib/stdwin/Buttons.py b/Lib/stdwin/Buttons.py
index 5395f88..3ad5605 100755
--- a/Lib/stdwin/Buttons.py
+++ b/Lib/stdwin/Buttons.py
@@ -1,10 +1,8 @@
 # Module 'Buttons'
 
 
-from Resize import *
-
-
-# Import module 'rect' renamed as '_rect'
+# Import module 'rect' renamed as '_rect' to avoid exporting it on
+# 'from Buttons import *'
 #
 import rect
 _rect = rect
@@ -28,61 +26,69 @@
 	#
 	# Initialization
 	#
-	def init_appearance(self, (win, bounds)):
-		self.win = win
-		self.bounds = bounds
+	def init_appearance(self):
+		self.bounds = _rect.empty
 		self.enabled = 1
 		self.hilited = 0
 		self.selected = 0
 		self.text = ''
-		self.limbo = 1
-		self.recalc()
-		self.win.change(self.bounds)
-		# While the limbo flag is set, redraw calls are ignored.
-		# It is cleared by the first draw event.
-		# This is intended to avoid duplicate drawing during
-		# initialization.
+	#
+	# Size enquiry
+	#
+	def minsize(self, m):
+		try:
+			self.text = self.text
+		except NameError:
+			self.text = ''
+		return m.textwidth(self.text) + 6, m.lineheight() + 6
+	#
+	def getbounds(self):
+		return self.bounds
 	#
 	# Changing the parameters
 	#
 	def settext(self, text):
 		self.text = text
-		self.recalctextpos()
-		self.redraw()
+		if self.bounds <> _rect.empty:
+			self.recalctextpos()
+			self.redraw()
 	#
 	def setbounds(self, bounds):
-		# This delays drawing until after all buttons are moved
-		self.win.change(self.bounds)
+		if self.bounds <> _rect.empty:
+			self.parent.change(self.bounds)
 		self.bounds = bounds
-		self.recalc()
-		self.win.change(bounds)
+		if self.bounds <> _rect.empty:
+			self.recalc()
+			self.parent.change(bounds)
 	#
 	# Changing the state bits
 	#
 	def enable(self, flag):
 		if flag <> self.enabled:
 			self.enabled = flag
-			if not self.limbo:
-				self.flipenable(self.win.begindrawing())
+			if self.bounds <> _rect.empty:
+				self.flipenable(self.parent.begindrawing())
 	#
 	def hilite(self, flag):
 		if flag <> self.hilited:
 			self.hilited = flag
-			if not self.limbo:
-				self.fliphilite(self.win.begindrawing())
+			if self.bounds <> _rect.empty:
+				self.fliphilite(self.parent.begindrawing())
 	#
 	def select(self, flag):
 		if flag <> self.selected:
 			self.selected = flag
-			self.redraw()
+			if self.bounds <> _rect.empty:
+				self.redraw()
 	#
 	# Recalculate the box bounds and text position.
 	# This can be overridden by buttons that draw different boxes
 	# or want their text in a different position.
 	#
 	def recalc(self):
-		self.recalcbounds()
-		self.recalctextpos()
+		if self.bounds <> _rect.empty:
+			self.recalcbounds()
+			self.recalctextpos()
 	#
 	def recalcbounds(self):
 		self.hilitebounds = _rect.inset(self.bounds, (3, 3))
@@ -90,20 +96,19 @@
 	#
 	def recalctextpos(self):
 		(left, top), (right, bottom) = self.bounds
-		d = self.win.begindrawing()
-		h = (left + right - d.textwidth(self.text)) / 2
-		v = (top + bottom - d.lineheight()) / 2
+		m = self.parent.beginmeasuring()
+		h = (left + right - m.textwidth(self.text)) / 2
+		v = (top + bottom - m.lineheight()) / 2
 		self.textpos = h, v
 	#
-	# Generic drawing mechanism.
+	# Generic drawing interface.
 	# Do not override redraw() or draw() methods; override drawit() c.s.
 	#
 	def redraw(self):
-		if not self.limbo:
-			self.draw(self.win.begindrawing(), self.bounds)
+		if self.bounds <> _rect.empty:
+			self.draw(self.parent.begindrawing(), self.bounds)
 	#
 	def draw(self, (d, area)):
-		self.limbo = 0
 		area = _rect.intersect(area, self.bounds)
 		if area = _rect.empty:
 			return
@@ -161,6 +166,10 @@
 #
 class CheckAppearance() = LabelAppearance():
 	#
+	def minsize(self, m):
+		width, height = m.textwidth(self.text) + 6, m.lineheight() + 6
+		return width + height + m.textwidth(' '), height
+	#
 	def drawpict(self, d):
 		d.box(self.boxbounds)
 		if self.selected: _xorcross(d, self.boxbounds)
@@ -173,10 +182,10 @@
 		self.hilitebounds = self.boxbounds
 	#
 	def recalctextpos(self):
-		d = self.win.begindrawing()
+		m = self.parent.beginmeasuring()
 		(left, top), (right, bottom) = self.boxbounds
-		h = right + d.textwidth(' ')
-		v = top + (self.size - d.lineheight()) / 2
+		h = right + m.textwidth(' ')
+		v = top + (self.size - m.lineheight()) / 2
 		self.textpos = h, v
 	#
 
@@ -199,24 +208,31 @@
 	#
 
 
-# NoReactivity ignores mouse and timer events.
+# NoReactivity ignores mouse events.
+#
+class NoReactivity():
+	def init_reactivity(self): pass
+
+
+# BaseReactivity defines hooks and asks for mouse events,
+# but provides only dummy mouse event handlers.
 # The trigger methods call the corresponding hooks set by the user.
 # Hooks (and triggers) mean the following:
 # down_hook	called on some mouse-down events
 # move_hook	called on some mouse-move events
 # up_hook	called on mouse-up events
 # on_hook	called for buttons with on/off state, when it goes on
-# timer_hook	called on timer events
 # hook		called when a button 'fires' or a radiobutton goes on
 # There are usually extra conditions, e.g., hooks are only called
 # when the button is enabled, or active, or selected (on).
 #
-class NoReactivity():
+class BaseReactivity():
 	#
 	def init_reactivity(self):
 		self.down_hook = self.move_hook = self.up_hook = \
-		  self.on_hook = self.off_hook = self.timer_hook = \
-		  self.hook = self.active = 0
+			self.on_hook = self.off_hook = \
+			self.hook = self.active = 0
+		self.parent.need_mouse(self)
 	#
 	def mousetest(self, hv):
 		return _rect.pointinrect(hv, self.bounds)
@@ -230,9 +246,6 @@
 	def mouse_up(self, detail):
 		pass
 	#
-	def timer(self):
-		pass
-	#
 	def down_trigger(self):
 		if self.down_hook: self.down_hook(self)
 	#
@@ -248,18 +261,14 @@
 	def off_trigger(self):
 		if self.off_hook: self.off_hook(self)
 	#
-	def timer_trigger(self):
-		if self.timer_hook: self.timer_hook(self)
-	#
 	def trigger(self):
 		if self.hook: self.hook(self)
 
 
 # ToggleReactivity acts like a simple pushbutton.
 # It toggles its hilite state on mouse down events.
-# Its timer_trigger method is called for all timer events while hilited.
 #
-class ToggleReactivity() = NoReactivity():
+class ToggleReactivity() = BaseReactivity():
 	#
 	def mouse_down(self, detail):
 		if self.enabled and self.mousetest(detail[_HV]):
@@ -276,10 +285,6 @@
 			self.up_trigger()
 			self.active = 0
 	#
-	def timer(self):
-		if self.hilited:
-			self.timer_trigger()
-	#
 	def down_trigger(self):
 		if self.hilited:
 			self.on_trigger()
@@ -292,7 +297,7 @@
 # TriggerReactivity acts like a fancy pushbutton.
 # It hilites itself while the mouse is down within its bounds.
 #
-class TriggerReactivity() = NoReactivity():
+class TriggerReactivity() = BaseReactivity():
 	#
 	def mouse_down(self, detail):
 		if self.enabled and self.mousetest(detail[_HV]):
@@ -315,10 +320,6 @@
 			self.active = 0
 			self.hilite(0)
 	#
-	def timer(self):
-		if self.active and self.hilited:
-			self.timer_trigger()
-	#
 
 
 # CheckReactivity handles mouse events like TriggerReactivity,
@@ -356,13 +357,22 @@
 
 
 # Auxiliary class for 'define' method.
+# Call the initializers in the right order.
 #
-class Define() = NoResize():
+class Define():
 	#
-	def define(self, (win, bounds, text)):
-		self.init_appearance(win, bounds)
+	def define(self, parent):
+		self.parent = parent
+		parent.addchild(self)
+		self.init_appearance()
 		self.init_reactivity()
-		self.init_resize()
+		return self
+	#
+	def destroy(self):
+		self.parent = 0
+	#
+	def definetext(self, (parent, text)):
+		self = self.define(parent)
 		self.settext(text)
 		return self
 
@@ -380,11 +390,10 @@
 	d.xorline((left, bottom), (right, top))
 
 
-# Ready-made button classes
+# Ready-made button classes.
 #
-class BaseButton() = NoReactivity(), LabelAppearance(), Define(): pass
 class Label() = NoReactivity(), LabelAppearance(), Define(): pass
-class ClassicButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
+class PushButton() = TriggerReactivity(), ButtonAppearance(), Define(): pass
 class CheckButton() = CheckReactivity(), CheckAppearance(), Define(): pass
 class RadioButton() = RadioReactivity(), RadioAppearance(), Define(): pass
-class Toggle() = ToggleReactivity(), ButtonAppearance(), Define(): pass
+class ToggleButton() = ToggleReactivity(), ButtonAppearance(), Define(): pass
diff --git a/Lib/stdwin/Histogram.py b/Lib/stdwin/Histogram.py
index f469272..505bbaf 100755
--- a/Lib/stdwin/Histogram.py
+++ b/Lib/stdwin/Histogram.py
@@ -1,41 +1,36 @@
 # Module 'Histogram'
 
 from Buttons import *
-from Resize import Resize
-
 
 # A Histogram displays a histogram of numeric data.
-# It reacts to resize events by resizing itself,
-# leaving the same amount of space around the borders.
-# (This is geometry management, and should really be implemented
-# by a different group of classes, but for now this hack is OK.)
 #
-class HistogramAppearance() = Resize(), LabelAppearance():
+class HistogramAppearance() = LabelAppearance(), Define():
 	#
-	def define(self, (win, bounds, ydata, scale)):
-		self.init_appearance(win, bounds)
-		self.init_resize()
-		self.ydata = ydata
-		self.scale = scale # (min, max)
+	def define(self, parent):
+		Define.define(self, (parent, ''))
+		self.ydata = []
+		self.scale = (0, 100)
 		return self
 	#
 	def setdata(self, (ydata, scale)):
 		self.ydata = ydata
 		self.scale = scale # (min, max)
-		self.win.change(self.bounds)
+		self.parent.change(self.bounds)
 	#
-	def drawit(self, d):
-		ydata = self.ydata
+	def drawpict(self, d):
 		(left, top), (right, bottom) = self.bounds
 		min, max = self.scale
 		size = max-min
 		width, height = right-left, bottom-top
-		for i in range(len(ydata)):
-			h0 = left + i * width/len(ydata)
-			h1 = left + (i+1) * width/len(ydata)
-			v0 = top + height - (self.ydata[i]-min)*height/size
-			v1 = top + height
+		ydata = self.ydata
+		npoints = len(ydata)
+		v1 = top + height	# constant
+		h1 = left		# changed in loop
+		for i in range(npoints):
+			h0 = h1
+			v0 = top + height - (ydata[i]-min)*height/size
+			h1 = left + (i+1) * width/npoints
 			d.paint((h0, v0), (h1, v1))
 	#
 
-class Histogram() = HistogramAppearance(), NoReactivity(): pass
+class Histogram() = NoReactivity(), HistogramAppearance(): pass
diff --git a/Lib/stdwin/Sliders.py b/Lib/stdwin/Sliders.py
index b142ef4..f655e77 100755
--- a/Lib/stdwin/Sliders.py
+++ b/Lib/stdwin/Sliders.py
@@ -1,13 +1,11 @@
 # Module 'Sliders'
-#
-# XXX Should split caller interface, appearance and reactivity better
 
 
 import stdwin
 from stdwinevents import *
 import rect
 from Buttons import *
-from Resize import *
+from HVSplit import HSplit
 
 
 # Field indices in event detail
@@ -18,11 +16,13 @@
 _MASK = 3
 
 
-# A dragslider is the simplest possible slider.
+# 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() = NoReactivity():
+class DragSliderReactivity() = BaseReactivity():
 	#
 	def mouse_down(self, detail):
 		h, v = hv = detail[_HV]
@@ -43,136 +43,133 @@
 			self.active = 0
 	#
 
-class DragSliderAppearance() = NoResize(), 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
+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 in Python ranges, the end point belongs
+	# (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.setval_trigger()
-			# (The trigger may change val, pretext and postext)
-			self.settext(self.pretext + `self.val` + self.postext)
+			self.recalctext()
+			self.trigger()
 	#
-	def setval_trigger(self):
-		if self.setval_hook:
-			self.setval_hook(self)
+	def trigger(self):
+		if self.hook:
+			self.hook(self)
+	#
+	def recalctext(self):
+		self.settext(self.pretext + `self.val` + self.postext)
 	#
 
-class DragSlider() = DragSliderReactivity(), DragSliderAppearance(): pass
+class DragSlider() = DragSliderReactivity(), DragSliderAppearance(), Define():
+	def definetext(self, (parent, text)):
+		raise RuntimeError, 'DragSlider.definetext() not supported'
 
 
-# Auxiliary class for DragSlider incorporated in ComplexSlider
+# Auxiliary class for PushButton 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
+class _StepButton() = PushButton():
+	def define(self, parent):
+		self = PushButton.define(self, parent)
+		self.step = 0
+		return self
+	def setstep(self, step):
 		self.step = step
-		return ClassicButton.define(self, (win, bounds, text))
+	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.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)
+		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 a wrapper around three buttons:
-# One to step down, a dragslider, and one to step up.
+
+# A complex slider is an HSplit initialized to three buttons:
+# one to step down, a dragslider, and one to step up.
 #
-class ComplexSlider() = NoResize(), LabelAppearance(), NoReactivity():
+class ComplexSlider() = HSplit():
 	#
-	def define(self, (win, bounds)):
+	# Override Slider define() method
+	#
+	def define(self, parent):
+		self = self.create(parent) # HSplit
 		#
-		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]
+		self.downbutton = _StepButton().definetextstep(self, '-', -1)
+		self.dragbutton = DragSlider().define(self)
+		self.upbutton = _StepButton().definetextstep(self, '+', 1)
 		#
 		return self
 	#
-	def mouse_down(self, detail):
-		for b in self.children:
-			b.mouse_down(detail)
+	# Override HSplit methods
 	#
-	def mouse_move(self, detail):
-		for b in self.children:
-			b.mouse_move(detail)
+	def minsize(self, m):
+		w1, h1 = self.downbutton.minsize(m)
+		w2, h2 = self.dragbutton.minsize(m)
+		w3, h3 = self.upbutton.minsize(m)
+		height = max(h1, h2, h3)
+		w1 = max(w1, height)
+		w3 = max(w3, height)
+		return w1+w2+w3, height
 	#
-	def mouse_up(self, detail):
-		for b in self.children:
-			b.mouse_up(detail)
+	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))
 	#
-	def timer(self):
-		for b in self.children:
-			b.timer()
+	# Pass other Slider methods on to dragbutton
 	#
-	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)
-	#
+	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)
diff --git a/Lib/stdwin/StripChart.py b/Lib/stdwin/StripChart.py
index 2021cc4..ac767c3 100755
--- a/Lib/stdwin/StripChart.py
+++ b/Lib/stdwin/StripChart.py
@@ -1,17 +1,18 @@
 # Module 'StripChart'
 
-
 import rect
-from Buttons import *
-from Resize import *
+from Buttons import LabelAppearance, NoReactivity
 
+# A StripChart doesn't really look like a label but it needs a base class.
+# LabelAppearance allows it to be disabled and hilited.
 
-class StripChart() = LabelAppearance(), NoReactivity(), NoResize():
+class StripChart() = LabelAppearance(), NoReactivity():
 	#
-	def define(self, (win, bounds, scale)):
-		self.init_appearance(win, bounds)
+	def define(self, (parent, scale)):
+		self.parent = parent
+		parent.addchild(self)
+		self.init_appearance()
 		self.init_reactivity()
-		self.init_resize()
 		self.ydata = []
 		self.scale = scale
 		self.resetbounds()
@@ -37,18 +38,18 @@
 		excess = len(self.ydata) - self.width
 		if excess > 0:
 			del self.ydata[:excess]
-			if not self.limbo:
-				self.win.scroll(self.bounds, (-excess, 0))
-		if not self.limbo:
+			if self.bounds <> rect.empty:
+				self.parent.scroll(self.bounds, (-excess, 0))
+		if self.bounds <> rect.empty:
 			(left, top), (right, bottom) = self.bounds
 			i = len(self.ydata)
 			area = (left+i-1, top), (left+i, bottom)
-			self.draw(self.win.begindrawing(), area)
+			self.draw(self.parent.begindrawing(), area)
 	#
 	def draw(self, (d, area)):
-		self.limbo = 0
 		area = rect.intersect(area, self.bounds)
 		if area = rect.empty:
+			print 'mt'
 			return
 		d.cliprect(area)
 		d.erase(self.bounds)
diff --git a/Lib/stdwin/VUMeter.py b/Lib/stdwin/VUMeter.py
index 65c018b..59fc36f 100755
--- a/Lib/stdwin/VUMeter.py
+++ b/Lib/stdwin/VUMeter.py
@@ -10,8 +10,9 @@
 	#
 	# Override define() and timer() methods
 	#
-	def define(self, (win, bounds)):
-		self = StripChart.define(self, (win, bounds, 128))
+	def define(self, parent):
+		self = StripChart.define(self, (parent, 128))
+		self.parent.need_timer(self)
 		self.sampling = 0
 		self.rate = 3
 		self.enable(0)
@@ -31,7 +32,7 @@
 			audio.start_recording(size)
 			self.sampling = 1
 		if self.sampling:
-			self.win.settimer(1)
+			self.parent.settimer(1)
 	#
 	# New methods: start() and stop()
 	#