Skip to content

Commit

Permalink
Add sliders/level meters. (#58)
Browse files Browse the repository at this point in the history
* Add sliders/level meters.
  • Loading branch information
Erik Corry authored Dec 7, 2023
1 parent 2b36b1d commit 176d22a
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
151 changes: 151 additions & 0 deletions src/slider.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (C) 2023 Toitware ApS. All rights reserved.
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file.
import .common
import .element
import .style

/**
A vertical slider that can indicate a value between a minimum and a maxiumum.
You can provide a background to draw when the slider is above a certain level,
and a different one for when the slider is below that level. If either
background is omitted the slider is transparent in that section.
Currently no thumb is drawn, so the indication is the boundary between
the two backgrounds.
*/
class Slider extends CustomElement:
value_/num? := ?
min_/num? := ?
max_/num? := ?
background_lo_ := ?
background_hi_ := ?
horizontal_ := ?

thumb_min_/int
thumb_max_/int?
boundary_/int := 0

type -> string: return "vertical-slider"

constructor --x/int?=null --y/int?=null --w/int?=null --h/int?=null --background-hi=null --background-lo=null --value/num?=null --min/num?=0 --max/num?=100 --thumb_min/int=0 --thumb_max/int?=null --horizontal/bool=false:
value_ = value
min_ = min
max_ = max
background_lo_ = background_lo
background_hi_ = background_hi
thumb_min_ = thumb_min
thumb_max_ = thumb_max
horizontal_ = horizontal
super --x=x --y=y --w=w --h=h
recalculate_

thumb_max: return thumb_max_ or (horizontal_ ? w : h)

recalculate_ -> none:
if not (min_ and max_ and value_ and h): return
if (min_ == max_): return
value_ = max value_ min_
value_ = min value_ max_
old_boundary := boundary_
boundary_ = ((value_ - min_).to_float / (max_ - min_) * (thumb_max - thumb_min_) + 0.1).to_int + thumb_min_
if boundary_ != old_boundary:
top := max old_boundary boundary_
bottom := min old_boundary boundary_
if horizontal_:
invalidate
--x = x + w - top
--w = top - bottom
else:
invalidate
--y = y + h - top
--h = top - bottom

h= value/int -> none:
if value != h:
invalidate
h_ = value
recalculate_
invalidate

w= value/int -> none:
if value != w:
invalidate
w_ = value
recalculate_
invalidate

custom_draw canvas/Canvas -> none:
blend := false
if background_lo_ and boundary_ > thumb_min_:
analysis := ?
if horizontal_:
analysis = canvas.bounds_analysis 0 0 (w - boundary_) h
else:
analysis = canvas.bounds_analysis 0 0 w (h - boundary_)
if analysis != Canvas.DISJOINT:
if analysis == Canvas.CANVAS_IN_AREA or analysis == Canvas.COINCIDENT:
background_lo_.draw canvas 0 0 w h --autocropped
else:
blend = true
if background_hi_ and boundary_ < thumb_max:
analysis := ?
if horizontal_:
analysis = canvas.bounds_analysis (w - boundary_) 0 w h
else:
analysis = canvas.bounds_analysis 0 (h - boundary_) w h
if analysis != Canvas.DISJOINT:
if analysis == Canvas.CANVAS_IN_AREA or analysis == Canvas.COINCIDENT:
background_hi_.draw canvas 0 0 w h --autocropped
else:
blend = true
if not blend: return

lo_alpha := background_lo_ ? canvas.make_alpha_map : ClippingDiv.ALL_TRANSPARENT
hi_alpha := background_hi_ ? canvas.make_alpha_map : ClippingDiv.ALL_TRANSPARENT
lo := canvas.create_similar
hi := canvas.create_similar

if background_lo_:
if horizontal_:
lo_alpha.rectangle 0 0 --w=(w - boundary_) --h=h --color=0xff
else:
lo_alpha.rectangle 0 0 --w=w --h=(h - boundary_) --color=0xff
Background.draw background_lo_ lo 0 0 w h --autocropped
if background_hi_:
if horizontal_:
hi_alpha.rectangle (w - boundary_) 0 --w=boundary_ --h=h --color=0xff
else:
hi_alpha.rectangle 0 (h - boundary_) --w=w --h=boundary_ --color=0xff
Background.draw background_hi_ hi 0 0 w h --autocropped

canvas.composit hi_alpha hi lo_alpha lo

set_attribute key/string value -> none:
if key == "value":
value_ = value
recalculate_
else if key == "min":
min_ = value
recalculate_
else if key == "max":
max_ = value
recalculate_
else if key == "background-lo":
background_lo_ = value
invalidate
else if key == "background-hi":
background_hi_ = value
invalidate
else if key == "horizontal":
invalidate
horizontal_ = value
recalculate_
invalidate
else:
super key value

value= value/num -> none:
if value != value_:
value_ = value
recalculate_
Binary file added tests/gold/slider_visualized.toit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 74 additions & 0 deletions tests/slider_visualized.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (C) 2023 Toitware ApS.
// Use of this source code is governed by a Zero-Clause BSD license that can
// be found in the TESTS_LICENSE file.
// Tests some simple vertical sliders where there is a movable boundary between
// two different backgrounds.
import bitmap show *
import expect show *
import font show *
import pixel_display show *
import pixel_display.element show *
import pixel_display.gradient show *
import pixel_display.slider show *
import pixel_display.style show *
import .png_visualizer

main args:
if args.size != 1:
print "Usage: script.toit png-basename"
exit 1
WIDTH ::= 220
HEIGHT ::= 140
driver := TrueColorPngVisualizer WIDTH HEIGHT args[0] --outline=0x4040ff
display := TrueColorPixelDisplay driver
display.background = 0x808080

heat := GradientBackground --angle=0 --specifiers=[
GradientSpecifier --color=0xc0c000 0,
GradientSpecifier --color=0xff8000 100,
]

cold := GradientBackground --angle=90 --specifiers=[
GradientSpecifier --color=0xa0a0a0 0,
GradientSpecifier --color=0x404040 10,
GradientSpecifier --color=0x404040 90,
GradientSpecifier --color=0xa0a0a0 100,
]

sliders := List 5:
Slider --x=(20 + 40 * it) --y=10 --value=(10 + it * 20)
labels := List 5:
Label --x=(30 + 40 * it) --y=125 --label="$(%c 'A' + it)" --alignment=ALIGN_CENTER

content := Div --x=0 --y=0 --w=WIDTH --h=HEIGHT --background=0x202020 (sliders + labels)

display.add content

sans10 := Font.get "sans10"

style := Style
--type-map={
"vertical-slider": Style {
"background-hi": heat,
"background-lo": cold,
"width": 20,
"height": 100,
},
"label": Style --font=sans10 --color=0xffffff,
}

content.set_styles [style]

display.draw
driver.write_png

sliders[0].value = 50
sliders[1].value = 70
sliders[2].value = 10
sliders[3].value = 90
sliders[4].value = 30

display.draw
driver.write_png

0 comments on commit 176d22a

Please sign in to comment.