319 lines
14 KiB
Racket
319 lines
14 KiB
Racket
#lang scribble/manual
|
|
@(require (for-syntax racket/base)
|
|
(for-label raart
|
|
lux/chaos
|
|
ansi
|
|
racket/format
|
|
racket/contract
|
|
racket/base)
|
|
racket/sandbox
|
|
scribble/example)
|
|
|
|
@(define ev
|
|
(parameterize ([sandbox-output 'string]
|
|
[sandbox-error-output 'string]
|
|
[sandbox-memory-limit 50]
|
|
;; Need to load the tty-raw extension and we don't
|
|
;; know where it is.
|
|
[sandbox-path-permissions '((execute #rx#""))])
|
|
(make-evaluator 'racket/base)))
|
|
@(ev '(require raart/draw))
|
|
@(define-syntax-rule (ex r ...) (examples #:eval ev r ...))
|
|
|
|
@title{raart: Racket ASCII Art and Interfaces}
|
|
@author{Jay McCarthy}
|
|
|
|
@defmodule[raart]
|
|
|
|
The @racketmodname[raart] module provides an algebraic model of ASCII
|
|
that can be used for art, user interfaces, and diagrams. It is
|
|
comparable to @racketmodname[2htdp/image].
|
|
|
|
Check out some examples in the
|
|
@link["https://github.com/jeapostrophe/raart/tree/master/t"]{test}
|
|
directory in the source.
|
|
|
|
@local-table-of-contents[]
|
|
|
|
@section{Buffers}
|
|
@defmodule[raart/buffer]
|
|
|
|
When drawing, @racketmodname[raart] renders to a @deftech{buffer}. In
|
|
almost all circumstances, you should use @racket[make-cached-buffer].
|
|
|
|
@defproc[(buffer? [x any/c]) boolean?]{Identifiers @tech{buffer}s.}
|
|
|
|
@defproc[(make-output-buffer [#:output output output-port? (current-output-port)])
|
|
buffer?]{
|
|
|
|
A @tech{buffer} that displays to @racket[output].
|
|
|
|
}
|
|
|
|
@defproc[(make-terminal-buffer [rows exact-nonnegative-integer?]
|
|
[cols exact-nonnegative-integer?]
|
|
[#:clear? clear? boolean? #t]
|
|
[#:output output output-port? (current-output-port)])
|
|
buffer?]{
|
|
|
|
A @tech{buffer} that displays to a terminal of @racket[rows] rows and
|
|
@racket[cols] columns via the port @racket[output]. If @racket[clear?]
|
|
is non-false, then the terminal will be cleared before display.
|
|
|
|
}
|
|
|
|
@defproc[(make-cached-buffer [rows exact-nonnegative-integer?]
|
|
[cols exact-nonnegative-integer?]
|
|
[#:output output output-port? (current-output-port)])
|
|
buffer?]{
|
|
|
|
A @tech{buffer} that displays to a terminal of @racket[rows] rows and
|
|
@racket[cols] columns via the port @racket[output], with minimal
|
|
output to the terminal implemented via client-side caching of the
|
|
screen content so only updates are output. }
|
|
|
|
@defthing[color/c contract?]{
|
|
|
|
A contract that recognizes the ASCII colors from the list
|
|
@racket['(black red green yellow blue magenta cyan white brblack brred
|
|
brgreen bryellow brblue brmagenta brcyan brwhite)]. The actual color
|
|
display depends on the terminal configuration. }
|
|
|
|
@defthing[style/c contract?]{
|
|
|
|
A contract that recognizes the ASCII styles from the list
|
|
@racket['(normal bold inverse underline)]. The actual font displayed
|
|
may depend on the terminal configuration. }
|
|
|
|
@section{Drawing}
|
|
@defmodule[raart/draw]
|
|
|
|
@racketmodname[raart] represents ASCII art algebraically as an
|
|
abstract @racket[raart?] object.
|
|
|
|
@defproc[(raart? [x any/c]) boolean?]{Identifies ASCII art.}
|
|
|
|
@defproc[(raart-w [x raart?]) exact-nonnegative-integer?]{Returns the
|
|
width of the art.}
|
|
|
|
@defproc[(raart-h [x raart?]) exact-nonnegative-integer?]{Returns the
|
|
height of the art.}
|
|
|
|
@defproc[(draw [b buffer?] [r raart?]) void?]{Displays @racket[r] to
|
|
the @racket[b] buffer.}
|
|
|
|
@defproc[(draw-here [r raart?]) void?]{Displays @racket[r] with a
|
|
freshly created buffer made with @racket[make-output-buffer].}
|
|
|
|
@defproc[(style [s style/c] [r raart?]) raart?]{@racket[r], except
|
|
with the style given by @racket[s].}
|
|
|
|
@defproc[(fg [c color/c] [r raart?]) raart?]{@racket[r], except with
|
|
the foreground color given by @racket[c].}
|
|
|
|
@defproc[(bg [c color/c] [r raart?]) raart?]{@racket[r], except with
|
|
the background color given by @racket[c].}
|
|
|
|
@defproc[(with-drawing [s (or/c style/c #f)] [fc (or/c color/c #f)]
|
|
[bc (or/c color/c #f)] [r raart?]) raart?]{Wraps @racket[r] in calls
|
|
to @racket[style], @racket[fg], and @racket[bg] if @racket[s],
|
|
@racket[fc], or @racket[bc] (respectively) are provided as non-false.}
|
|
|
|
@defproc[(blank [w exact-nonnegative-integer? 0] [h
|
|
exact-nonnegative-integer? 0]) raart?]{A blank art of width @racket[w]
|
|
and height @racket[h].
|
|
|
|
@ex[(draw-here (blank 2 2))
|
|
(draw-here (blank 5 5))]
|
|
|
|
}
|
|
|
|
@defproc[(char [c (and/c char? (not/c char-iso-control?))]) raart?]{An
|
|
art displaying @racket[c].
|
|
|
|
@ex[(draw-here (char #\a)) (draw-here (char #\b))]}
|
|
|
|
@defproc[(text [s string?]) raart?]{An art displaying @racket[s],
|
|
which must not contain any @racket[char-iso-control?] characters.
|
|
|
|
@ex[(draw-here (text "Hello World!"))]}
|
|
|
|
@defproc[(hline [w exact-nonnegative-integer?]) raart?]{A horizontal
|
|
line of @litchar{-} characters of width @racket[w].
|
|
|
|
@ex[(draw-here (hline 5))]}
|
|
|
|
@defproc[(vline [h exact-nonnegative-integer?]) raart?]{A vertical
|
|
line of @litchar{|} characters of height @racket[h].
|
|
|
|
@ex[(draw-here (vline 3))]}
|
|
|
|
@defthing[halign/c contract?]{A contract for the horizontal alignment modes @racket['(left center right)]. @racket['left] means that the art will be extended with blanks to the right@";" @racket['center] places the blanks equally on both sides@";" and @racket['right] places the blanks to the left.}
|
|
|
|
@defthing[valign/c contract?]{A contract for the vertical alignment modes @racket['(top center bottom)]. @racket['top] means that the art will be extended with blanks below";" @racket['center] places the blanks equally on both sides@";" and @racket['bottom] places the blanks above.}
|
|
|
|
@defproc[(vappend2 [y raart?] [x raart?] [#:halign halign (or/c
|
|
halign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{ Renders
|
|
@racket[y] vertically above @racket[x]. (If @racket[reverse?] is true,
|
|
then the effects are evaluated in the opposite order.) Uses
|
|
@racket[halign] to determine the horizontal alignment. If
|
|
@racket[halign] is @racket[#f], then the arts must have the same
|
|
width.
|
|
|
|
@ex[(draw-here (vappend2 (text "Hello") (text "World")))
|
|
(eval:error
|
|
(draw-here (vappend2 (text "Short") (text "Very Very Long"))))
|
|
(draw-here (vappend2 (text "Short") (text "Very Very Long") #:halign 'left))
|
|
(draw-here (vappend2 (text "Short") (text "Very Very Long") #:halign 'right))
|
|
(draw-here (vappend2 (text "Short") (text "Very Very Long") #:halign 'center))]}
|
|
|
|
@defproc[(vappend [y raart?] [x raart?] ... [#:halign halign (or/c
|
|
halign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{Like
|
|
@racket[vappend2], but for many arguments.
|
|
|
|
@ex[(draw-here (vappend (text "Short") (text "A Little Medium") (text "Very Very Long") #:halign 'right))]}
|
|
|
|
@defproc[(vappend* [y-and-xs (non-empty-listof raart?)] [#:halign
|
|
halign (or/c halign/c #f) #f] [#:reverse? reverse? boolean? #f])
|
|
raart?]{Like @racket[vappend], but accepts arguments as a list.
|
|
|
|
@ex[(draw-here (vappend* (list (text "Short") (text "A Little Medium") (text "Very Very Long")) #:halign 'right))]}
|
|
|
|
@defproc[(happend2 [y raart?] [x raart?] [#:valign valign (or/c
|
|
valign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{ Renders
|
|
@racket[y] horizontally to the left of @racket[x]. (If
|
|
@racket[reverse?] is true, then the effects are evaluated in the
|
|
opposite order.) Uses @racket[valign] to determine the vertical
|
|
alignment. If @racket[valign] is @racket[#f], then the arts must have
|
|
the same height.
|
|
|
|
@ex[(draw-here (happend2 (vline 2) (vline 2)))
|
|
(eval:error
|
|
(draw-here (happend2 (vline 2) (vline 4))))
|
|
(draw-here (happend2 (vline 2) (vline 4) #:valign 'top))
|
|
(draw-here (happend2 (vline 2) (vline 4) #:valign 'center))
|
|
(draw-here (happend2 (vline 2) (vline 4) #:valign 'bottom))]}
|
|
|
|
@defproc[(happend [y raart?] [x raart?] ... [#:valign valign (or/c
|
|
valign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{Like
|
|
@racket[happend2], but for many arguments.
|
|
|
|
@ex[(draw-here (happend (vline 2) (vline 3) (vline 4) #:valign 'top))]}
|
|
|
|
@defproc[(happend* [y-and-xs (non-empty-listof raart?)] [#:valign
|
|
valign (or/c valign/c #f) #f] [#:reverse? reverse? boolean? #f])
|
|
raart?]{Like @racket[happend], but accepts arguments as a list.
|
|
|
|
@ex[(draw-here (happend* (list (vline 2) (vline 3) (vline 4)) #:valign 'top))]}
|
|
|
|
@defproc[(para [max-width exact-nonnegative-integer?] [s string?] [#:halign halign halign/c 'left]) raart?]{An art displaying @racket[s], that is at most @racket[max-width] wide, taking multiple lines if necessary.
|
|
|
|
@ex[(draw-here (para 45 "And it came to pass that I, Nephi, said unto my father: I will go and do the things which the Lord hath commanded, for I know that the Lord giveth no commandments unto the children of men, save he shall prepare a way for them that they may accomplish the thing which he commandeth them."))]}
|
|
|
|
@defproc[(para* [max-width exact-nonnegative-integer?] [rs (listof raart?)] [#:halign halign halign/c 'left] [#:gap gap raart? (blank)]) raart?]{Like @racket[happend*], but limits the total width and uses @racket[vappend] when things get too long. @racket[para] uses this after splitting the input string into words and supplies @racket[(text " ")] as the @racket[gap].}
|
|
|
|
@defproc[(place-at [back raart?] [dr exact-nonnegative-integer?] [dh
|
|
exact-nonnegative-integer?] [front raart?]) raart?]{Renders
|
|
@racket[front] on top of @racket[back] offset by @racket[dr] rows and
|
|
@racket[dh] columns.}
|
|
|
|
@defform[(place-at* back [dr dc fore] ...) #:contracts ([back raart?]
|
|
[dr exact-nonnegative-integer?] [dc exact-nonnegative-integer?] [fore
|
|
raart?])]{Calls @racket[place-at] on a sequence of art objects from
|
|
back on the left to front on the right.}
|
|
|
|
@defproc[(frame [#:style s (or/c style/c #f) #f] [#:fg fc (or/c
|
|
color/c #f)] [#:bg bc (or/c color/c #f)] [x raart?]) raart?]{Renders
|
|
@racket[x] with a frame where the frame character's style is
|
|
controlled by @racket[s], @racket[fc], and @racket[bc].}
|
|
|
|
@defproc[(matte-at [mw exact-nonnegative-integer?] [mh
|
|
exact-nonnegative-integer?] [c exact-nonnegative-integer?] [r
|
|
exact-nonnegative-integer?] [x raart?]) raart?]{Mattes @racket[x]
|
|
inside a blank of size @racket[mw] columns and @racket[mh] rows at row
|
|
@racket[r] and column @racket[c].}
|
|
|
|
@defproc[(translate [dr exact-nonnegative-integer?] [dc
|
|
exact-nonnegative-integer?] [x raart?]) raart?]{Translates @racket[x]
|
|
by @racket[dr] rows and @racket[dc] columns.}
|
|
|
|
@defproc[(matte [w exact-nonnegative-integer?] [h
|
|
exact-nonnegative-integer?] [#:halign halign halign/c 'center]
|
|
[#:valign valign valign/c 'center] [x raart?]) raart?]{Mattes
|
|
@racket[x] inside a blank of size @racket[w]x@racket[h] with the given
|
|
alignment.}
|
|
|
|
@defproc[(inset [dw exact-nonnegative-integer?] [dh
|
|
exact-nonnegative-integer?] [x raart?]) raart?]{Insets @racket[x] with
|
|
@racket[dw] columns and @racket[dh] rows of blanks.}
|
|
|
|
@defproc[(mask [mc exact-nonnegative-integer?] [mw
|
|
exact-nonnegative-integer?] [mr exact-nonnegative-integer?] [mh
|
|
exact-nonnegative-integer?] [x raart?]) raart?]{Renders the portion of
|
|
@racket[x] inside the rectangle (@racket[mc],@racket[mr])
|
|
to (@racket[(+ mc mw)],@racket[(+ mr mh)]).}
|
|
|
|
@defproc[(crop [cc exact-nonnegative-integer?] [cw
|
|
exact-nonnegative-integer?] [cr exact-nonnegative-integer?] [ch
|
|
exact-nonnegative-integer?] [x raart?]) raart?]{Renders the portion of
|
|
@racket[x] inside the rectangle (@racket[cc],@racket[cr])
|
|
to (@racket[(+ cc cw)],@racket[(+ cr ch)]) and removes the surrounding
|
|
blanks.}
|
|
|
|
@defproc[(table [cells (listof (listof raart?))] [#:frames? frames?
|
|
boolean? #t] [#:style s (or/c style/c #f) #f] [#:fg f (or/c color/c
|
|
#f) #f] [#:bg b (or/c color/c #f) #f] [#:inset-dw dw
|
|
exact-nonnegative-integer? 0] [#:inset-dh dh
|
|
exact-nonnegative-integer? 0] [#:valign row-valign valign/c 'top]
|
|
[#:halign halign (or/c halign/c (list*of halign/c (or/c halign/c
|
|
'()))) 'left]) raart?]{Renders a table of cells where frames are added
|
|
if @racket[frames?] is non-false with style and color given by the
|
|
arguments. Cells are inset by @racket[inset-dh] rows and
|
|
@racket[inset-dw] columns. Cells are horizontally aligned with
|
|
@racket[halign]. Rows are vertically aligned with
|
|
@racket[row-valign].}
|
|
|
|
@defproc[(text-rows [cells (listof (listof any/c))]) (listof (listof
|
|
raart?))]{Transforms a matrix of content into a matrix of art objects,
|
|
using @racket[~a] composed with @racket[text] if they are not already
|
|
art objects.}
|
|
|
|
@defproc[(if-drawn [f (-> exact-nonnegative-integer?
|
|
exact-nonnegative-integer? exact-nonnegative-integer?
|
|
exact-nonnegative-integer? any)] [x raart?]) raart?]{Renders
|
|
@racket[x] and if it ends up being displayed, then calls @racket[f]
|
|
with the actual bounding box, given as a row, column, width, and
|
|
height.}
|
|
|
|
@defproc[(place-cursor-after [x raart?] [cr
|
|
exact-nonnegative-integer?] [ch exact-nonnegative-integer?])
|
|
raart?]{Renders @racket[x] but places the cursor at row @racket[cr]
|
|
and column @racket[ch] afterwards.}
|
|
|
|
@defproc[(without-cursor [x raart?]) raart?]{Renders @racket[x], but
|
|
signals to @racket[draw] to not display the cursor, if this is the art
|
|
object given to it. (That is, this has no effect if composed with
|
|
other drawing operations.)}
|
|
|
|
@section{lux integration}
|
|
@defmodule[raart/lux-chaos]
|
|
|
|
@racketmodname[raart] provides integration with @racketmodname[lux]
|
|
via the @racketmodname[ansi] module.
|
|
|
|
@defproc[(make-raart [#:mouse? mouse? boolean? #f]) chaos?]{
|
|
|
|
Returns a @tech[#:doc '(lib "lux/scribblings/lux.scrbl")]{chaos} that
|
|
manages the terminal.
|
|
|
|
The values that @racket[word-event] is called with are characters or
|
|
@racket[screen-size-report] structures. If @racket[mouse?] is
|
|
non-false, then @racket[any-mouse-event], @racket[mouse-focus-event],
|
|
or @racket[mouse-event] structures may also be provided.
|
|
|
|
The values that @racket[word-output] should return are @racket[raart?]
|
|
objects. The drawing will use @racket[make-cached-buffer] to optimize
|
|
the display process.
|
|
|
|
}
|