|
|
#lang debug racket/base
|
|
|
(require (for-syntax racket/base racket/syntax)
|
|
|
racket/struct
|
|
|
racket/format
|
|
|
racket/list
|
|
|
racket/string
|
|
|
racket/promise
|
|
|
racket/dict
|
|
|
racket/match
|
|
|
"param.rkt"
|
|
|
"rebase.rkt")
|
|
|
(provide (all-defined-out))
|
|
|
|
|
|
(module+ test (require rackunit))
|
|
|
|
|
|
(define-syntax (define-quad-attribute stx)
|
|
|
(syntax-case stx ()
|
|
|
[(_ ATTR)
|
|
|
(with-syntax ([QUAD-ATTR (format-id #'ATTR "quad-~a" #'ATTR)]
|
|
|
[QUAD-ATTR-SET! (format-id #'ATTR "quad-~a-set!" #'ATTR)])
|
|
|
#'(begin
|
|
|
(define (QUAD-ATTR q) (hash-ref q 'ATTR #false))
|
|
|
(define (QUAD-ATTR-SET! q val) (hash-set! q 'ATTR val) q)))]
|
|
|
[(_ ATTR ...)
|
|
|
#'(begin
|
|
|
(define-quad-attribute ATTR) ...)]))
|
|
|
|
|
|
(define-quad-attribute size)
|
|
|
|
|
|
(define (size q)
|
|
|
(match (quad-size q)
|
|
|
[(? procedure? proc) proc (proc q)]
|
|
|
[(? promise? prom) (force prom)]
|
|
|
[val val]))
|
|
|
|
|
|
(define-quad-attribute printable)
|
|
|
|
|
|
(define (printable? q [signal #f])
|
|
|
(match (quad-printable q)
|
|
|
[(? procedure? proc) (proc q signal)]
|
|
|
[val val]))
|
|
|
|
|
|
(define-quad-attribute draw-start draw draw-end)
|
|
|
|
|
|
(define (draw q [surface (current-output-port)])
|
|
|
((or (quad-draw-start q) void) q surface)
|
|
|
((or (quad-draw q) void) q surface)
|
|
|
((or (quad-draw-end q) void) q surface))
|
|
|
|
|
|
(define-quad-attribute elems shift from-parent from to shift-elems origin)
|
|
|
|
|
|
(define quad=? equal?)
|
|
|
|
|
|
;; keep this param here so you don't have to import quad/param to get it
|
|
|
(define verbose-quad-printing? (make-parameter #f))
|
|
|
|
|
|
(define quad? hash?)
|
|
|
|
|
|
#;(struct quad (
|
|
|
;; WARNING
|
|
|
;; atomize procedure depends on attrs & elems
|
|
|
;; being first two fields of struct.
|
|
|
attrs ; key-value pairs, arbitrary
|
|
|
elems ; subquads or text
|
|
|
;; size is a two-dim pt
|
|
|
size ; outer size of quad for layout (though not necessarily the bounding box for drawing)
|
|
|
;; from-parent, from, to are phrased in terms of cardinal position
|
|
|
from-parent ; alignment point on parent. if not #f, supersedes `from`
|
|
|
;; (this way, `from` doens't change, so a quad can "remember" its default `from` attachment point)
|
|
|
from ; alignment point on ref quad
|
|
|
to ; alignment point on this quad that is matched to `from` on previous quad
|
|
|
;; shift-elements, shift are two-dim pts
|
|
|
;; shift-elements = Similar to `relative` CSS positioning
|
|
|
;; moves origin for elements . Does NOT change layout position of parent.
|
|
|
shift-elems
|
|
|
;; shift = shift between previous out point and current in point.
|
|
|
;; DOES change the layout position.
|
|
|
shift
|
|
|
;; reference point (in absolute coordinates)
|
|
|
;; for all subsequent drawing ops in the quad. Calculated, not set directly
|
|
|
origin
|
|
|
printable ; whether the quad will print
|
|
|
draw-start ; func called at the beginning of every draw event (for setup ops)
|
|
|
draw ; func called in the middle of every daw event
|
|
|
draw-end ; func called at the end of every draw event (for teardown ops)
|
|
|
name ; for anchor resolution
|
|
|
tag) ; from q-expr, maybe
|
|
|
#:mutable
|
|
|
#:transparent
|
|
|
#:property prop:custom-write
|
|
|
(λ (q p w?) (display
|
|
|
(format "<~a-~a~a~a>"
|
|
|
(quad-tag q)
|
|
|
(object-name q)
|
|
|
(if (verbose-quad-printing?)
|
|
|
(string-join (map ~v (flatten (hash->list (quad-attrs q))))
|
|
|
" " #:before-first "(" #:after-last ")")
|
|
|
"")
|
|
|
(match (quad-elems q)
|
|
|
[(? pair?) (string-join (map ~v (quad-elems q)) " " #:before-first " ")]
|
|
|
[_ ""])) p))
|
|
|
#:methods gen:equal+hash
|
|
|
[(define equal-proc quad=?)
|
|
|
(define (hash-proc h recur) (equal-hash-code h))
|
|
|
(define (hash2-proc h recur) (equal-secondary-hash-code h))])
|
|
|
|
|
|
#;(struct quad-attr (key default-val) #:transparent)
|
|
|
|
|
|
#;(define (make-quad-attr key [default-val #f])
|
|
|
(quad-attr key default-val))
|
|
|
|
|
|
(define quad-ref hash-ref)
|
|
|
|
|
|
(define quad-set! hash-set!)
|
|
|
|
|
|
(define-syntax (quad-copy stx)
|
|
|
(syntax-case stx ()
|
|
|
[(_ QUAD-TYPE ID [K V] ...)
|
|
|
(if (free-identifier=? #'quad #'QUAD-TYPE)
|
|
|
#'(struct-copy QUAD-TYPE ID
|
|
|
[K V] ...)
|
|
|
#'(struct-copy QUAD-TYPE ID
|
|
|
[K #:parent quad V] ...))]))
|
|
|
|
|
|
#;(define-syntax (quad-update! stx)
|
|
|
(syntax-case stx ()
|
|
|
[(_ ID [K V] ...)
|
|
|
(with-syntax ([(K-SETTER ...) (for/list ([kstx (in-list (syntax->list #'(K ...)))])
|
|
|
(format-id kstx "set-quad-~a!" kstx))])
|
|
|
#'(let ([q ID])
|
|
|
(K-SETTER q V) ...
|
|
|
q))]))
|
|
|
|
|
|
(define (default-printable q [sig #f]) #t)
|
|
|
|
|
|
(define (default-draw q surface)
|
|
|
(for-each (λ (qi) (draw qi surface)) (quad-elems q)))
|
|
|
|
|
|
;; why 'nw and 'ne as defaults for in and out points:
|
|
|
;; if size is '(0 0), 'nw and 'ne are the same point,
|
|
|
;; and everything piles up at the origin
|
|
|
;; if size is otherwise, the items don't pile up (but rather lay out in a row)
|
|
|
|
|
|
#;(define (make-quad-constructor type)
|
|
|
(make-keyword-procedure (λ (kws kw-args . rest)
|
|
|
(keyword-apply make-quad #:type type kws kw-args rest))))
|
|
|
|
|
|
(define (derive-quad-constructor q)
|
|
|
(define-values (x-structure-type _) (struct-info q))
|
|
|
(struct-type-make-constructor x-structure-type))
|
|
|
|
|
|
|
|
|
(define q make-hash)
|
|
|
|
|
|
(define only-prints-in-middle (λ (q sig) (not (memq sig '(start end)))))
|
|
|
|
|
|
(define/match (from-parent qs [where #f])
|
|
|
;; doesn't change any positioning. doesn't depend on state. can happen anytime.
|
|
|
;; can be repeated without damage.
|
|
|
[((? null?) _) null]
|
|
|
[((cons q rest) where)
|
|
|
(quad-set! q [from-parent (or where (quad-from q))])
|
|
|
(cons q rest)])
|
|
|
|
|
|
(module+ test
|
|
|
(require racket/port)
|
|
|
(define q1 (q '((elems #\H #\e #\l #\o))))
|
|
|
(define q2 (q '((elems #\H #\e #\l #\o))))
|
|
|
(define q3 (q '((elems #\H #\e #\l))))
|
|
|
(check-true (equal? q1 q1))
|
|
|
(check-true (equal? q1 q2))
|
|
|
(check-false (equal? q1 q3))
|
|
|
(define q4 (quad-draw-set! (hash-copy q1) (λ (q surface) (display "foo" surface))))
|
|
|
(check-equal? (with-output-to-string (λ () (draw q4))) "foo")) |