#lang racket
(require racket/gui/dynamic)
(define frame% (if (gui-available?) (gui-dynamic-require 'frame%) #f))
(define slider% (if (gui-available?) (gui-dynamic-require 'slider%) #f))
(define the-font-list (if (gui-available?) (gui-dynamic-require 'the-font-list) #f))
(when (gui-available?) (dynamic-require 'racket/gui/base 0))
(require (only-in "backends.rkt" delete-all-shapes realize refresh))
(provide sliders)
(define (argument<-slider param slider)
(let ((s (send slider get-value)))
(if (or (empty? (cdr param)) (empty? (cddr param)) (empty? (cdddr param)))
s
(* s (cadddr param)))))
(define (label<-parameter param)
(car param))
(define (min-value<-parameter param)
(if (empty? (cdr param))
0
(cadr param)))
(define (max-value<-parameter param)
(if (or (empty? (cdr param)) (empty? (cddr param)))
100
(caddr param)))
(define (make-draw-fn fn params sliders-fn)
(lambda ()
(begin (begin
(delete-all-shapes)
(let ((shape
(with-handlers ((exn:fail:contract:divide-by-zero? (lambda (exn) #f)))
(apply fn (map argument<-slider params (sliders-fn))))))
(realize shape)
(refresh))))))
(define (make-sliders-frame label)
(new frame% [label label]))
(define (make-slider parent label min-value max-value callback)
(new slider%
[parent parent]
[style '(horizontal vertical-label)]
[font (send the-font-list
find-or-create-font
12 'script 'italic 'bold #f 'smoothed)]
[label label]
[min-value min-value]
[max-value max-value]
[callback callback]))
(define (make-slider-callback fn params)
(let ((prev #f))
(λ (slider event)
(let ((new (send slider get-value)))
(unless (eqv? new prev)
(set! prev new)
((fn)))))))
(define (make-sliders parent fn params)
(letrec ((draw-fn (make-draw-fn fn params (λ () sliders)))
(sliders
(map
(λ (param)
(make-slider parent
(label<-parameter param)
(min-value<-parameter param)
(max-value<-parameter param)
(make-slider-callback (λ () draw-fn) params)))
params)))
(draw-fn)
(send parent show #t)))
(define (sliders label fn params)
(make-sliders (make-sliders-frame label) fn params))