(module name-message mzscheme (require (lib "string-constant.ss" "string-constants") (lib "class.ss") (lib "file.ss") (lib "list.ss") (lib "mred.ss" "mred") (lib "contract.ss")) ;; min-w, min-h : number -> contract ;; determines if the widths and heights are suitable (define (min-w h) (flat-named-contract "draw-button-label-width" (lambda (w) (w . > . (- h (* 2 border-inset)))))) (define (min-h w) (flat-named-contract "draw-button-label-height" (lambda (h) (h . > . (* 2 border-inset))))) (provide/contract (pad-xywh (number? number? (>=/c 0) (>=/c 0) . -> . (values number? number? (>=/c 0) (>=/c 0)))) (draw-button-label (->r ([dc (is-a?/c dc<%>)] [label (union false/c string?)] [x number?] [y number?] [w (and/c number? (min-w h))] [h (and/c number? (min-h w))] [mouse-over? boolean?] [grabbed? boolean?]) void?)) (calc-button-min-sizes (->* ((is-a?/c dc<%>) string?) (number? number?)))) (provide name-message%) (define name-message% (class canvas% (inherit popup-menu get-dc get-size get-client-size min-width min-height stretchable-width stretchable-height get-top-level-window refresh) (define paths #f) ;; label : string (init-field [label (string-constant untitled)]) (define full-name-window #f) (define mouse-grabbed? #f) (define mouse-over? #f) (define/public (on-choose-directory dir) (void)) ;; set-message : boolean (union #f path string) -> void ;; if file-name? is #t, path-name should be a path (or #f) ;; if file-name? is #f, path-name should be a string (or #f) (define/public (set-message file-name? path-name) (set! paths (if (and file-name? path-name (file-exists? path-name)) (map path->string (explode-path (normalize-path path-name))) #f)) (let ([new-label (cond [(and paths (not (null? paths))) (car (last-pair paths))] [path-name path-name] [else (string-constant untitled)])]) (unless (equal? label new-label) (set! label new-label) (update-min-sizes) (refresh)))) (define/public (fill-popup menu reset) (if (and paths (not (null? paths))) (let loop ([paths (cdr (reverse paths))]) (cond [(null? paths) (void)] [else (make-object menu-item% (car paths) menu (lambda (evt item) (reset) (on-choose-directory (apply build-path (reverse paths))))) (loop (cdr paths))])) (let ([i (make-object menu-item% (string-constant no-full-name-since-not-saved) menu void)]) (send i enable #f)))) (define/override (on-event evt) (let-values ([(max-x max-y) (get-size)]) (let ([inside? (and (not (send evt leaving?)) (<= 0 (send evt get-x) max-x) (<= 0 (send evt get-y) max-y))]) (unless (eq? inside? mouse-over?) (set! mouse-over? inside?) (refresh)))) (cond [(send evt button-down?) (let-values ([(width height) (get-size)] [(reset) (lambda () (set! mouse-grabbed? #f) (set! mouse-over? #f) (refresh))]) (set! mouse-over? #t) (set! mouse-grabbed? #t) (let ([menu (make-object popup-menu% #f (lambda x (reset)))]) (fill-popup menu reset) ;; Refresh the screen (wait for repaint) (set! paint-sema (make-semaphore)) (refresh) (yield paint-sema) (set! paint-sema #f) ;; Popup menu (popup-menu menu 0 height)))])) (define paint-sema #f) (inherit get-parent) (define/private (update-min-sizes) (let-values ([(w h) (calc-button-min-sizes (get-dc) label)]) (min-width w) (min-height h) (send (get-parent) reflow-container))) (define/override (on-paint) (when paint-sema (semaphore-post paint-sema)) (let ([dc (get-dc)]) (let-values ([(w h) (get-client-size)]) (when (and (> w 5) (> h 5)) (draw-button-label dc label 0 0 w h mouse-over? mouse-grabbed?))))) (super-new [style '(transparent)]) (update-min-sizes) (stretchable-width #f) (stretchable-height #f))) (define button-label-font small-control-font) (define button-label-inset 1) (define black-color (make-object color% "BLACK")) (define triangle-width 10) (define triangle-height 14) (define triangle-color (make-object color% 50 50 50)) (define border-inset 1) (define triangle-space 0) (define circle-spacer 4) (define rrect-spacer 3) (define (offset-color color offset-one) (make-object color% (offset-one (send color red)) (offset-one (send color green)) (offset-one (send color blue)))) (define mouse-over-color (case (system-type) [(macosx) "darkgray"] [else (make-object color% 230 230 230)])) (define mouse-grabbed-color (make-object color% 100 100 100)) (define grabbed-fg-color (make-object color% 220 220 220)) (define (calc-button-min-sizes dc label) (let-values ([(w h a d) (send dc get-text-extent label button-label-font)]) (let-values ([(px py pw ph) (pad-xywh 0 0 w h)]) (values pw ph)))) (define (pad-xywh tx ty tw th) (let* ([ans-h (+ button-label-inset (max 0 (+ 2 (inexact->exact (ceiling th))) (+ 2 triangle-height)) button-label-inset)] [ans-w (max (+ ans-h ans-h) (+ circle-spacer button-label-inset 1 ;; becuase "(define ...)" has the wrong size under windows (max 0 (inexact->exact (ceiling tw))) triangle-space triangle-width circle-spacer button-label-inset))]) (values (- tx (quotient (- ans-w tw) 2)) (- ty (quotient (- ans-h th) 2)) ans-w ans-h))) (define (draw-button-label dc label dx dy w h mouse-over? grabbed?) (when (or mouse-over? grabbed?) (let ([color (if grabbed? mouse-grabbed-color mouse-over-color)] [xh (- h (* 2 border-inset))]) (case (system-type) [(macosx) (send dc set-pen (send the-pen-list find-or-create-pen color 1 'solid)) (send dc set-brush (send the-brush-list find-or-create-brush color 'solid)) (let ([old-smooth (send dc get-smoothing)]) (send dc set-smoothing 'aligned) (send dc draw-ellipse (+ dx border-inset) (+ dy border-inset) xh xh) (send dc draw-ellipse (+ dx (- w xh)) (+ dy border-inset) xh xh) (send dc set-smoothing old-smooth)) (send dc set-pen (send the-pen-list find-or-create-pen "black" 1 'transparent)) (send dc draw-rectangle (+ dx (quotient xh 2)) (+ dy border-inset) (- w xh) xh) (send dc set-pen (send the-pen-list find-or-create-pen color 1 'solid)) (send dc draw-line (+ dx (quotient xh 2)) (+ dy border-inset) (+ dx (- w (quotient xh 2))) (+ dy border-inset)) (send dc draw-line (+ dx (quotient xh 2)) (+ dy (- h 1 border-inset)) (+ dx (- w (quotient xh 2))) (+ dy (- h 1 border-inset)))] [else (send dc set-pen (send the-pen-list find-or-create-pen triangle-color 1 'solid)) (send dc set-brush (send the-brush-list find-or-create-brush color 'solid)) (send dc draw-rounded-rectangle (+ dx rrect-spacer) (+ dy border-inset) (- w border-inset rrect-spacer) xh 2)]))) (when label (send dc set-text-foreground (if grabbed? grabbed-fg-color black-color)) (send dc set-font button-label-font) (let-values ([(tw th _1 _2) (send dc get-text-extent label)]) (send dc draw-text label (+ dx (+ circle-spacer button-label-inset)) (+ dy (- (/ h 2) (/ th 2))) #t))) (send dc set-pen (send the-pen-list find-or-create-pen (if grabbed? grabbed-fg-color triangle-color) 1 'solid)) (let ([x (- w triangle-width circle-spacer)] [y (- (/ h 2) (/ triangle-height 2))]) (let loop ([x-off 0][off-y 5]) (unless (= 5 x-off) (send dc draw-line (+ dx (+ x 1 x-off)) (+ dy (+ y off-y)) (+ dx (+ x (- triangle-width 1 x-off))) (+ dy (+ y off-y))) (loop (+ x-off 1) (+ off-y 1))))) (void)))