fork of raart with fixes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
14 KiB

  1. #lang scribble/manual
  2. @(require (for-syntax racket/base)
  3. (for-label raart
  4. lux/chaos
  5. ansi
  6. racket/format
  7. racket/contract
  8. racket/base)
  9. racket/sandbox
  10. scribble/example)
  11. @(define ev
  12. (parameterize ([sandbox-output 'string]
  13. [sandbox-error-output 'string]
  14. [sandbox-memory-limit 50]
  15. ;; Need to load the tty-raw extension and we don't
  16. ;; know where it is.
  17. [sandbox-path-permissions '((execute #rx#""))])
  18. (make-evaluator 'racket/base)))
  19. @(ev '(require raart/draw))
  20. @(define-syntax-rule (ex r ...) (examples #:eval ev r ...))
  21. @title{raart: Racket ASCII Art and Interfaces}
  22. @author{Jay McCarthy}
  23. @defmodule[raart]
  24. The @racketmodname[raart] module provides an algebraic model of ASCII
  25. that can be used for art, user interfaces, and diagrams. It is
  26. comparable to @racketmodname[2htdp/image].
  27. Check out some examples in the
  28. @link["https://github.com/jeapostrophe/raart/tree/master/t"]{test}
  29. directory in the source.
  30. @local-table-of-contents[]
  31. @section{Buffers}
  32. @defmodule[raart/buffer]
  33. When drawing, @racketmodname[raart] renders to a @deftech{buffer}. In
  34. almost all circumstances, you should use @racket[make-cached-buffer].
  35. @defproc[(buffer? [x any/c]) boolean?]{Identifiers @tech{buffer}s.}
  36. @defproc[(make-output-buffer [#:output output output-port? (current-output-port)])
  37. buffer?]{
  38. A @tech{buffer} that displays to @racket[output].
  39. }
  40. @defproc[(make-terminal-buffer [rows exact-nonnegative-integer?]
  41. [cols exact-nonnegative-integer?]
  42. [#:clear? clear? boolean? #t]
  43. [#:output output output-port? (current-output-port)])
  44. buffer?]{
  45. A @tech{buffer} that displays to a terminal of @racket[rows] rows and
  46. @racket[cols] columns via the port @racket[output]. If @racket[clear?]
  47. is non-false, then the terminal will be cleared before display.
  48. }
  49. @defproc[(make-cached-buffer [rows exact-nonnegative-integer?]
  50. [cols exact-nonnegative-integer?]
  51. [#:output output output-port? (current-output-port)])
  52. buffer?]{
  53. A @tech{buffer} that displays to a terminal of @racket[rows] rows and
  54. @racket[cols] columns via the port @racket[output], with minimal
  55. output to the terminal implemented via client-side caching of the
  56. screen content so only updates are output. }
  57. @defthing[color/c contract?]{
  58. A contract that recognizes the ASCII colors from the list
  59. @racket['(black red green yellow blue magenta cyan white brblack brred
  60. brgreen bryellow brblue brmagenta brcyan brwhite)], as well as any
  61. @racket[byte?] value. The actual color display depends on the terminal
  62. configuration. }
  63. @defthing[style/c contract?]{
  64. A contract that recognizes the ASCII styles from the list
  65. @racket['(normal bold inverse underline)]. The actual font displayed
  66. may depend on the terminal configuration. }
  67. @section{Drawing}
  68. @defmodule[raart/draw]
  69. @racketmodname[raart] represents ASCII art algebraically as an
  70. abstract @racket[raart?] object.
  71. @defproc[(raart? [x any/c]) boolean?]{Identifies ASCII art.}
  72. @defproc[(raart-w [x raart?]) exact-nonnegative-integer?]{Returns the
  73. width of the art.}
  74. @defproc[(raart-h [x raart?]) exact-nonnegative-integer?]{Returns the
  75. height of the art.}
  76. @defproc[(draw [b buffer?] [r raart?]) void?]{Displays @racket[r] to
  77. the @racket[b] buffer.}
  78. @defproc[(draw-here [r raart?]) void?]{Displays @racket[r] with a
  79. freshly created buffer made with @racket[make-output-buffer].}
  80. @defproc[(style [s style/c] [r raart?]) raart?]{@racket[r], except
  81. with the style given by @racket[s].}
  82. @defproc[(fg [c color/c] [r raart?]) raart?]{@racket[r], except with
  83. the foreground color given by @racket[c].}
  84. @defproc[(bg [c color/c] [r raart?]) raart?]{@racket[r], except with
  85. the background color given by @racket[c].}
  86. @defproc[(with-drawing [s (or/c style/c #f)] [fc (or/c color/c #f)]
  87. [bc (or/c color/c #f)] [r raart?]) raart?]{Wraps @racket[r] in calls
  88. to @racket[style], @racket[fg], and @racket[bg] if @racket[s],
  89. @racket[fc], or @racket[bc] (respectively) are provided as non-false.}
  90. @defproc[(blank [w exact-nonnegative-integer? 0] [h
  91. exact-nonnegative-integer? 0]) raart?]{A blank art of width @racket[w]
  92. and height @racket[h].
  93. @ex[(draw-here (blank 2 2))
  94. (draw-here (blank 5 5))]
  95. }
  96. @defproc[(char [c (and/c char? (not/c char-iso-control?))]) raart?]{An
  97. art displaying @racket[c].
  98. @ex[(draw-here (char #\a)) (draw-here (char #\b))]}
  99. @defproc[(text [s string?]) raart?]{An art displaying @racket[s],
  100. which must not contain any @racket[char-iso-control?] characters.
  101. @ex[(draw-here (text "Hello World!"))]}
  102. @defproc[(hline [w exact-nonnegative-integer?]) raart?]{A horizontal
  103. line of @litchar{-} characters of width @racket[w].
  104. @ex[(draw-here (hline 5))]}
  105. @defproc[(vline [h exact-nonnegative-integer?]) raart?]{A vertical
  106. line of @litchar{|} characters of height @racket[h].
  107. @ex[(draw-here (vline 3))]}
  108. @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.}
  109. @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.}
  110. @defproc[(vappend2 [y raart?] [x raart?] [#:halign halign (or/c
  111. halign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{ Renders
  112. @racket[y] vertically above @racket[x]. (If @racket[reverse?] is true,
  113. then the effects are evaluated in the opposite order.) Uses
  114. @racket[halign] to determine the horizontal alignment. If
  115. @racket[halign] is @racket[#f], then the arts must have the same
  116. width.
  117. @ex[(draw-here (vappend2 (text "Hello") (text "World")))
  118. (eval:error
  119. (draw-here (vappend2 (text "Short") (text "Very Very Long"))))
  120. (draw-here (vappend2 (text "Short") (text "Very Very Long") #:halign 'left))
  121. (draw-here (vappend2 (text "Short") (text "Very Very Long") #:halign 'right))
  122. (draw-here (vappend2 (text "Short") (text "Very Very Long") #:halign 'center))]}
  123. @defproc[(vappend [y raart?] [x raart?] ... [#:halign halign (or/c
  124. halign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{Like
  125. @racket[vappend2], but for many arguments.
  126. @ex[(draw-here (vappend (text "Short") (text "A Little Medium") (text "Very Very Long") #:halign 'right))]}
  127. @defproc[(vappend* [y-and-xs (non-empty-listof raart?)] [#:halign
  128. halign (or/c halign/c #f) #f] [#:reverse? reverse? boolean? #f])
  129. raart?]{Like @racket[vappend], but accepts arguments as a list.
  130. @ex[(draw-here (vappend* (list (text "Short") (text "A Little Medium") (text "Very Very Long")) #:halign 'right))]}
  131. @defproc[(happend2 [y raart?] [x raart?] [#:valign valign (or/c
  132. valign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{ Renders
  133. @racket[y] horizontally to the left of @racket[x]. (If
  134. @racket[reverse?] is true, then the effects are evaluated in the
  135. opposite order.) Uses @racket[valign] to determine the vertical
  136. alignment. If @racket[valign] is @racket[#f], then the arts must have
  137. the same height.
  138. @ex[(draw-here (happend2 (vline 2) (vline 2)))
  139. (eval:error
  140. (draw-here (happend2 (vline 2) (vline 4))))
  141. (draw-here (happend2 (vline 2) (vline 4) #:valign 'top))
  142. (draw-here (happend2 (vline 2) (vline 4) #:valign 'center))
  143. (draw-here (happend2 (vline 2) (vline 4) #:valign 'bottom))]}
  144. @defproc[(happend [y raart?] [x raart?] ... [#:valign valign (or/c
  145. valign/c #f) #f] [#:reverse? reverse? boolean? #f]) raart?]{Like
  146. @racket[happend2], but for many arguments.
  147. @ex[(draw-here (happend (vline 2) (vline 3) (vline 4) #:valign 'top))]}
  148. @defproc[(happend* [y-and-xs (non-empty-listof raart?)] [#:valign
  149. valign (or/c valign/c #f) #f] [#:reverse? reverse? boolean? #f])
  150. raart?]{Like @racket[happend], but accepts arguments as a list.
  151. @ex[(draw-here (happend* (list (vline 2) (vline 3) (vline 4)) #:valign 'top))]}
  152. @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.
  153. @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."))]}
  154. @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].}
  155. @defproc[(place-at [back raart?] [dr exact-nonnegative-integer?] [dh
  156. exact-nonnegative-integer?] [front raart?]) raart?]{Renders
  157. @racket[front] on top of @racket[back] offset by @racket[dr] rows and
  158. @racket[dh] columns.}
  159. @defform[(place-at* back [dr dc fore] ...) #:contracts ([back raart?]
  160. [dr exact-nonnegative-integer?] [dc exact-nonnegative-integer?] [fore
  161. raart?])]{Calls @racket[place-at] on a sequence of art objects from
  162. back on the left to front on the right.}
  163. @defproc[(frame [#:style s (or/c style/c #f) #f] [#:fg fc (or/c
  164. color/c #f)] [#:bg bc (or/c color/c #f)] [x raart?]) raart?]{Renders
  165. @racket[x] with a frame where the frame character's style is
  166. controlled by @racket[s], @racket[fc], and @racket[bc].}
  167. @defproc[(matte-at [mw exact-nonnegative-integer?] [mh
  168. exact-nonnegative-integer?] [c exact-nonnegative-integer?] [r
  169. exact-nonnegative-integer?] [x raart?]) raart?]{Mattes @racket[x]
  170. inside a blank of size @racket[mw] columns and @racket[mh] rows at row
  171. @racket[r] and column @racket[c].}
  172. @defproc[(translate [dr exact-nonnegative-integer?] [dc
  173. exact-nonnegative-integer?] [x raart?]) raart?]{Translates @racket[x]
  174. by @racket[dr] rows and @racket[dc] columns.}
  175. @defproc[(matte [w exact-nonnegative-integer?] [h
  176. exact-nonnegative-integer?] [#:halign halign halign/c 'center]
  177. [#:valign valign valign/c 'center] [x raart?]) raart?]{Mattes
  178. @racket[x] inside a blank of size @racket[w]x@racket[h] with the given
  179. alignment.}
  180. @defproc[(inset [dw exact-nonnegative-integer?] [dh
  181. exact-nonnegative-integer?] [x raart?]) raart?]{Insets @racket[x] with
  182. @racket[dw] columns and @racket[dh] rows of blanks.}
  183. @defproc[(mask [mc exact-nonnegative-integer?] [mw
  184. exact-nonnegative-integer?] [mr exact-nonnegative-integer?] [mh
  185. exact-nonnegative-integer?] [x raart?]) raart?]{Renders the portion of
  186. @racket[x] inside the rectangle (@racket[mc],@racket[mr])
  187. to (@racket[(+ mc mw)],@racket[(+ mr mh)]).}
  188. @defproc[(crop [cc exact-nonnegative-integer?] [cw
  189. exact-nonnegative-integer?] [cr exact-nonnegative-integer?] [ch
  190. exact-nonnegative-integer?] [x raart?]) raart?]{Renders the portion of
  191. @racket[x] inside the rectangle (@racket[cc],@racket[cr])
  192. to (@racket[(+ cc cw)],@racket[(+ cr ch)]) and removes the surrounding
  193. blanks.}
  194. @defproc[(table [cells (listof (listof raart?))] [#:frames? frames?
  195. boolean? #t] [#:style s (or/c style/c #f) #f] [#:fg f (or/c color/c
  196. #f) #f] [#:bg b (or/c color/c #f) #f] [#:inset-dw dw
  197. exact-nonnegative-integer? 0] [#:inset-dh dh
  198. exact-nonnegative-integer? 0] [#:valign row-valign valign/c 'top]
  199. [#:halign halign (or/c halign/c (list*of halign/c (or/c halign/c
  200. '()))) 'left]) raart?]{Renders a table of cells where frames are added
  201. if @racket[frames?] is non-false with style and color given by the
  202. arguments. Cells are inset by @racket[inset-dh] rows and
  203. @racket[inset-dw] columns. Cells are horizontally aligned with
  204. @racket[halign]. Rows are vertically aligned with
  205. @racket[row-valign].}
  206. @defproc[(text-rows [cells (listof (listof any/c))]) (listof (listof
  207. raart?))]{Transforms a matrix of content into a matrix of art objects,
  208. using @racket[~a] composed with @racket[text] if they are not already
  209. art objects.}
  210. @defproc[(if-drawn [f (-> exact-nonnegative-integer?
  211. exact-nonnegative-integer? exact-nonnegative-integer?
  212. exact-nonnegative-integer? any)] [x raart?]) raart?]{Renders
  213. @racket[x] and if it ends up being displayed, then calls @racket[f]
  214. with the actual bounding box, given as a row, column, width, and
  215. height.}
  216. @defproc[(place-cursor-after [x raart?] [cr
  217. exact-nonnegative-integer?] [ch exact-nonnegative-integer?])
  218. raart?]{Renders @racket[x] but places the cursor at row @racket[cr]
  219. and column @racket[ch] afterwards.}
  220. @defproc[(without-cursor [x raart?]) raart?]{Renders @racket[x], but
  221. signals to @racket[draw] to not display the cursor, if this is the art
  222. object given to it. (That is, this has no effect if composed with
  223. other drawing operations.)}
  224. @section{lux integration}
  225. @defmodule[raart/lux-chaos]
  226. @racketmodname[raart] provides integration with @racketmodname[lux]
  227. via the @racketmodname[ansi] module.
  228. @defproc[(make-raart [#:mouse? mouse? boolean? #f]) chaos?]{
  229. Returns a @tech[#:doc '(lib "lux/scribblings/lux.scrbl")]{chaos} that
  230. manages the terminal.
  231. The values that @racket[word-event] is called with are characters or
  232. @racket[screen-size-report] structures. If @racket[mouse?] is
  233. non-false, then @racket[any-mouse-event], @racket[mouse-focus-event],
  234. or @racket[mouse-event] structures may also be provided.
  235. The values that @racket[word-output] should return are @racket[raart?]
  236. objects. The drawing will use @racket[make-cached-buffer] to optimize
  237. the display process.
  238. }