documentation

This commit is contained in:
2026-03-16 20:04:30 +01:00
parent a7257a3492
commit 7c3b780ae9
14 changed files with 2424 additions and 583 deletions

797
scrbl/racket-webview.scrbl Normal file
View File

@@ -0,0 +1,797 @@
#lang scribble/manual
@title{High-Level Racket Interface for Webviews}
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
@defmodule[racket-webview]
@section{Overview}
The module @tt{racket-webview.rkt} provides the high-level Racket interface to
the native @tt{rktwebview_qt} backend.
It is built on top of the low-level FFI layer and adds:
@itemlist[#:style 'compact
@item{Racket contracts on exported procedures}
@item{HTTP(S) context management}
@item{an embedded local HTTPS server per context}
@item{automatic self-signed certificate generation}
@item{decoded event delivery as Racket hash tables}
@item{decoded JavaScript results as Racket values}
@item{DOM convenience functions for values, attributes, classes, and styles}
]
Applications are expected to use this module rather than the lower-level FFI
module directly.
@section{Architecture}
This module sits above the low-level FFI wrapper and provides a more idiomatic
Racket API.
The stack is organized as follows:
@itemlist[#:style 'compact
@item{the native Qt backend}
@item{the thin FFI wrapper in @tt{racket-webview-qt.rkt}}
@item{the higher-level Racket interface in @tt{racket-webview.rkt}}
]
This layer introduces two main abstractions:
@itemlist[#:style 'compact
@item{@tt{wv-context}, representing an HTTP(S) context}
@item{@tt{wv-win}, representing a webview window}
]
@section{HTTP(S) Contexts}
A context represents the HTTP(S) environment in which webviews operate.
Unlike the lower-level FFI layer, this module does more than create a native
WebEngine profile. It also starts a local HTTPS server bound to
@tt{127.0.0.1} on an automatically chosen port.
Each context therefore combines:
@itemlist[#:style 'compact
@item{a native WebEngine context}
@item{a local HTTPS server}
@item{a self-signed certificate}
@item{a base URL}
@item{a file getter used to resolve incoming requests}
]
@defstruct*[wv-context
([context exact-integer?]
[port exact-integer?]
[file-getter procedure?]
[webserver-thread thread?]
[base-url string?]
[request-count any/c]
[sec-token-cache any/c]
[cert-ou-token string?])]{
Represents a webview HTTP(S) context.
The structure is transparent, but it is intended to be treated as a context
handle.
}
@defproc[(webview-new-context
[file-getter procedure?]
[#:boilerplate-js boilerplate-js string?
(webview-default-boilerplate-js)])
wv-context?]{
Creates a new HTTP(S) context.
The @racket[file-getter] procedure is used by the embedded web server to map
request paths to files on disk.
The function performs the following steps:
@itemlist[#:style 'compact
@item{creates a fresh context structure}
@item{generates a self-signed certificate}
@item{starts a local HTTPS server on @tt{127.0.0.1}}
@item{creates the native context through the FFI layer}
@item{stores the resulting base URL}
]
The @racket[boilerplate-js] argument supplies JavaScript that is injected into
pages loaded within the native context.
The returned value is a @racket[wv-context] structure.
}
@defproc[(wv-context-base-url [ctx wv-context?]) string?]{
Returns the base URL associated with the context.
This is the local HTTPS URL served by the embedded web server, for example a
URL of the form @tt{https://127.0.0.1:port}.
}
@section{Webview Windows}
A webview window is represented by a @racket[wv-win] structure.
@defstruct*[wv-win
([handle any/c]
[context wv-context?]
[window-nr exact-integer?])]{
Represents a webview window.
Fields:
@itemlist[#:style 'compact
@item{@tt{handle}: the low-level FFI handle}
@item{@tt{context}: the owning @racket[wv-context]}
@item{@tt{window-nr}: the native numeric window identifier}
]
}
@defproc[(wv-win-window-nr [wv wv-win?]) exact-integer?]{
Returns the native numeric window identifier of @racket[wv].
}
@defproc[(webview-create
[context wv-context?]
[url-path string?]
[event-callback procedure?]
[#:parent parent (or/c wv-win? #f) #f])
wv-win?]{
Creates a new webview window inside the given context.
Arguments:
@itemlist[#:style 'compact
@item{@racket[context]: the HTTP(S) context}
@item{@racket[url-path]: initial URL path to load}
@item{@racket[event-callback]: callback receiving decoded events}
@item{@racket[parent]: optional parent window}
]
The function creates the native window, associates the context certificate
OU token with it, and immediately loads the given path through
@racket[webview-set-url!].
The callback receives two arguments:
@itemlist[#:style 'compact
@item{the created @racket[wv-win]}
@item{a decoded event hash}
]
Native JSON event strings are converted to Racket hashes before delivery.
}
@defproc[(webview-close [wv wv-win?]) symbol?]{
Closes the webview.
Returns @racket['oke].
}
@section{Navigation and HTML Loading}
@defproc[(webview-set-url! [wv wv-win?] [url (or/c string? url?)]) symbol?]{
Loads a URL into the webview.
If the supplied URL is relative or missing scheme and host information, the
function resolves it against the base URL of the window's context.
This makes it possible to load application pages relative to the embedded local
HTTPS server.
}
@defproc[(webview-navigate! [wv wv-win?] [place string?]) symbol?]{
Navigates the current page by assigning to @tt{window.location} through
JavaScript.
This is distinct from @racket[webview-set-url!], which loads a URL through the
native navigation interface.
}
@defproc[(webview-set-html! [wv wv-win?] [html (or/c string? xexpr?)]) symbol?]{
Loads HTML directly into the webview.
If @racket[html] is an x-expression, it is converted with
@racket[xexpr->string] first.
}
@defproc[(webview-base-url [wv wv-win?]) url?]{
Returns the base URL of the webview's context as a Racket URL value.
}
@section{Window Management}
@defproc[(webview-devtools [wv wv-win?]) symbol?]{
Opens the developer tools for the webview.
}
@defproc[(webview-move [wv wv-win?] [x number?] [y number?]) symbol?]{
Moves the window.
}
@defproc[(webview-resize [wv wv-win?] [w number?] [h number?]) symbol?]{
Resizes the window.
}
@defproc[(webview-show [wv wv-win?]) symbol?]{
Shows the window.
}
@defproc[(webview-hide [wv wv-win?]) symbol?]{
Hides the window.
}
@defproc[(webview-show-normal [wv wv-win?]) symbol?]{
Restores the window to the normal state.
}
@defproc[(webview-maximize [wv wv-win?]) symbol?]{
Maximizes the window.
}
@defproc[(webview-minimize [wv wv-win?]) symbol?]{
Minimizes the window.
}
@defproc[(webview-present [wv wv-win?]) symbol?]{
Presents the window to the user.
}
@defproc[(webview-window-state [wv wv-win?]) symbol?]{
Returns the current window state.
}
@defproc[(webview-set-title! [wv wv-win?] [title string?]) symbol?]{
Sets the native window title.
}
@section{Message Boxes and File Dialogs}
@defproc[(webview-messagebox
[wv wv-win?]
[type symbol?]
[title string?]
[message string?]
[#:sub submessage string? ""])
symbol?]{
Shows a native message box.
The @racket[type] argument is passed through to the lower layer and is expected
to be one of the supported message box kinds, such as
@racket['info], @racket['error], @racket['warning], @racket['yes-no], or
@racket['oke-cancel].
}
@defproc[(webview-choose-dir
[wv wv-win?]
[title string?]
[base-dir (or/c path? string?)])
symbol?]{
Opens a directory chooser dialog.
}
@defproc[(webview-file-open
[wv wv-win?]
[title string?]
[base-dir (or/c path? string?)]
[permitted-exts (or/c wv-permitted-exts? wv-list-of-permitted-exts?)])
symbol?]{
Opens a native file-open dialog.
The @racket[permitted-exts] value is converted to a Qt file filter string.
}
@defproc[(webview-file-save
[wv wv-win?]
[title string?]
[base-dir (or/c path? string?)]
[permitted-exts (or/c wv-permitted-exts? wv-list-of-permitted-exts?)])
symbol?]{
Opens a native file-save dialog.
The @racket[permitted-exts] value is converted to a Qt file filter string.
}
@section{Permitted Extensions}
The file dialog helpers use @racket[wv-permitted-exts] values to describe file
type filters.
@defstruct*[wv-permitted-exts
([name string?]
[exts wv-permitted-exts-exts?])]{
Represents a named file filter.
The @racket[name] field is the human-readable filter name.
The @racket[exts] field is a list of filename extensions represented as
symbols.
}
@defproc[(wv-permitted-exts-exts? [v any/c]) boolean?]{
Returns whether @racket[v] is a valid list of extension symbols.
}
@defproc[(wv-permitted-exts-name? [v any/c]) boolean?]{
Returns whether @racket[v] is a valid filter name.
}
@defproc[(wv-list-of-permitted-exts? [v any/c]) boolean?]{
Returns whether @racket[v] is a list of valid @racket[wv-permitted-exts]
values.
}
@defproc[(webview-filter->exts [str string?])
(or/c wv-permitted-exts? #f)]{
Parses a Qt-style filter string into a @racket[wv-permitted-exts] value.
If the string does not match the expected filter format, the result is
@racket[#f].
}
@section{Event Binding}
The module provides helpers for binding DOM events from page elements back to
the Racket side.
@defproc[(webview-bind!
[wv wv-win?]
[selector (or/c symbol? string?)]
[event (or/c symbol? list-of-symbol?)])
list?]{
Binds one or more DOM events to elements selected by @racket[selector].
If @racket[selector] is a symbol, it is interpreted as an element id and
rewritten as a CSS id selector.
If @racket[event] is a symbol, it is treated as a single event name.
The result is a list describing the created bindings.
}
@defproc[(webview-unbind!
[wv wv-win?]
[selector (or/c symbol? string?)]
[event (or/c symbol? list-of-symbol?)])
list?]{
Removes previously established DOM event bindings.
Its arguments and return value follow the same conventions as
@racket[webview-bind!].
}
@section{JavaScript Execution}
@defproc[(webview-run-js [wv wv-win?] [js string?]) symbol?]{
Executes JavaScript for side effects.
}
@defproc[(webview-call-js-result? [x any/c]) boolean?]{
Returns whether @racket[x] has the low-level two-element result shape used by
the FFI function @racket[rkt-webview-call-js].
This predicate is mainly a utility for internal result checking.
}
@defproc[(webview-call-js [wv wv-win?] [js string?])
(or/c string? list? boolean? hash?)]{
Executes JavaScript and returns the decoded @tt{result} field of the underlying
native JSON response.
Unlike the lower-level FFI wrapper, this function does not return a
@racket[(list result-symbol json-string)] pair. Instead:
@itemlist[#:style 'compact
@item{the low-level result is checked}
@item{the JSON string is decoded}
@item{the @tt{result} field is extracted}
@item{the extracted value is returned directly}
]
If the JavaScript evaluation fails, the function raises an error.
}
@section{DOM Content Helpers}
@defproc[(webview-set-innerHTML!
[wv wv-win?]
[id symbol?]
[html (or/c string? xexpr?)])
symbol?]{
Sets the @tt{innerHTML} of the element with the given id.
If @racket[html] is an x-expression, it is converted to HTML first.
}
@section{Form Value Helpers}
@defproc[(webview-set-value!
[wv wv-win?]
[id symbol?]
[val (or/c symbol?
string?
number?
boolean?
g:date?
g:time?
g:datetime?
rgba?)])
symbol?]{
Sets the value of the element with the given id.
Checkboxes and radio buttons are handled through their @tt{checked} property.
Other elements are handled through their @tt{value} property.
Date, time, datetime, and color values are converted to strings before being
sent to the browser.
}
@defproc[(webview-value [wv wv-win?] [id symbol?])
(or/c string? boolean?)]{
Returns the current value of the element with the given id.
For checkboxes and radio buttons, the returned value reflects the checked state.
For other elements, the result is the string value.
}
@defproc[(webview-value/boolean [wv wv-win?] [id symbol?])
(or/c boolean? #f)]{
Returns the current value converted to a boolean.
}
@defproc[(webview-value/symbol [wv wv-win?] [id symbol?])
(or/c symbol? #f)]{
Returns the current value converted to a symbol.
}
@defproc[(webview-value/number [wv wv-win?] [id symbol?])
(or/c number? #f)]{
Returns the current value converted to a number.
}
@defproc[(webview-value/date [wv wv-win?] [id symbol?])
(or/c g:date? #f)]{
Returns the current value converted to a date.
}
@defproc[(webview-value/time [wv wv-win?] [id symbol?])
(or/c g:time? #f)]{
Returns the current value converted to a time.
}
@defproc[(webview-value/datetime [wv wv-win?] [id symbol?])
(or/c g:datetime? #f)]{
Returns the current value converted to a datetime.
}
@defproc[(webview-value/color [wv wv-win?] [id symbol?])
(or/c rgba? #f)]{
Returns the current value converted to an RGBA color.
}
@section{Class Helpers}
@defproc[(webview-add-class!
[wv wv-win?]
[id-or-selector (or/c symbol? string?)]
[class (or/c symbol? string? list?)])
hash?]{
Adds one or more CSS classes to all elements selected by
@racket[id-or-selector].
A symbol selector is interpreted as an element id.
}
@defproc[(webview-remove-class!
[wv wv-win?]
[id-or-selector (or/c symbol? string?)]
[class (or/c symbol? string? list?)])
hash?]{
Removes one or more CSS classes from all selected elements.
}
@section{Style Helpers}
@defproc[(webview-set-style!
[wv wv-win?]
[selector (or/c symbol? string?)]
[style-entries (or/c kv? list-of-kv?)])
hash?]{
Sets one or more inline CSS style properties on the selected elements.
The @racket[style-entries] argument is either a single key/value pair or a list
of key/value pairs.
}
@defproc[(webview-get-style
[wv wv-win?]
[selector (or/c symbol? string?)]
[styles (or/c symbol? list-of-symbol?)])
(or/c list? hash?)]{
Reads computed style values from the selected elements.
If @racket[selector] is a symbol, the result is the style hash for that single
element.
If @racket[selector] is a string selector, the result is a list of
@racket[(cons id style-hash)] pairs.
}
@defproc[(webview-unset-style!
[wv wv-win?]
[selector (or/c symbol? string?)]
[style-entries (or/c symbol? list-of-symbol?)])
hash?]{
Clears one or more inline style properties from the selected elements.
}
@section{Attribute Helpers}
@defproc[(webview-set-attr!
[wv wv-win?]
[selector (or/c symbol? string?)]
[attr-entries (or/c kv? list-of-kv?)])
hash?]{
Sets one or more HTML attributes on the selected elements.
Date, time, datetime, and color values are converted to strings before being
sent to the browser.
}
@defproc[(webview-attr
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c string? boolean?)]{
Returns the value of the named attribute of the element with the given id.
If the attribute does not exist, the result is @racket[#f].
}
@defproc[(webview-attr/boolean
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c boolean? #f)]{
Returns the attribute converted to a boolean.
}
@defproc[(webview-attr/symbol
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c symbol? #f)]{
Returns the attribute converted to a symbol.
}
@defproc[(webview-attr/number
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c number? #f)]{
Returns the attribute converted to a number.
}
@defproc[(webview-attr/date
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c g:date? #f)]{
Returns the attribute converted to a date.
}
@defproc[(webview-attr/time
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c g:time? #f)]{
Returns the attribute converted to a time.
}
@defproc[(webview-attr/datetime
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c g:datetime? #f)]{
Returns the attribute converted to a datetime.
}
@defproc[(webview-attr/color
[wv wv-win?]
[id symbol?]
[attr (or/c symbol? string?)])
(or/c rgba? #f)]{
Returns the attribute converted to an RGBA color.
}
@section{Utility Functions}
@defproc[(webview-standard-file-getter
[base-path path-string?]
[#:not-exist on-not-exist procedure?
(λ (file base-path path) path)])
procedure?]{
Creates a standard file getter for use with @racket[webview-new-context].
The resulting procedure maps request paths to files under @racket[base-path].
The special request path @tt{/} is mapped to @tt{index.html}.
If the resolved file does not exist, the @racket[on-not-exist] procedure is
used to determine the result path.
}
@defproc[(webview-default-boilerplate-js [custom-js procedure?] ...)
string?]{
Returns the default boilerplate JavaScript used by the system.
Any supplied procedures are called and their returned JavaScript strings are
appended to the default boilerplate.
}
@defproc[(webview-version) pair?]{
Returns version information for the high-level webview package and the native
backend.
The result is a pair whose first element describes the Racket webview package
version and whose remaining data comes from @racket[rkt-webview-version].
}
@section{Event Reference}
Events are delivered to @racket[webview-create]'s callback as decoded Racket
hash tables.
The helper @racket[util-parse-event] converts the incoming JSON and also
normalizes the @tt{event} field from a string to a symbol.
Each event therefore contains at least an @racket['event] entry.
@subsection{Window Events}
@subsubsection{@tt{show}}
Example:
@racketblock[
'#hash((event . show))
]
@subsubsection{@tt{hide}}
Example:
@racketblock[
'#hash((event . hide))
]
@subsubsection{@tt{move}}
Typical fields:
@itemlist[#:style 'compact
@item{@racket['x]}
@item{@racket['y]}
]
@subsubsection{@tt{resize}}
Typical fields:
@itemlist[#:style 'compact
@item{@racket['w]}
@item{@racket['h]}
]
@subsubsection{@tt{closed}}
Indicates that the window was closed.
@subsubsection{@tt{can-close?}}
Generated when the user attempts to close the window.
@subsection{Navigation and Loading Events}
@subsubsection{@tt{page-loaded}}
Contains an @racket['oke] field indicating whether loading succeeded.
@subsubsection{@tt{navigation-request}}
Typically contains:
@itemlist[#:style 'compact
@item{@racket['url]}
@item{@racket['type]}
]
@subsection{JavaScript Events}
@subsubsection{@tt{js-evt}}
Represents a JavaScript-originated event sent through the injected browser
bridge.
The original JavaScript payload is available under the @racket['js-evt] key.
@subsection{Dialog Events}
Dialog completion is reported through ordinary events, including event names
such as:
@itemlist[#:style 'compact
@item{@tt{choose-dir-ok}}
@item{@tt{choose-dir-cancel}}
@item{@tt{file-open-ok}}
@item{@tt{file-open-cancel}}
@item{@tt{file-save-ok}}
@item{@tt{file-save-cancel}}
@item{@tt{msgbox-ok}}
@item{@tt{msgbox-cancel}}
@item{@tt{msgbox-yes}}
@item{@tt{msgbox-no}}
]
@section{Example}
@racketblock[
(define file-getter
(webview-standard-file-getter "htdocs"))
(define ctx
(webview-new-context file-getter))
(define wv
(webview-create
ctx
"index.html"
(λ (wv evt)
(displayln evt))))
(webview-set-title! wv "Example")
(webview-resize wv 800 600)
]
@section{Summary}
The module @tt{racket-webview.rkt} provides the main Racket programming
interface for the webview system.
Compared to the lower-level FFI layer, it adds:
@itemlist[#:style 'compact
@item{HTTP(S) contexts with local web serving}
@item{automatic certificate generation}
@item{decoded event delivery}
@item{decoded JavaScript results}
@item{DOM convenience helpers}
]