#lang racket/base (require racket/class racket/draw racket/draw/private/xp ffi/unsafe "../../syntax.rkt" "../../lock.rkt" "../common/event.rkt" "item.rkt" "utils.rkt" "const.rkt" "window.rkt" "wndclass.rkt" "hbitmap.rkt" "types.rkt") (provide (protect-out base-button% button%)) (define BM_SETSTYLE #x00F4) (define base-button% (class item% (inherit set-control-font auto-size get-hwnd remember-label-bitmap set-size) (init parent cb label x y w h style font) (define callback cb) (define bitmap? (or (label . is-a? . bitmap%) (pair? label))) (define orientation (and (pair? label) (caddr label))) (define/public (get-class) "PLTBUTTON") (define/public (get-flags) BS_PUSHBUTTON) (super-new [callback cb] [parent parent] [hwnd (CreateWindowExW/control 0 (get-class) (cond [(string? label) label] [(pair? label) (cadr label)] [else ""]) (bitwise-ior (get-flags) WS_CHILD WS_CLIPSIBLINGS (if bitmap? (case (and (not xp?) orientation) [(#f) BS_BITMAP] [(left) BS_LEFT] [(right) BS_RIGHT] [(top) BS_TOP] [(bottom) BS_BOTTOM]) BS_MULTILINE)) 0 0 0 0 (send parent get-content-hwnd) #f hInstance #f)] [style style]) (when bitmap? (let ([hbitmap (bitmap->hbitmap (if (pair? label) (if xp? (collapse-to-bitmap label font) (car label)) label) #:bg (get-button-background))]) (remember-label-bitmap hbitmap) (SendMessageW (get-hwnd) BM_SETIMAGE IMAGE_BITMAP (cast hbitmap _HBITMAP _LPARAM)))) (define/private (collapse-to-bitmap label font) ;; XP doesn't handle a combination of string ;; and bitmap labels (let-values ([(w h) (auto-size-button font label #:resize (lambda (w h) (values w h)))]) (let* ([bm (make-object bitmap% w h #f #f)] [dc (make-object bitmap-dc% bm)] [h? (memq (caddr label) '(left right))]) (send dc draw-bitmap (car label) (if h? (if (eq? (caddr label) 'left) 3 (- w (send (car label) get-width) 3)) (quotient (- w (send (car label) get-width)) 2)) (if h? (quotient (- h (send (car label) get-height)) 2) (if (eq? (caddr label) 'top) 3 (- h (send (car label) get-height) 3)))) (send dc set-font (or font (get-default-control-font))) (let-values ([(tw th ta td) (send dc get-text-extent (cadr label))]) (send dc draw-text (cadr label) (if h? (if (eq? (caddr label) 'left) (- w tw 3) 3) (quotient (- w tw) 2)) (if h? (quotient (- h th) 2) (if (eq? (caddr label) 'top) (- h th 3) 3)))) (send dc set-bitmap #f) bm))) (set-control-font font) (define/public (get-button-background) #xFFFFFF) (define/public (auto-size-button font label #:resize [resize (lambda (w h) (set-size #f #f w h))]) (cond [orientation (let ([h? (memq orientation '(left right))]) (auto-size font (list (car label) (cadr label)) 0 0 12 8 resize #:combine-width (if h? + max) #:combine-height (if h? max +)))] [bitmap? (auto-size font label 0 0 4 4)] [else (auto-size font label 60 20 12 0 #:scale-w 1.1 #:scale-h 1.1)])) (auto-size-button font label) (define/override (size->screen v) (->screen* v)) ;; XP doesn't show both bitmap and string labels, ;; so we synthesize a bitmap label when we have both (define xp-label-bitmap (and xp? orientation (car label))) (define xp-label-string (and xp? orientation (string->immutable-string (cadr label)))) (define xp-label-font (and xp? orientation font)) (define/override (set-label s) (if (and orientation xp?) (atomically (begin (if (string? s) (set! xp-label-string s) (set! xp-label-bitmap s)) (super set-label (collapse-to-bitmap (list xp-label-bitmap xp-label-string orientation) xp-label-font)))) (super set-label s))) ;; Avoid passing any key event to a button or checkbox. The ;; `pre-on-char` of the frame will take care of changing space to ;; a control action, but the control itself may use WM_KEYDOWN ;; instead of WM_CHAR. (define/override (capture-all-key-events?) #t) (define/override (is-command? cmd) (= cmd BN_CLICKED)) (define/override (do-command cmd control-hwnd) (queue-window-event this (lambda () (callback this (new control-event% [event-type 'button] [time-stamp (current-milliseconds)]))))) (define/public (set-border on?) (SendMessageW (get-hwnd) BM_SETSTYLE (if on? BS_DEFPUSHBUTTON BS_PUSHBUTTON) 1)))) (define button% (class base-button% (super-new)))