(module wxitem mzscheme (require mzlib/class mzlib/class100 mzlib/etc mzlib/file (prefix wx: "kernel.ss") "lock.ss" "helper.ss" "const.ss" "wx.ss" "check.ss" "wxwindow.ss") (provide (protect make-item% make-control% make-simple-control% wx-button% wx-check-box% wx-choice% wx-message% wx-gauge% wx-list-box% wx-radio-box% wx-slider%)) ;; make-item%: creates items which are suitable for placing into ;; containers. ;; input: item%: a wx:item% descendant (but see below) from which the ;; new class will be derived. ;; stretch-x/stretch-y: booleans which specify the default ;; stretchability behavior for the new class. ;; returns: a class, descended from wx:item%, which is suitable for ;; placing in a container. ;; Note: the item% parameter does not necessarily HAVE to be a ;; descendant of wx:item%, so long as it contains the identifiers in the ;; inherit section below. You will note below that I ran wx:panel% ;; through this function to create panel%. (define make-item% (lambda (item% x-margin-w y-margin-h stretch-x stretch-y) (class100 (wx-make-window% item% #f) (window-style . args) (inherit get-width get-height get-x get-y get-parent get-client-size get-top-level) (private-field [enabled? #t]) (override [enable (lambda (b) (set! enabled? (and b #t)) (super enable b))] ;; set-size: caches calls to set-size to avoid unnecessary work, ;; and works with windowsless panels ;; input: x/y: new position for object ;; width/height: new size for object ;; returns: nothing ;; effect: if arguments mark a different geometry than the object's ;; current geometry, passes args to super-class's set-size. ;; Otherwise, does nothing. [set-size (lambda (x y width height) (set! x (+ x (send (area-parent) dx))) (set! y (+ y (send (area-parent) dy))) (unless (and (same-dimension? x (get-x)) (same-dimension? y (get-y)) (same-dimension? width (get-width)) (same-dimension? height (get-height))) (super set-size x y width height)))]) (public [is-enabled? (lambda () enabled?)]) (private-field ;; Store minimum size of item. ;; This will never change after the item is created. hard-min-width hard-min-height) (public [set-min-height (lambda (v) (set! hard-min-height v) (min-height v))] [set-min-width (lambda (v) (set! hard-min-width v) (min-width v))] [get-hard-minimum-size (lambda () (values hard-min-width hard-min-height))] [client-inset (lambda (h?) (let ([h #f][w #f]) (unless h (let ([w-box (box 0)] [h-box (box 0)]) (get-client-size w-box h-box) (set! h (- (get-height) (unbox h-box))) (set! w (- (get-width) (unbox w-box))))) (if h? h w)))] ;; gets/sets user's requirement for minimum width. Errors out ;; if new value is not a non-negative real number. Forces a ;; redraw upon a set. [min-client-width (case-lambda [() (- (min-width) (client-inset #f))] [(new-width) (check-range-integer '(method canvas<%> min-client-width) new-width) (min-width (+ new-width (client-inset #f)))])] [min-client-height (case-lambda [() (- (min-height) (client-inset #t))] [(new-height) (check-range-integer '(method canvas<%> min-client-height) new-height) (min-height (+ new-height (client-inset #t)))])]) (private-field [-mw 0] [-mh 0] [-xm x-margin-w] [-ym y-margin-h] [-sx stretch-x] [-sy stretch-y] [first-arg (car args)]) (public [min-width (mk-param -mw identity (lambda (v) (check-range-integer '(method area<%> min-width) v)) force-redraw)] [min-height (mk-param -mh identity (lambda (v) (check-range-integer '(method area<%> min-height) v)) force-redraw)] [x-margin (mk-param -xm identity (lambda (v) (check-margin-integer '(method subarea<%> horiz-margin) v) v) force-redraw)] [y-margin (mk-param -ym identity (lambda (v) (check-margin-integer '(method subarea<%> vert-margin) v) v) force-redraw)] [stretchable-in-x (mk-param -sx (lambda (x) (and x #t)) void force-redraw)] [stretchable-in-y (mk-param -sy (lambda (x) (and x #t)) void force-redraw)] ;; get-info: passes necessary info up to parent. ;; input: none ;; returns: child-info struct containing the info about this ;; item. ;; intended to be called by item's parent upon resize. [get-info (lambda () (let* ([min-size (get-min-size)] [result (make-child-info (car min-size) (cadr min-size) (x-margin) (y-margin) (stretchable-in-x) (stretchable-in-y))]) result))] [area-parent (lambda () first-arg)] ;; force-redraw: unconditionally trigger redraw. ;; input: none ;; returns: nothing ;; effects: forces the item's parent (if it exists) to redraw ;; itself. This will recompute the min-size cache if it is ;; invalid. [force-redraw (lambda () (let ([parent (area-parent)]) (when parent (send parent child-redraw-request this))))] [on-container-resize (lambda () (void))] ; This object doesn't contain anything [init-min (lambda (x) x)] ;; get-min-size: computes the minimum size the item can ;; reasonably assume. ;; input: none ;; returns: a list containing the minimum width & height. [get-min-size (lambda () (let ([w (+ (* 2 (x-margin)) (max hard-min-width (min-width)))] [h (+ (* 2 (y-margin)) (max hard-min-height (min-height)))]) (list w h)))]) (sequence (apply super-init (send (car args) get-window) (cdr args)) (set-min-width (init-min (get-width))) (set-min-height (init-min (get-height))) (unless (memq 'deleted window-style) (send (get-top-level) show-control this #t) ;; For a pane[l], the creator must call the equivalent of the following, ;; delaying to let the panel's wx field get initialized before ;; panel-sizing methods are called (unless (is-a? this wx-basic-panel<%>) (send (area-parent) add-child this))))))) ;; make-control% - for non-panel items (define (make-control% item% x-margin y-margin stretch-x stretch-y) (class100 (make-item% item% x-margin y-margin stretch-x stretch-y) args (inherit get-parent) (sequence (apply super-init args) (send (get-parent) set-item-cursor 0 0)))) (define (make-simple-control% item%) (make-control% item% const-default-x-margin const-default-y-margin #f #f)) (define wx-button% (make-window-glue% (class100 (make-simple-control% wx:button%) (parent cb label x y w h style font) (inherit command set-border get-top-level) (private-field [border? (memq 'border style)] [border-on? border?]) (public [defaulting (lambda (on?) (set! border-on? on?) (set-border border-on?))] [has-border? (lambda () border-on?)]) (override [char-to (lambda () (as-exit (lambda () (command (make-object wx:control-event% 'button)))))]) (sequence (super-init style parent cb label x y w h (cons 'deleted style) font) (when border? (send (get-top-level) add-border-button this)))))) (define wx-check-box% (class100 (make-window-glue% (make-simple-control% wx:check-box%)) (mred proxy parent cb label x y w h style font) (inherit set-value get-value command) (override [char-to (lambda () (as-exit (lambda () (set-value (not (get-value))) (command (make-object wx:control-event% 'check-box)))))]) (sequence (super-init mred proxy style parent cb label x y w h (cons 'deleted style) font)))) (define wx-choice% (class100 (make-window-glue% (make-simple-control% wx:choice%)) (mred proxy parent cb label x y w h choices style font) (override [handles-key-code (lambda (x alpha? meta?) (or (memq x '(up down)) (and alpha? (not meta?))))]) (sequence (super-init mred proxy style parent cb label x y w h choices (cons 'deleted style) font)))) (define wx-message% (class100 (make-window-glue% (make-simple-control% wx:message%)) (mred proxy parent label x y style font) (override [gets-focus? (lambda () #f)]) (sequence (super-init mred proxy style parent label x y (cons 'deleted style) font)))) (define wx-gauge% (make-window-glue% (class100 (make-control% wx:gauge% const-default-x-margin const-default-y-margin #f #f) (parent label range style font) (inherit get-client-size get-width get-height set-size stretchable-in-x stretchable-in-y set-min-height set-min-width get-parent) (override [gets-focus? (lambda () #f)]) (private-field ;; # pixels per unit of value. [pixels-per-value 1]) (sequence (super-init style parent label range -1 -1 -1 -1 (cons 'deleted style) font) (let-values ([(client-width client-height) (get-two-int-values (lambda (a b) (get-client-size a b)))]) (let ([delta-w (- (get-width) client-width)] [delta-h (- (get-height) client-height)] [vertical-labels? (eq? (send (send (get-parent) get-window) get-label-position) 'vertical)] [horizontal? (memq 'horizontal style)]) (set-min-width (if horizontal? (let ([cw (min const-max-gauge-length (* range pixels-per-value))]) (max (if vertical-labels? cw (+ cw delta-w)) (get-width))) ;; client-height is the default ;; dimension in the minor direction. (+ client-width delta-w))) (set-min-height (if horizontal? (+ client-height delta-h) (let ([ch (min const-max-gauge-length (* range pixels-per-value))]) (max (if vertical-labels? (+ ch delta-h) ch) (get-height))))))) (if (memq 'horizontal style) (begin (stretchable-in-x #t) (stretchable-in-y #f)) (begin (stretchable-in-x #f) (stretchable-in-y #t))))))) (define list-box-wheel-step #f) (define wx-list-box% (make-window-glue% (class100 (make-control% wx:list-box% const-default-x-margin const-default-y-margin #t #t) (parent cb label kind x y w h choices style font label-font) (inherit get-first-item set-first-visible-item) (private [scroll (lambda (dir) (unless list-box-wheel-step (set! list-box-wheel-step (get-preference '|MrEd:wheelStep| (lambda () 3))) (unless (and (number? list-box-wheel-step) (exact? list-box-wheel-step) (integer? list-box-wheel-step) (<= 1 list-box-wheel-step 100)) (set! list-box-wheel-step 3))) (let ([top (get-first-item)]) (set-first-visible-item (max 0 (+ top (* list-box-wheel-step dir))))))]) (override [handles-key-code (lambda (x alpha? meta?) (case x [(up down) #t] [else (and alpha? (not meta?))]))] [pre-on-char (lambda (w e) (or (super pre-on-char w e) (case (send e get-key-code) [(wheel-up) (scroll -1) #t] [(wheel-down) (scroll 1) #t] [else #f])))]) (sequence (super-init style parent cb label kind x y w h choices (cons 'deleted style) font label-font))))) (define wx-radio-box% (make-window-glue% (class100 (make-simple-control% wx:radio-box%) (parent cb label x y w h choices major style font) (inherit number orig-enable set-selection command) (override [enable (case-lambda [(on?) (super enable on?)] [(which on?) (when (< -1 which (number)) (vector-set! enable-vector which (and on? #t)) (orig-enable which on?))])] [is-enabled? (case-lambda [() (super is-enabled?)] [(which) (and (< -1 which (number)) (vector-ref enable-vector which))])]) (private-field [is-vertical? (memq 'vertical style)]) (public [vertical? (lambda () is-vertical?)] [char-to-button (lambda (i) (as-exit (lambda () (set-selection i) (command (make-object wx:control-event% 'radio-box)))))]) (sequence (super-init style parent cb label x y w h choices major (cons 'deleted style) font)) (private-field [enable-vector (make-vector (number) #t)])))) (define wx-slider% (make-window-glue% (class100 (make-control% wx:slider% const-default-x-margin const-default-y-margin #f #f) (parent func label value min-val max-val style font) (inherit set-min-width set-min-height stretchable-in-x stretchable-in-y get-client-size get-width get-height get-parent) (private-field ;; # pixels per possible setting. [pixels-per-value 3]) ;; 3 is good because with horizontal sliders under Xt, with 1 or 2 ;; pixels per value, the thumb is too small to display the number, ;; which looks bad. (sequence (super-init style parent func label value min-val max-val -1 -1 -1 (cons 'deleted style) font) (let-values ([(client-w client-h) (get-two-int-values (lambda (a b) (get-client-size a b)))]) (let* ([horizontal? (memq 'horizontal style)] [vertical-labels? (eq? (send (send (get-parent) get-window) get-label-position) 'vertical)] [range (+ (* pixels-per-value (add1 (- max-val min-val))) (cond [(and horizontal? (not vertical-labels?)) (- (get-width) client-w)] [(and (not horizontal?) vertical-labels?) (- (get-height) client-h)] [else 0]))]) ((if horizontal? (lambda (v) (set-min-width v)) (lambda (v) (set-min-height v))) (max ((if horizontal? (lambda () (get-width)) (lambda () (get-height)))) (min const-max-gauge-length range))) (stretchable-in-x horizontal?) (stretchable-in-y (not horizontal?)))))))) )