I'm interested in using text%
from racket/gui
for an application that needs a rich text editor. I'm interested in general in any existing work along these lines, but I have a very basic question to start with: How can I get structured data from an editor with styled text?
To be concrete, imagine a tiny subset of HTML:
(flat-rec-contract xexpr/c
string?
(cons/c 'b (listof xexpr/c))
(cons/c 'i (listof xexpr/c))
(cons/c 'span (listof xexpr/c)))
Thanks to Markdown View using the Racket editor%, I know how to display such an xexpr/c
in a text%
:
#lang racket/gui
(define (insert-html ed x)
(dynamic-wind
(λ ()
(send ed begin-edit-sequence))
(λ ()
(define s-l (send ed get-style-list))
(define plain-style
(send s-l
find-named-style
(send ed default-style-name)))
(define bold-style
(send s-l
find-or-create-style
plain-style
(make-object style-delta% 'change-bold)))
(define italic-style
(send s-l
find-or-create-style
plain-style
(make-object style-delta% 'change-style 'italic)))
(define bold-italic-style
(send s-l
find-or-create-join-style
bold-style
italic-style))
(let loop ([modes #hasheq()]
[x x])
(match x
[(cons 'span xs)
(for ([x (in-list xs)])
(loop modes x))]
[(cons 'b xs)
(for ([x (in-list xs)])
(loop (hash-set modes 'b #t) x))]
[(cons 'i xs)
(for ([x (in-list xs)])
(loop (hash-set modes 'i #t) x))]
[(? string?)
(define start (send ed last-position))
(send ed insert x)
(define end (send ed last-position))
(send ed
change-style
(match (hash-keys modes 'ordered)
['(b i)
bold-italic-style]
['(b)
bold-style]
['(i)
italic-style]
['()
plain-style])
start
end)])))
(λ ()
(send ed end-edit-sequence))))
(define txt
(new text% [auto-wrap #t]))
(insert-html
txt
`(span "This is some text with "
(b "bold " (i "and italic"))
" parts. It also has "
(i "italic " (b "and bold"))
" parts."))
(define f
(new frame%
[label "Rich text%"]
[width 400]
[height 400]))
(define ec
(new editor-canvas%
[parent f]
[style '(no-hscroll)]
[editor txt]))
(send f show #t)
Similarly, I can imagine how to add buttons and menu items so the user can edit text and styling in the usual way.
What I don't know is how to get the contents of an editor like txt
as an xexpr/c
. I know there are various methods like find-first-snip
and get-style
in snip%
, but I'm not clear on how change-style
interacts with creating/splitting/joining snips, or if there's some better approach overall.