Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cee2f452aa | |||
| 37468d4fa0 | |||
| 963fec5df5 | |||
| 3d4deed94a | |||
| d6f555d43f |
@@ -25,3 +25,4 @@ rktwebview/build/
|
|||||||
/scrbl/*.js
|
/scrbl/*.js
|
||||||
*.bak
|
*.bak
|
||||||
/scrbl/*.html
|
/scrbl/*.html
|
||||||
|
/doc
|
||||||
|
|||||||
@@ -1,35 +1,11 @@
|
|||||||
# racket-webview
|
# racket-webview
|
||||||
|
|
||||||
Webview integration for Racket.
|
Webview integration for racket
|
||||||
|
|
||||||
## Release v0.1.11
|
## Release v0.1.8
|
||||||
|
|
||||||
This release assigns explicit, file-prefixed Scribble tags to manual parts and sections. This prevents duplicate collected-information warnings for headings such as `Overview`, `Example`, `Conversion`, `Events`, and `racket-webview` during `raco setup`.
|
Uses release v0.2.2 of racket-webview-qt.
|
||||||
|
QtWebEngine select popup rendering issue observed with older Qt 6.x.
|
||||||
The process-interface changes from v0.1.10 remain unchanged.
|
Symptom: native HTML <select> popup keeps growing/repainting while open;
|
||||||
|
QtWebEngine process busy, Racket process idle.
|
||||||
### Process interface (introduced in v0.1.10)
|
Resolved by upgrading to Qt 6.11.1.
|
||||||
|
|
||||||
This version replaces the FFI/shared-memory transport with a child-process
|
|
||||||
interface to racket-webview-qt v0.2.3:
|
|
||||||
|
|
||||||
- commands: JSON lines on child stdin
|
|
||||||
- results and events: JSON lines on child stdout
|
|
||||||
- backend diagnostics: child stderr
|
|
||||||
|
|
||||||
The public Racket procedures, command numbers, command payloads, result symbols,
|
|
||||||
and event JSON remain unchanged. Set `RKT_WEBVIEW_PRG` to an explicit locally
|
|
||||||
built backend executable during development.
|
|
||||||
|
|
||||||
## Previous Qt update
|
|
||||||
|
|
||||||
QtWebEngine select popup rendering issues were observed with older Qt 6.x. The
|
|
||||||
native HTML `<select>` popup could keep growing or repainting while open while
|
|
||||||
the QtWebEngine process remained busy. Upgrading to Qt 6.11.1 resolved this.
|
|
||||||
|
|
||||||
### Qt backend logging
|
|
||||||
|
|
||||||
Structured stderr records from `rktwebview_prg` are logged through `simple-log`
|
|
||||||
under the separate topic `webview-backend`. Ordinary Racket-side messages keep
|
|
||||||
the topic `webview`. Unstructured stderr lines are accepted and logged at debug
|
|
||||||
level under `webview-backend`.
|
|
||||||
|
|||||||
+13
-3
@@ -235,6 +235,17 @@
|
|||||||
(send this update-counter)
|
(send this update-counter)
|
||||||
(send this set-folder my-dir)
|
(send this set-folder my-dir)
|
||||||
|
|
||||||
|
(when (eq? my-page 'first)
|
||||||
|
(let ((language-select
|
||||||
|
(send this element 'sel-lang)))
|
||||||
|
(send language-select
|
||||||
|
on-change!
|
||||||
|
(λ (value)
|
||||||
|
(displayln
|
||||||
|
(format
|
||||||
|
"Selected language: ~a"
|
||||||
|
(send language-select get)))))))
|
||||||
|
|
||||||
(ww-debug "CONNECTING BUTTONS")
|
(ww-debug "CONNECTING BUTTONS")
|
||||||
(send this bind! 'dialog-button 'click (λ (el evt data)
|
(send this bind! 'dialog-button 'click (λ (el evt data)
|
||||||
(send this prefs)))
|
(send this prefs)))
|
||||||
@@ -310,8 +321,7 @@
|
|||||||
(webview-set-loglevel 'debug)
|
(webview-set-loglevel 'debug)
|
||||||
(define log-file (build-path (find-system-path 'temp-dir) "example1.log"))
|
(define log-file (build-path (find-system-path 'temp-dir) "example1.log"))
|
||||||
(displayln (format "logging to ~a" log-file))
|
(displayln (format "logging to ~a" log-file))
|
||||||
;(sl-log-to-file log-file)
|
(sl-log-to-file log-file)
|
||||||
(define store (sl-log-to-store 10000))
|
|
||||||
|
|
||||||
(define (run-example)
|
(define (run-example)
|
||||||
(let* ((ini (new ini% [file 'web-racket-example1]))
|
(let* ((ini (new ini% [file 'web-racket-example1]))
|
||||||
@@ -328,5 +338,5 @@
|
|||||||
(let ((window (run-example)))
|
(let ((window (run-example)))
|
||||||
(webview-wait-for-quit)
|
(webview-wait-for-quit)
|
||||||
(webview-exit)
|
(webview-exit)
|
||||||
;(exit)
|
(exit)
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#lang info
|
#lang info
|
||||||
|
|
||||||
(define pkg-authors '(hnmdijkema))
|
(define pkg-authors '(hnmdijkema))
|
||||||
(define version "0.1.11")
|
(define version "0.1.9")
|
||||||
(define license 'MIT)
|
(define license 'MIT)
|
||||||
(define collection "racket-webview")
|
(define collection "racket-webview")
|
||||||
(define pkg-desc "racket-webview - A Web Based GUI library, based on a Qt WebEngine backend")
|
(define pkg-desc "racket-webview - A Web Based GUI library, based on a Qt WebEngine backend")
|
||||||
|
|
||||||
@@ -13,13 +13,13 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
(define deps
|
(define deps
|
||||||
'("racket/base"
|
'("racket/base"
|
||||||
"http-easy"
|
"http-easy"
|
||||||
"gregor"
|
"gregor"
|
||||||
"racket/gui"
|
"racket/gui"
|
||||||
"html-printer-lib"
|
"html-printer-lib"
|
||||||
"racket/net"
|
"racket/net"
|
||||||
"simple-ini"
|
"simple-ini"
|
||||||
"gregor-utils"
|
"gregor-utils"
|
||||||
"racket-sprintf"
|
"racket-sprintf"
|
||||||
"lru-cache"
|
"lru-cache"
|
||||||
|
|||||||
@@ -40,19 +40,11 @@
|
|||||||
warn-webview
|
warn-webview
|
||||||
fatal-webview
|
fatal-webview
|
||||||
sync-log-webview
|
sync-log-webview
|
||||||
|
|
||||||
dbg-webview-backend
|
|
||||||
err-webview-backend
|
|
||||||
info-webview-backend
|
|
||||||
warn-webview-backend
|
|
||||||
fatal-webview-backend
|
|
||||||
sync-log-webview-backend
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
(sl-def-log webview)
|
(sl-def-log webview)
|
||||||
(sl-def-log webview-backend)
|
|
||||||
|
|
||||||
(define-syntax while
|
(define-syntax while
|
||||||
(syntax-rules ()
|
(syntax-rules ()
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(define rkt-qt-version-major 0)
|
(define rkt-qt-version-major 0)
|
||||||
(define rkt-qt-version-minor 2)
|
(define rkt-qt-version-minor (if (eq? (system-type 'os) 'windows) 3 2))
|
||||||
(define rkt-qt-version-patch 3)
|
(define rkt-qt-version-patch (if (eq? (system-type 'os) 'windows) 1 1))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Internal functions
|
;; Internal functions
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
(let ((out (open-output-file version-file #:exists 'replace)))
|
(let ((out (open-output-file version-file #:exists 'replace)))
|
||||||
(write version out)
|
(write version out)
|
||||||
(close-output-port out)))
|
(close-output-port out)))
|
||||||
(displayln "Version file written; backend process is ready")
|
(displayln "Version file written; ready for FFI integration")
|
||||||
#t
|
#t
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+883
-871
File diff suppressed because it is too large
Load Diff
+12
-3
@@ -729,7 +729,11 @@
|
|||||||
'oke))
|
'oke))
|
||||||
|
|
||||||
(define/contract (webview-bind! wv selector event no-prevent-default)
|
(define/contract (webview-bind! wv selector event no-prevent-default)
|
||||||
(-> wv-win? (or/c symbol? string?) (or/c symbol? list-of-symbol?) boolean? list?)
|
(-> wv-win?
|
||||||
|
(or/c symbol? string?)
|
||||||
|
(or/c symbol? list-of-symbol?)
|
||||||
|
boolean?
|
||||||
|
(or/c #f list?))
|
||||||
(let* ((sel (if (symbol? selector)
|
(let* ((sel (if (symbol? selector)
|
||||||
(format "#~a" selector)
|
(format "#~a" selector)
|
||||||
selector))
|
selector))
|
||||||
@@ -909,7 +913,12 @@
|
|||||||
(wvv webview-value/number number? string->number)
|
(wvv webview-value/number number? string->number)
|
||||||
(wvv webview-value/symbol symbol? string->symbol)
|
(wvv webview-value/symbol symbol? string->symbol)
|
||||||
(wvv webview-value/color rgba? hex->rgba)
|
(wvv webview-value/color rgba? hex->rgba)
|
||||||
(wvv webview-value/boolean boolean? (λ (e) (if (string=? e "true") #t #f)))
|
(wvv webview-value/boolean
|
||||||
|
boolean?
|
||||||
|
(λ (e)
|
||||||
|
(if (boolean? e)
|
||||||
|
e
|
||||||
|
(string-ci=? e "true"))))
|
||||||
|
|
||||||
|
|
||||||
(define/contract (webview-add-class! wv id-or-selector class)
|
(define/contract (webview-add-class! wv id-or-selector class)
|
||||||
@@ -1056,7 +1065,7 @@
|
|||||||
; " return { id: id, style: r };"
|
; " return { id: id, style: r };"
|
||||||
; "}") cl))
|
; "}") cl))
|
||||||
; )))
|
; )))
|
||||||
; (display "Result: ") (write r) (newline)
|
;(display "Result: ") (write r) (newline)
|
||||||
(if (eq? r #f)
|
(if (eq? r #f)
|
||||||
#f
|
#f
|
||||||
(let ((h (hash-ref r 'with-ids)))
|
(let ((h (hash-ref r 'with-ids)))
|
||||||
|
|||||||
+11
-11
@@ -3,7 +3,7 @@
|
|||||||
@(require (for-label racket/base
|
@(require (for-label racket/base
|
||||||
"../private/js-transform.rkt"))
|
"../private/js-transform.rkt"))
|
||||||
|
|
||||||
@title[#:tag "js-transform"]{JavaScript Transformation}
|
@title{JavaScript Transformation}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/private/js-transform]
|
@defmodule[racket-webview/private/js-transform]
|
||||||
@@ -47,7 +47,7 @@ This generates JavaScript source that assigns a function to
|
|||||||
sets an attribute, and writes a message to the JavaScript console.
|
sets an attribute, and writes a message to the JavaScript console.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "js-transform-primitive-values"]{Primitive values}
|
@section{Primitive values}
|
||||||
|
|
||||||
Numbers are emitted as JavaScript numeric literals. Strings are emitted as
|
Numbers are emitted as JavaScript numeric literals. Strings are emitted as
|
||||||
double-quoted JavaScript strings, with embedded double quotes escaped.
|
double-quoted JavaScript strings, with embedded double quotes escaped.
|
||||||
@@ -68,7 +68,7 @@ Symbols can be quoted to produce JavaScript string values:
|
|||||||
(send console log 'hello))
|
(send console log 'hello))
|
||||||
]
|
]
|
||||||
|
|
||||||
@section[#:tag "js-transform-function-calls-and-method-calls"]{Function calls and method calls}
|
@section{Function calls and method calls}
|
||||||
|
|
||||||
A form that is not recognized as a special DSL form is treated as a JavaScript
|
A form that is not recognized as a special DSL form is treated as a JavaScript
|
||||||
function call:
|
function call:
|
||||||
@@ -109,7 +109,7 @@ document.getElementById("hi")
|
|||||||
Using @racket[send] keeps the method-call structure explicit in the DSL. It is
|
Using @racket[send] keeps the method-call structure explicit in the DSL. It is
|
||||||
usually clearer than writing dotted JavaScript names directly as function names.
|
usually clearer than writing dotted JavaScript names directly as function names.
|
||||||
|
|
||||||
@section[#:tag "js-transform-operators"]{Operators}
|
@section{Operators}
|
||||||
|
|
||||||
The DSL supports a small set of JavaScript infix operators.
|
The DSL supports a small set of JavaScript infix operators.
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ For example:
|
|||||||
(return (* x x)))))
|
(return (* x x)))))
|
||||||
]
|
]
|
||||||
|
|
||||||
@section[#:tag "js-transform-definitions-and-assignments"]{Definitions and assignments}
|
@section{Definitions and assignments}
|
||||||
|
|
||||||
@defform[(define (name arg ...) body ...)]{
|
@defform[(define (name arg ...) body ...)]{
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ Generates a JavaScript assignment.
|
|||||||
This produces an assignment to @tt{window.answer}.
|
This produces an assignment to @tt{window.answer}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "js-transform-functions"]{Functions}
|
@section{Functions}
|
||||||
|
|
||||||
@defform[(lambda (arg ...) body ...)]{
|
@defform[(lambda (arg ...) body ...)]{
|
||||||
Generates a JavaScript function expression.
|
Generates a JavaScript function expression.
|
||||||
@@ -194,7 +194,7 @@ A function body may contain more than one DSL statement:
|
|||||||
(return (* x x)))))
|
(return (* x x)))))
|
||||||
]
|
]
|
||||||
|
|
||||||
@section[#:tag "js-transform-control-flow-and-statement-blocks"]{Control flow and statement blocks}
|
@section{Control flow and statement blocks}
|
||||||
|
|
||||||
@defform[(if condition then-expr else-expr)]{
|
@defform[(if condition then-expr else-expr)]{
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ Generates a JavaScript @tt{return} statement.
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "js-transform-sequential-bindings"]{Sequential bindings}
|
@section{Sequential bindings}
|
||||||
|
|
||||||
@defform[(let* ((id expr) ...) body ...)]{
|
@defform[(let* ((id expr) ...) body ...)]{
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ context. Use @racket[let*] instead, so that the generated JavaScript bindings
|
|||||||
remain explicitly sequential.
|
remain explicitly sequential.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "js-transform-lists"]{Lists}
|
@section{Lists}
|
||||||
|
|
||||||
@defform[(list expr ...)]{
|
@defform[(list expr ...)]{
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ console.log([ x].concat([ y].concat([ z])));
|
|||||||
}|
|
}|
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "js-transform-embedding-racket-values"]{Embedding Racket values}
|
@section{Embedding Racket values}
|
||||||
|
|
||||||
@defform[(eval value)]{
|
@defform[(eval value)]{
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ method:
|
|||||||
This generates a JavaScript function that logs an array, constructs the same
|
This generates a JavaScript function that logs an array, constructs the same
|
||||||
array in a local binding, maps over it, and returns the mapped result.
|
array in a local binding, maps over it, and returns the mapped result.
|
||||||
|
|
||||||
@section[#:tag "js-transform-limitations"]{Limitations}
|
@section{Limitations}
|
||||||
|
|
||||||
This transformer is intentionally small. It is not a complete JavaScript
|
This transformer is intentionally small. It is not a complete JavaScript
|
||||||
parser, not a JavaScript evaluator and not a general Racket-to-JavaScript
|
parser, not a JavaScript evaluator and not a general Racket-to-JavaScript
|
||||||
|
|||||||
+6
-6
@@ -8,7 +8,7 @@
|
|||||||
json
|
json
|
||||||
"../menu.rkt"))
|
"../menu.rkt"))
|
||||||
|
|
||||||
@title[#:tag "menu"]{menu}
|
@title{menu}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/menu]
|
@defmodule[racket-webview/menu]
|
||||||
@@ -29,7 +29,7 @@ strings and may be supplied either as @racket[#f], strings, or URL values.
|
|||||||
The module does not display menus itself. It provides the menu data structure
|
The module does not display menus itself. It provides the menu data structure
|
||||||
used by higher layers.
|
used by higher layers.
|
||||||
|
|
||||||
@section[#:tag "menu-internal-representation"]{Internal Representation}
|
@section{Internal Representation}
|
||||||
|
|
||||||
Internally, menus are represented by transparent structure values. These
|
Internally, menus are represented by transparent structure values. These
|
||||||
structure constructors and predicates are not exported directly. The public API
|
structure constructors and predicates are not exported directly. The public API
|
||||||
@@ -48,7 +48,7 @@ list is a list of internal menu items, and every submenu recursively also
|
|||||||
satisfies @racket[is-wv-menu?].
|
satisfies @racket[is-wv-menu?].
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "menu-constructors"]{Constructors}
|
@section{Constructors}
|
||||||
|
|
||||||
@defproc[(wv-menu [item-or-id any/c] ...)
|
@defproc[(wv-menu [item-or-id any/c] ...)
|
||||||
any/c]{
|
any/c]{
|
||||||
@@ -97,7 +97,7 @@ stored.
|
|||||||
If any argument does not satisfy these conditions, an exception is raised.
|
If any argument does not satisfy these conditions, an exception is raised.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "menu-traversal-and-lookup"]{Traversal and Lookup}
|
@section{Traversal and Lookup}
|
||||||
|
|
||||||
@defproc[(wv-menu-for-each [menu any/c] [cb procedure?]) boolean?]{
|
@defproc[(wv-menu-for-each [menu any/c] [cb procedure?]) boolean?]{
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ After the callback has been applied, the original @racket[menu] value is
|
|||||||
returned.
|
returned.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "menu-mutation"]{Mutation}
|
@section{Mutation}
|
||||||
|
|
||||||
@defproc[(wv-menu-set-title! [menu any/c] [id symbol?] [title string?])
|
@defproc[(wv-menu-set-title! [menu any/c] [id symbol?] [title string?])
|
||||||
any/c]{
|
any/c]{
|
||||||
@@ -177,7 +177,7 @@ The @racket['id] field of the top-level menu is also converted to a string in
|
|||||||
the JSON output.
|
the JSON output.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "menu-accessors"]{Accessors}
|
@section{Accessors}
|
||||||
|
|
||||||
@defproc[(wv-menu-id [m any/c]) any/c]{
|
@defproc[(wv-menu-id [m any/c]) any/c]{
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
scribble/core
|
scribble/core
|
||||||
)
|
)
|
||||||
|
|
||||||
@title[#:tag "racket-webview-collection"]{@elem{racket-webview}}
|
@title[#:tag "racket-webview-manual"]{@elem{racket-webview}}
|
||||||
|
|
||||||
@table-of-contents[]
|
@table-of-contents[]
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
setup/dirs
|
setup/dirs
|
||||||
"../racket-webview-downloader.rkt"))
|
"../racket-webview-downloader.rkt"))
|
||||||
|
|
||||||
@title[#:tag "racket-webview-downloader"]{racket-webview-downloader}
|
@title{racket-webview-downloader}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/racket-webview-downloader]
|
@defmodule[racket-webview/racket-webview-downloader]
|
||||||
@@ -18,7 +18,7 @@ This module provides functions for checking whether the native runtime is
|
|||||||
already installed, whether it can be resolved and downloaded, and for
|
already installed, whether it can be resolved and downloaded, and for
|
||||||
downloading and unpacking it into the user's addon directory.
|
downloading and unpacking it into the user's addon directory.
|
||||||
|
|
||||||
@section[#:tag "racket-webview-downloader-overview"]{Overview}
|
@section{Overview}
|
||||||
|
|
||||||
The module manages one specific downloadable version of the native
|
The module manages one specific downloadable version of the native
|
||||||
@tt{racket-webview-qt} package. The version is fixed in the source code through
|
@tt{racket-webview-qt} package. The version is fixed in the source code through
|
||||||
@@ -36,7 +36,7 @@ The download URL is derived from:
|
|||||||
The downloaded archive is installed below the user's addon directory in a
|
The downloaded archive is installed below the user's addon directory in a
|
||||||
subdirectory named @tt{racket-webview-qt}.
|
subdirectory named @tt{racket-webview-qt}.
|
||||||
|
|
||||||
@section[#:tag "racket-webview-downloader-installation-layout"]{Installation Layout}
|
@section{Installation Layout}
|
||||||
|
|
||||||
The installation directory is:
|
The installation directory is:
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ The OS and architecture components are derived from:
|
|||||||
(system-type 'arch)
|
(system-type 'arch)
|
||||||
]
|
]
|
||||||
|
|
||||||
@section[#:tag "racket-webview-downloader-availability-and-version-checks"]{Availability and Version Checks}
|
@section{Availability and Version Checks}
|
||||||
|
|
||||||
@defproc[(racket-webview-qt-is-available?) boolean?]{
|
@defproc[(racket-webview-qt-is-available?) boolean?]{
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ The returned path has the form:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-downloader-downloadability-checks"]{Downloadability Checks}
|
@section{Downloadability Checks}
|
||||||
|
|
||||||
@defproc[(racket-webview-qt-resolves?) boolean?]{
|
@defproc[(racket-webview-qt-resolves?) boolean?]{
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ succeeds, the port is closed immediately and the function returns
|
|||||||
@racket[#t]. Any failure is caught and results in @racket[#f].
|
@racket[#t]. Any failure is caught and results in @racket[#f].
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-downloader-downloading"]{Downloading}
|
@section{Downloading}
|
||||||
|
|
||||||
@defproc[(download-racket-webview-qt) boolean?]{
|
@defproc[(download-racket-webview-qt) boolean?]{
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ If no download port can be obtained, the function raises an exception. Otherwise
|
|||||||
on successful completion, it returns @racket[#t].
|
on successful completion, it returns @racket[#t].
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-downloader-notes"]{Notes}
|
@section{Notes}
|
||||||
|
|
||||||
The module forces HTTPS downloads by temporarily setting the current HTTPS
|
The module forces HTTPS downloads by temporarily setting the current HTTPS
|
||||||
protocol to @racket['secure] while opening the download port.
|
protocol to @racket['secure] while opening the download port.
|
||||||
|
|||||||
@@ -23,10 +23,10 @@
|
|||||||
@(define version (webview-version-string))
|
@(define version (webview-version-string))
|
||||||
|
|
||||||
|
|
||||||
@title[#:tag "racket-webview-intro"]{Racket Webview - v@version - Introduction}
|
@title{Racket Webview - v@version - Introduction}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule{racket-webview}
|
@defmodule[racket-webview]
|
||||||
|
|
||||||
@section[#:tag "racket-webview-intro-overview"]{Overview}
|
@section[#:tag "racket-webview-intro-overview"]{Overview}
|
||||||
|
|
||||||
@@ -35,13 +35,12 @@ This documentation is provided for version @bold{@version} of racket webview.
|
|||||||
Racket Webview is a class-oriented webview library built on top of a Qt-based
|
Racket Webview is a class-oriented webview library built on top of a Qt-based
|
||||||
native runtime.
|
native runtime.
|
||||||
|
|
||||||
The library is layered. At the lowest level, Racket starts a separate Qt helper
|
The library is layered. At the lowest level a native FFI layer is used. On top
|
||||||
process and exchanges line-delimited JSON messages through stdin and stdout.
|
of that, @racketmodname[racket-webview/racket-webview] provides a functional API. The
|
||||||
On top of that, @racketmodname[racket-webview/racket-webview] provides a
|
class-oriented API described in this manual is built from smaller modules on top
|
||||||
functional API. The class-oriented API described in this manual is built from
|
of that layer.
|
||||||
smaller modules on top of that layer.
|
|
||||||
|
|
||||||
@section[#:tag "racket-webview-intro-core-concepts"]{Core Concepts}
|
@section{Core Concepts}
|
||||||
|
|
||||||
The library is structured around two main concepts:
|
The library is structured around two main concepts:
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ The library is structured around two main concepts:
|
|||||||
A context manages local file serving, certificates, and settings. A window loads
|
A context manages local file serving, certificates, and settings. A window loads
|
||||||
content, handles events, and provides access to DOM elements.
|
content, handles events, and provides access to DOM elements.
|
||||||
|
|
||||||
@section[#:tag "racket-webview-intro-modules"]{Modules}
|
@section{Modules}
|
||||||
|
|
||||||
The public API is divided into the following modules:
|
The public API is divided into the following modules:
|
||||||
|
|
||||||
@@ -66,7 +65,7 @@ The public API is divided into the following modules:
|
|||||||
@item{@racketmodname[racket-webview/rgba] — RGBA color values}
|
@item{@racketmodname[racket-webview/rgba] — RGBA color values}
|
||||||
@item{@racketmodname[racket-mimetypes] — MIME type lookup}]
|
@item{@racketmodname[racket-mimetypes] — MIME type lookup}]
|
||||||
|
|
||||||
@section[#:tag "racket-webview-intro-typical-usage"]{Typical Usage}
|
@section{Typical Usage}
|
||||||
|
|
||||||
A typical application creates a @racket[wv-context%] object and then creates one
|
A typical application creates a @racket[wv-context%] object and then creates one
|
||||||
or more @racket[wv-window%] objects within that context. DOM elements are
|
or more @racket[wv-window%] objects within that context. DOM elements are
|
||||||
|
|||||||
+101
-60
@@ -1,25 +1,25 @@
|
|||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@defmodule{racket-webview/racket-webview-qt}
|
|
||||||
|
|
||||||
@title[#:tag "rktwebview-process"]{Racket Process Interface for @tt{rktwebview_prg}}
|
@title{Racket FFI Interface for @tt{rktwebview_qt}}
|
||||||
|
|
||||||
|
@defmodule[racket-webview/racket-webview-qt]
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-overview"]{Overview}
|
@section[#:tag "racket-webview-qt-overview"]{Overview}
|
||||||
|
|
||||||
The module @tt{racket-webview-qt.rkt} provides the low-level Racket interface to
|
The module @tt{racket-webview-qt.rkt} provides a Racket FFI wrapper around the
|
||||||
the Qt helper process @tt{rktwebview_prg}. It keeps the existing command numbers,
|
native @tt{rktwebview_qt} library. It loads the shared library, initializes the
|
||||||
JSON command payloads, result symbols, event strings, and public Racket
|
native runtime, and exposes Racket functions for creating and controlling
|
||||||
procedures, but no longer loads a shared library through FFI.
|
webview windows.
|
||||||
|
|
||||||
The helper process is started lazily when the first backend operation is
|
If the Qt backend is available locally, it is loaded directly. Otherwise the
|
||||||
requested. Commands are written as one JSON object per line to the process
|
module attempts to resolve and download the backend. If that is not possible,
|
||||||
stdin. Results and asynchronous events are read from stdout. Backend diagnostics
|
the module continues in a degraded mode in which a limited subset of the
|
||||||
are copied from stderr to the current Racket error port.
|
FFI entry points will only display a warning and perform a no-op. All others will fail.
|
||||||
|
|
||||||
Each command has a request identifier. The identifier is transport metadata and
|
The wrapper translates the low-level C interface into a Racket-oriented API
|
||||||
does not alter the existing command payload. It makes it possible to associate
|
based on structures, callbacks, and ordinary Racket values.
|
||||||
a result with the correct caller when multiple Racket threads use the backend.
|
|
||||||
|
|
||||||
The module provides:
|
The module provides:
|
||||||
|
|
||||||
@@ -31,39 +31,81 @@ The module provides:
|
|||||||
@item{window geometry and visibility control}
|
@item{window geometry and visibility control}
|
||||||
@item{native dialogs}
|
@item{native dialogs}
|
||||||
@item{asynchronous event delivery}
|
@item{asynchronous event delivery}
|
||||||
@item{version and cleanup utilities}]
|
@item{version and cleanup utilities}
|
||||||
|
]
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-backend-availability"]{Backend Availability}
|
@section{Requirements}
|
||||||
|
|
||||||
The environment variable @tt{RKT_WEBVIEW_PRG} may name an explicit backend
|
The native backend requires Qt version @tt{6.10.2} or newer.
|
||||||
executable. This is useful during development and testing.
|
|
||||||
|
|
||||||
Without that variable, the module uses the existing downloader and installation
|
The shared library @tt{rktwebview_qt} must therefore be built against Qt
|
||||||
directory. Availability is checked when the first backend operation is made,
|
@tt{6.10.2} or a compatible later release.
|
||||||
not while the module is loaded. If the configured backend cannot be found or
|
|
||||||
downloaded, that operation raises an exception with the reason.
|
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-module-initialization"]{Module Initialization}
|
Earlier Qt versions are not supported.
|
||||||
|
|
||||||
Starting the backend performs the following steps:
|
@section{Backend Availability}
|
||||||
|
|
||||||
|
The module first checks whether the expected @tt{racket-webview-qt} backend is
|
||||||
|
already installed.
|
||||||
|
|
||||||
|
If it is not installed, the module attempts to resolve the configured download
|
||||||
|
site. If the site can be resolved and the configured archive is downloadable,
|
||||||
|
the backend is downloaded automatically.
|
||||||
|
|
||||||
|
If the download site cannot be resolved, if no archive is available for the
|
||||||
|
current operating system and machine architecture, or if the download fails, the
|
||||||
|
module does not immediately abort module loading. Instead it switches to a
|
||||||
|
degraded mode in which native FFI loading is disabled.
|
||||||
|
|
||||||
|
In that degraded mode, a textual reason is stored internally and selected FFI
|
||||||
|
entry points are replaced by fallback implementations.
|
||||||
|
|
||||||
|
When the backend cannot be loaded, the module defines fallback implementations
|
||||||
|
for missing FFI entry points through @racket[define-ffi-definer] and
|
||||||
|
@racket[#:default-make-fail].
|
||||||
|
|
||||||
|
These fallbacks behave in two different ways.
|
||||||
|
|
||||||
|
For a small set of initialization and shutdown functions, a non-failing fallback
|
||||||
|
is installed:
|
||||||
|
|
||||||
@itemlist[#:style 'compact
|
@itemlist[#:style 'compact
|
||||||
@item{locates or downloads @tt{rktwebview_prg}}
|
@item{@racket[rkt_webview_env] returns @racket[#t]}
|
||||||
@item{sets the Qt runtime environment for the child process}
|
@item{@racket[rkt_webview_events_waiting] returns @racket[0]}
|
||||||
@item{starts the child with separate stdin, stdout, and stderr pipes}
|
@item{@racket[rkt_webview_init] returns @racket[#t]}
|
||||||
@item{starts a stdout protocol reader}
|
@item{@racket[rkt_webview_cleanup] returns @racket[#t]}]
|
||||||
@item{starts a stderr forwarding thread}
|
|
||||||
@item{waits for the protocol-version handshake}
|
|
||||||
@item{starts an independent Racket event-dispatch thread}]
|
|
||||||
|
|
||||||
Stdout is reserved for protocol records. Human-readable backend logging is
|
All other missing FFI functions raise an exception when called.
|
||||||
written only to stderr.
|
|
||||||
|
|
||||||
Currently the wrapper supports @tt{'linux} and @tt{'windows} release layouts.
|
Fallback warnings are emitted at most once per function. If native loading was
|
||||||
|
disabled because the backend was unavailable, the warning message includes the
|
||||||
|
recorded reason. If native loading was enabled but a specific symbol could not
|
||||||
|
be loaded from the library, the error names the library file.
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-data-model"]{Data Model}
|
@section{Module Initialization}
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-qt-rkt-wv-structure"]{The @tt{rkt-wv} Structure}
|
Loading the module performs several initialization steps automatically.
|
||||||
|
|
||||||
|
@itemlist[#:style 'compact
|
||||||
|
@item{determines the operating system and architecture}
|
||||||
|
@item{sets Qt runtime environment variables}
|
||||||
|
@item{loads the @tt{rktwebview_qt} shared library}
|
||||||
|
@item{initializes the native runtime}
|
||||||
|
@item{starts a background thread that processes native events}
|
||||||
|
]
|
||||||
|
|
||||||
|
Currently the wrapper supports the following platforms:
|
||||||
|
|
||||||
|
@itemlist[#:style 'compact
|
||||||
|
@item{@tt{'linux}}
|
||||||
|
@item{@tt{'windows}}
|
||||||
|
]
|
||||||
|
|
||||||
|
If the current system is unsupported, loading the module raises an error.
|
||||||
|
|
||||||
|
@section{Data Model}
|
||||||
|
|
||||||
|
@subsection{The @tt{rkt-wv} Structure}
|
||||||
|
|
||||||
Each webview window is represented by a transparent Racket structure.
|
Each webview window is represented by a transparent Racket structure.
|
||||||
|
|
||||||
@@ -95,7 +137,7 @@ an opaque handle.
|
|||||||
@;Returns the native window handle associated with @racket[wv].
|
@;Returns the native window handle associated with @racket[wv].
|
||||||
@;}
|
@;}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-http-s-contexts"]{HTTP(S) Contexts}
|
@section{HTTP(S) Contexts}
|
||||||
|
|
||||||
A context represents the shared HTTP(S) environment used by webviews.
|
A context represents the shared HTTP(S) environment used by webviews.
|
||||||
|
|
||||||
@@ -126,7 +168,7 @@ The returned context identifier can be passed to
|
|||||||
@racket[rkt-webview-create] to create webviews within that context.
|
@racket[rkt-webview-create] to create webviews within that context.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-creating-webviews"]{Creating Webviews}
|
@section{Creating Webviews}
|
||||||
|
|
||||||
@defproc[(rkt-webview-create
|
@defproc[(rkt-webview-create
|
||||||
[context exact-integer?]
|
[context exact-integer?]
|
||||||
@@ -153,15 +195,14 @@ Events generated by the native layer are delivered asynchronously through
|
|||||||
@racket[evt-callback].
|
@racket[evt-callback].
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-window-lifecycle"]{Window Lifecycle}
|
@section{Window Lifecycle}
|
||||||
|
|
||||||
@defproc[(rkt-webview-close [wv rkt-wv?]) boolean?]{
|
@defproc[(rkt-webview-close [wv rkt-wv?]) boolean?]{
|
||||||
|
|
||||||
Requests that the webview window be closed.
|
Requests that the webview window be closed.
|
||||||
|
|
||||||
The wrapper forwards the request to the helper process. The Qt-generated
|
The wrapper forwards the request to the native backend and schedules cleanup of
|
||||||
@tt{"closed"} event is delivered asynchronously, after which the Racket handle
|
the event-processing loop.
|
||||||
is removed.
|
|
||||||
|
|
||||||
Returns @racket[#t].
|
Returns @racket[#t].
|
||||||
}
|
}
|
||||||
@@ -182,7 +223,7 @@ This function is also registered with the Racket plumber so that cleanup occurs
|
|||||||
automatically when the process exits.
|
automatically when the process exits.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-window-configuration"]{Window Configuration}
|
@section{Window Configuration}
|
||||||
|
|
||||||
@defproc[(rkt-webview-set-title! [wv rkt-wv?] [title string?])
|
@defproc[(rkt-webview-set-title! [wv rkt-wv?] [title string?])
|
||||||
symbol?]{
|
symbol?]{
|
||||||
@@ -206,7 +247,7 @@ This token may be used by the native layer when accepting certain
|
|||||||
self-signed certificates.
|
self-signed certificates.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-navigation"]{Navigation}
|
@section{Navigation}
|
||||||
|
|
||||||
@defproc[(rkt-webview-set-url! [wv rkt-wv?] [url string?]) symbol?]{
|
@defproc[(rkt-webview-set-url! [wv rkt-wv?] [url string?]) symbol?]{
|
||||||
|
|
||||||
@@ -232,7 +273,7 @@ Returns a result symbol such as:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-javascript-execution"]{JavaScript Execution}
|
@section{JavaScript Execution}
|
||||||
|
|
||||||
@defproc[(rkt-webview-run-js [wv rkt-wv?] [js string?]) symbol?]{
|
@defproc[(rkt-webview-run-js [wv rkt-wv?] [js string?]) symbol?]{
|
||||||
|
|
||||||
@@ -265,7 +306,7 @@ where:
|
|||||||
The JSON structure is generated by the native backend.
|
The JSON structure is generated by the native backend.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-window-geometry"]{Window Geometry}
|
@section{Window Geometry}
|
||||||
|
|
||||||
@defproc[(rkt-webview-move [wv rkt-wv?] [x exact-integer?] [y exact-integer?])
|
@defproc[(rkt-webview-move [wv rkt-wv?] [x exact-integer?] [y exact-integer?])
|
||||||
symbol?]{
|
symbol?]{
|
||||||
@@ -322,15 +363,15 @@ Possible results:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-developer-tools"]{Developer Tools}
|
@section{Developer Tools}
|
||||||
|
|
||||||
@defproc[(rkt-webview-open-devtools [wv rkt-wv?]) symbol?]{
|
@defproc[(rkt-webview-open-devtools [wv rkt-wv?]) symbol?]{
|
||||||
Opens the browser developer tools window.
|
Opens the browser developer tools window.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-native-dialogs"]{Native Dialogs}
|
@section{Native Dialogs}
|
||||||
|
|
||||||
Dialog functions return immediately with a status code.
|
Dialog functions return immediately with a status code.
|
||||||
The user’s choice is delivered asynchronously through the event callback.
|
The user’s choice is delivered asynchronously through the event callback.
|
||||||
|
|
||||||
@defproc[(rkt-webview-choose-dir
|
@defproc[(rkt-webview-choose-dir
|
||||||
@@ -405,19 +446,19 @@ Typical event types include:
|
|||||||
|
|
||||||
The wrapper does not parse the JSON payload.
|
The wrapper does not parse the JSON payload.
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-version-information"]{Version Information}
|
@section{Version Information}
|
||||||
|
|
||||||
@defproc[(rkt-webview-version)
|
@defproc[(rkt-webview-version)
|
||||||
(list/c list? list?)]{
|
(list/c list? list?)]{
|
||||||
|
|
||||||
Returns the stdio protocol version and the expected Qt backend release.
|
Returns version information for the native backend.
|
||||||
|
|
||||||
Example result:
|
Example result:
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(list
|
(list
|
||||||
(list 'webview-stdio-api 1 0 0)
|
(list 'webview-c-api 1 0 0)
|
||||||
(list 'racket-webview-qt 0 2 3))
|
(list 'qt 6 10 2))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,16 +481,16 @@ Example result:
|
|||||||
(rkt-webview-set-url! wv "https://example.org")
|
(rkt-webview-set-url! wv "https://example.org")
|
||||||
]
|
]
|
||||||
|
|
||||||
@section[#:tag "racket-webview-qt-summary"]{Summary}
|
@section{Summary}
|
||||||
|
|
||||||
The module provides a thin Racket interface to the separate
|
The FFI module provides a thin Racket interface to the native
|
||||||
@tt{rktwebview_prg} process.
|
@tt{rktwebview_qt} backend.
|
||||||
|
|
||||||
Key characteristics:
|
Key characteristics:
|
||||||
|
|
||||||
@itemlist[#:style 'compact
|
@itemlist[#:style 'compact
|
||||||
@item{line-delimited JSON over stdin and stdout}
|
@item{thin wrapper around the native C API}
|
||||||
@item{request identifiers for concurrent synchronous calls}
|
@item{asynchronous event delivery}
|
||||||
@item{asynchronous event delivery on a separate Racket thread}
|
@item{JSON-based event payloads}
|
||||||
@item{unchanged JSON event payloads}
|
@item{simple Racket structures for webviews}
|
||||||
@item{simple Racket structures for webviews}]
|
]
|
||||||
+17
-18
@@ -3,7 +3,7 @@
|
|||||||
@(require racket/base
|
@(require racket/base
|
||||||
scribble/core)
|
scribble/core)
|
||||||
|
|
||||||
@title[#:tag "racket-webview"]{racket-webview}
|
@title[#:tag "racket-webview-functional"]{racket-webview}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/racket-webview]
|
@defmodule[racket-webview/racket-webview]
|
||||||
@@ -14,7 +14,7 @@ This module provides a structured programming model around the lower-level
|
|||||||
webview bindings. It introduces contexts, a local HTTPS server, JSON-based
|
webview bindings. It introduces contexts, a local HTTPS server, JSON-based
|
||||||
event handling, and DOM and JavaScript utilities.
|
event handling, and DOM and JavaScript utilities.
|
||||||
|
|
||||||
@section[#:tag "racket-webview-architecture"]{Architecture}
|
@section{Architecture}
|
||||||
|
|
||||||
The module builds on the lower-level bindings from
|
The module builds on the lower-level bindings from
|
||||||
@racketmodname[racket-webview/racket-webview-qt]. It adds:
|
@racketmodname[racket-webview/racket-webview-qt]. It adds:
|
||||||
@@ -76,7 +76,7 @@ This URL can be used to construct URLs from relative path information.
|
|||||||
Recognizes context values.
|
Recognizes context values.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-windows"]{Windows}
|
@section{Windows}
|
||||||
|
|
||||||
@defproc[(webview-create
|
@defproc[(webview-create
|
||||||
[context wv-context?]
|
[context wv-context?]
|
||||||
@@ -136,7 +136,7 @@ Resizes the window.
|
|||||||
Returns the current window state.
|
Returns the current window state.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-navigation-and-content"]{Navigation and Content}
|
@section{Navigation and Content}
|
||||||
|
|
||||||
@defproc[(webview-set-url! [wv wv-win?] [url (or/c string? url?)])
|
@defproc[(webview-set-url! [wv wv-win?] [url (or/c string? url?)])
|
||||||
symbol?]{
|
symbol?]{
|
||||||
@@ -176,7 +176,7 @@ The result is the symbol returned by @racket[webview-run-js].
|
|||||||
Sets the window title.
|
Sets the window title.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-javascript"]{JavaScript}
|
@section{JavaScript}
|
||||||
|
|
||||||
@defproc[(webview-run-js [wv wv-win?] [js string?]) symbol?]{
|
@defproc[(webview-run-js [wv wv-win?] [js string?]) symbol?]{
|
||||||
Evaluates JavaScript.
|
Evaluates JavaScript.
|
||||||
@@ -254,9 +254,9 @@ is indicated through the event.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@section[#:tag "racket-webview-dom-interaction"]{DOM Interaction}
|
@section{DOM Interaction}
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-selectors-and-element-identifiers"]{Selectors and Element Identifiers}
|
@subsection{Selectors and Element Identifiers}
|
||||||
|
|
||||||
Many functions accept either an element identifier or a CSS selector.
|
Many functions accept either an element identifier or a CSS selector.
|
||||||
|
|
||||||
@@ -272,7 +272,7 @@ If a string is provided, it is used directly as a CSS selector.
|
|||||||
|
|
||||||
Functions apply their effect to all matched elements.
|
Functions apply their effect to all matched elements.
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-event-binding"]{Event Binding}
|
@subsection{Event Binding}
|
||||||
|
|
||||||
@defproc[(webview-bind!
|
@defproc[(webview-bind!
|
||||||
[wv wv-win?]
|
[wv wv-win?]
|
||||||
@@ -300,7 +300,7 @@ Selector handling is the same as for @racket[webview-bind!]. The result is a
|
|||||||
list describing the removed bindings.
|
list describing the removed bindings.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-dom-values"]{DOM Values}
|
@subsection{DOM Values}
|
||||||
|
|
||||||
@defproc[(webview-set-value!
|
@defproc[(webview-set-value!
|
||||||
[wv wv-win?]
|
[wv wv-win?]
|
||||||
@@ -367,7 +367,7 @@ Returns the value converted to a datetime.
|
|||||||
Returns the value converted to a color.
|
Returns the value converted to a color.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-classes-styles"]{Classes, Styles}
|
@subsection{Classes, Styles}
|
||||||
|
|
||||||
@defproc[(webview-add-class!
|
@defproc[(webview-add-class!
|
||||||
[wv wv-win?]
|
[wv wv-win?]
|
||||||
@@ -428,7 +428,7 @@ If @racket[selector] is a symbol, the result for that single element is returned
|
|||||||
directly. Otherwise the result covers all matched elements.
|
directly. Otherwise the result covers all matched elements.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-attributes"]{Attributes}
|
@subsection{Attributes}
|
||||||
|
|
||||||
@defproc[(webview-set-attr!
|
@defproc[(webview-set-attr!
|
||||||
[wv wv-win?]
|
[wv wv-win?]
|
||||||
@@ -517,7 +517,7 @@ Returns the attribute value converted to a datetime.
|
|||||||
Returns the attribute value converted to a color.
|
Returns the attribute value converted to a color.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsection[#:tag "racket-webview-inner-html"]{Inner HTML}
|
@subsection{Inner HTML}
|
||||||
|
|
||||||
@defproc[(webview-set-innerHTML!
|
@defproc[(webview-set-innerHTML!
|
||||||
[wv wv-win?]
|
[wv wv-win?]
|
||||||
@@ -531,7 +531,7 @@ Returns @racket['oke] if the injected JavaScript yields a true value, and
|
|||||||
@racket['failed] otherwise.
|
@racket['failed] otherwise.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-file-filters"]{File Filters}
|
@section{File Filters}
|
||||||
|
|
||||||
@defstruct*[wv-permitted-exts ([name string?]
|
@defstruct*[wv-permitted-exts ([name string?]
|
||||||
[exts (listof symbol?)])]{
|
[exts (listof symbol?)])]{
|
||||||
@@ -548,7 +548,7 @@ Represents a file dialog filter entry.
|
|||||||
Recognizes lists of filter entries.
|
Recognizes lists of filter entries.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-utilities"]{Utilities}
|
@section{Utilities}
|
||||||
|
|
||||||
@defproc[(webview-default-boilerplate-js [custom-js procedure?] ...)
|
@defproc[(webview-default-boilerplate-js [custom-js procedure?] ...)
|
||||||
string?]{
|
string?]{
|
||||||
@@ -576,7 +576,7 @@ If an additional procedure is supplied, its returned string is appended to that
|
|||||||
Creates a standard file getter.
|
Creates a standard file getter.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "racket-webview-diagnostics"]{Diagnostics}
|
@section{Diagnostics}
|
||||||
|
|
||||||
@defproc[(webview-version) list?]{
|
@defproc[(webview-version) list?]{
|
||||||
Returns version information.
|
Returns version information.
|
||||||
@@ -589,8 +589,7 @@ Returns runtime information.
|
|||||||
@defproc[(webview-set-loglevel [l (or/c 'error 'warning 'info 'debug)])
|
@defproc[(webview-set-loglevel [l (or/c 'error 'warning 'info 'debug)])
|
||||||
void?]{
|
void?]{
|
||||||
|
|
||||||
Sets the Qt backend log level.
|
Sets the native log level.
|
||||||
|
|
||||||
Backend diagnostics are written to stderr. The destination reported by
|
The native log file path can be obtained via @racket[webview-info].
|
||||||
@racket[webview-info] is @tt{"stderr"}.
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
racket/string
|
racket/string
|
||||||
racket/contract))
|
racket/contract))
|
||||||
|
|
||||||
@title[#:tag "rgba"]{rgba}
|
@title{rgba}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/rgba]
|
@defmodule[racket-webview/rgba]
|
||||||
@@ -53,7 +53,7 @@ A valid alpha component is a real number in the range from @racket[0] to
|
|||||||
@racket[1], inclusive.
|
@racket[1], inclusive.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "rgba-structure-type"]{Structure Type}
|
@section{Structure Type}
|
||||||
|
|
||||||
@defstruct*[rgba ([r rgba/color?]
|
@defstruct*[rgba ([r rgba/color?]
|
||||||
[g rgba/color?]
|
[g rgba/color?]
|
||||||
|
|||||||
+49
-50
@@ -1,22 +1,21 @@
|
|||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
|
||||||
@defmodule[racket-webview/c-api]
|
@bold{Interface:} @tt{racket-webview/c-api}
|
||||||
|
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@title[#:tag "rktwebview-api"]{C API for Racket Integration}
|
@title{C API for Racket Integration}
|
||||||
|
|
||||||
This section documents the legacy C API defined in @tt{rktwebview.h} and
|
This section describes the C API exactly as defined in @tt{rktwebview.h} and
|
||||||
@tt{rktwebview_types.h}. The source remains in the backend repository for
|
@tt{rktwebview_types.h}. The API is used from Racket through FFI, but is
|
||||||
reference and comparison, but the current build no longer exposes this shared
|
documented here in its native C/C++ form.
|
||||||
library to Racket. The active integration uses the stdio process protocol.
|
|
||||||
|
|
||||||
The interface is deliberately small. Handles are integers, most operations
|
The interface is deliberately small. Handles are integers, most operations
|
||||||
return a @tt{result_t}, and structured values are returned as @tt{rkt_data_t *}.
|
return a @tt{result_t}, and structured values are returned as @tt{rkt_data_t *}.
|
||||||
The latter are caller-owned and must be released with
|
The latter are caller-owned and must be released with
|
||||||
@tt{rkt_webview_free_data()}.
|
@tt{rkt_webview_free_data()}.
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-version-export-and-basic-types"]{Version, Export, and Basic Types}
|
@section{Version, Export, and Basic Types}
|
||||||
|
|
||||||
The public API version is:
|
The public API version is:
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ typedef int rkt_wv_context_t;
|
|||||||
@tt{rktwebview_t} identifies a webview. @tt{rkt_wv_context_t} identifies a
|
@tt{rktwebview_t} identifies a webview. @tt{rkt_wv_context_t} identifies a
|
||||||
context.
|
context.
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-enums-and-structured-data"]{Enums and Structured Data}
|
@section{Enums and Structured Data}
|
||||||
|
|
||||||
The API defines the following enums: @tt{rkt_webview_loglevel_t},
|
The API defines the following enums: @tt{rkt_webview_loglevel_t},
|
||||||
@tt{result_t}, @tt{window_state_t}, @tt{rkt_messagetype_t}, and
|
@tt{result_t}, @tt{window_state_t}, @tt{rkt_messagetype_t}, and
|
||||||
@@ -92,7 +91,7 @@ typedef struct {
|
|||||||
|
|
||||||
The @tt{kind} field determines which member of @tt{data} is valid.
|
The @tt{kind} field determines which member of @tt{data} is valid.
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-ownership"]{Ownership}
|
@section{Ownership}
|
||||||
|
|
||||||
Any function returning @tt{rkt_data_t *} returns allocated memory. The caller
|
Any function returning @tt{rkt_data_t *} returns allocated memory. The caller
|
||||||
must release it using:
|
must release it using:
|
||||||
@@ -105,9 +104,9 @@ This applies to values returned by @tt{rkt_webview_info()},
|
|||||||
@tt{rkt_webview_version()}, @tt{rkt_webview_get_event()}, and
|
@tt{rkt_webview_version()}, @tt{rkt_webview_get_event()}, and
|
||||||
@tt{rkt_webview_call_js()}.
|
@tt{rkt_webview_call_js()}.
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-environment-and-runtime-control"]{Environment and Runtime Control}
|
@section{Environment and Runtime Control}
|
||||||
|
|
||||||
@defproc[(rkt_webview_env [env_cmds any/c]) void?]{
|
@defproc[#:link-target? #f (rkt_webview_env [env_cmds any/c]) void?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -121,7 +120,7 @@ form @tt{"NAME=value"}.
|
|||||||
Return value: none.
|
Return value: none.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_init) void?]{
|
@defproc[#:link-target? #f (rkt_webview_init) void?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -134,7 +133,7 @@ helper process.
|
|||||||
Return value: none.
|
Return value: none.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_cleanup) void?]{
|
@defproc[#:link-target? #f (rkt_webview_cleanup) void?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -147,7 +146,7 @@ previously returned context and webview handles are invalid.
|
|||||||
Return value: none.
|
Return value: none.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_set_loglevel [l any/c]) void?]{
|
@defproc[#:link-target? #f (rkt_webview_set_loglevel [l any/c]) void?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -159,7 +158,7 @@ Sets the backend log level.
|
|||||||
Return value: none.
|
Return value: none.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_info) any/c]{
|
@defproc[#:link-target? #f (rkt_webview_info) any/c]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -172,7 +171,7 @@ Returns a pointer to @tt{rkt_data_t}. The returned object has
|
|||||||
Return value: @tt{rkt_data_t *}, caller-owned.
|
Return value: @tt{rkt_data_t *}, caller-owned.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_version) any/c]{
|
@defproc[#:link-target? #f (rkt_webview_version) any/c]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -185,9 +184,9 @@ Returns a pointer to @tt{rkt_data_t}. The returned object has
|
|||||||
Return value: @tt{rkt_data_t *}, caller-owned.
|
Return value: @tt{rkt_data_t *}, caller-owned.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsection[#:tag "rktwebview-api-events"]{Events}
|
@subsection[#:tag "rktwebview-c-api-events"]{Events}
|
||||||
|
|
||||||
@defproc[(rkt_webview_events_waiting) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_events_waiting) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -199,7 +198,7 @@ Returns the number of events currently waiting in the event queue.
|
|||||||
Return value: @tt{int}.
|
Return value: @tt{int}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_get_event) any/c]{
|
@defproc[#:link-target? #f (rkt_webview_get_event) any/c]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -213,7 +212,7 @@ returned pointer may be null.
|
|||||||
Return value: @tt{rkt_data_t *}, caller-owned, or null.
|
Return value: @tt{rkt_data_t *}, caller-owned, or null.
|
||||||
}
|
}
|
||||||
|
|
||||||
@subsubsection[#:tag "rktwebview-api-event-polling"]{Event Polling}
|
@subsubsection{Event Polling}
|
||||||
|
|
||||||
Events are retrieved explicitly by polling. In normal use, polling should be
|
Events are retrieved explicitly by polling. In normal use, polling should be
|
||||||
performed regularly; a polling interval of about 10 ms is appropriate.
|
performed regularly; a polling interval of about 10 ms is appropriate.
|
||||||
@@ -222,9 +221,9 @@ This keeps the event queue flowing, allows asynchronous operations such as
|
|||||||
dialogs to complete in a timely manner, and avoids the impression that the system
|
dialogs to complete in a timely manner, and avoids the impression that the system
|
||||||
has stalled while the Racket side is simply not looking.
|
has stalled while the Racket side is simply not looking.
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-contexts"]{Contexts}
|
@section[#:tag "rktwebview-c-api-contexts"]{Contexts}
|
||||||
|
|
||||||
@defproc[(rkt_webview_new_context [boilerplate_js string?]
|
@defproc[#:link-target? #f (rkt_webview_new_context [boilerplate_js string?]
|
||||||
[optional_server_cert_pem string?])
|
[optional_server_cert_pem string?])
|
||||||
exact-integer?]{
|
exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
@@ -240,9 +239,9 @@ self-signed certificate.
|
|||||||
Return value: @tt{rkt_wv_context_t}.
|
Return value: @tt{rkt_wv_context_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-webviews"]{Webviews}
|
@section{Webviews}
|
||||||
|
|
||||||
@defproc[(rkt_webview_create [context exact-integer?]
|
@defproc[#:link-target? #f (rkt_webview_create [context exact-integer?]
|
||||||
[parent exact-integer?])
|
[parent exact-integer?])
|
||||||
exact-integer?]{
|
exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
@@ -258,7 +257,7 @@ otherwise it is created as a top-level window.
|
|||||||
Return value: webview handle as @tt{int}.
|
Return value: webview handle as @tt{int}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_close [wv exact-integer?]) void?]{
|
@defproc[#:link-target? #f (rkt_webview_close [wv exact-integer?]) void?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -270,7 +269,7 @@ Closes the specified webview.
|
|||||||
Return value: none.
|
Return value: none.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_valid [wv exact-integer?]) boolean?]{
|
@defproc[#:link-target? #f (rkt_webview_valid [wv exact-integer?]) boolean?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -282,7 +281,7 @@ Checks whether @tt{wv} is a valid webview handle.
|
|||||||
Return value: @tt{bool}.
|
Return value: @tt{bool}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_set_title [wv exact-integer?] [title string?])
|
@defproc[#:link-target? #f (rkt_webview_set_title [wv exact-integer?] [title string?])
|
||||||
exact-integer?]{
|
exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@@ -295,7 +294,7 @@ Sets the window title of the specified webview.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_set_ou_token [wv exact-integer?] [token string?]) void?]{
|
@defproc[#:link-target? #f (rkt_webview_set_ou_token [wv exact-integer?] [token string?]) void?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -308,9 +307,9 @@ specified webview.
|
|||||||
Return value: none.
|
Return value: none.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-navigation-and-javascript"]{Navigation and JavaScript}
|
@section{Navigation and JavaScript}
|
||||||
|
|
||||||
@defproc[(rkt_webview_set_url [wv exact-integer?] [url string?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_set_url [wv exact-integer?] [url string?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -322,7 +321,7 @@ Navigates the specified webview to the given URL.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_set_html [wv exact-integer?] [html string?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_set_html [wv exact-integer?] [html string?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -334,7 +333,7 @@ Loads raw HTML into the specified webview.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_run_js [wv exact-integer?] [js string?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_run_js [wv exact-integer?] [js string?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -346,7 +345,7 @@ Executes JavaScript asynchronously in the specified webview.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_call_js [wv exact-integer?] [js string?]) any/c]{
|
@defproc[#:link-target? #f (rkt_webview_call_js [wv exact-integer?] [js string?]) any/c]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -366,7 +365,7 @@ result string.
|
|||||||
Return value: @tt{rkt_data_t *}, caller-owned.
|
Return value: @tt{rkt_data_t *}, caller-owned.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_open_devtools [wv exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_open_devtools [wv exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -378,9 +377,9 @@ Opens developer tools for the specified webview.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-window-management"]{Window Management}
|
@section{Window Management}
|
||||||
|
|
||||||
@defproc[(rkt_webview_move [w exact-integer?] [x exact-integer?] [y exact-integer?])
|
@defproc[#:link-target? #f (rkt_webview_move [w exact-integer?] [x exact-integer?] [y exact-integer?])
|
||||||
exact-integer?]{
|
exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@@ -393,7 +392,7 @@ Moves the specified window to the given screen coordinates.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_resize [w exact-integer?]
|
@defproc[#:link-target? #f (rkt_webview_resize [w exact-integer?]
|
||||||
[width exact-integer?]
|
[width exact-integer?]
|
||||||
[height exact-integer?])
|
[height exact-integer?])
|
||||||
exact-integer?]{
|
exact-integer?]{
|
||||||
@@ -408,7 +407,7 @@ Resizes the specified window.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_hide [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_hide [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -420,7 +419,7 @@ Hides the specified window.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_show [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_show [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -432,7 +431,7 @@ Shows the specified window.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_show_normal [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_show_normal [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -444,7 +443,7 @@ Restores the specified window to its normal state.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_present [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_present [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -456,7 +455,7 @@ Presents the specified window to the user.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_maximize [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_maximize [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -468,7 +467,7 @@ Maximizes the specified window.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_minimize [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_minimize [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -480,7 +479,7 @@ Minimizes the specified window.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_window_state [w exact-integer?]) exact-integer?]{
|
@defproc[#:link-target? #f (rkt_webview_window_state [w exact-integer?]) exact-integer?]{
|
||||||
C signature:
|
C signature:
|
||||||
|
|
||||||
@verbatim{
|
@verbatim{
|
||||||
@@ -492,7 +491,7 @@ Returns the current state of the specified window.
|
|||||||
Return value: @tt{window_state_t}.
|
Return value: @tt{window_state_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-dialogs"]{Dialogs}
|
@section[#:tag "rktwebview-c-api-dialogs"]{Dialogs}
|
||||||
|
|
||||||
The dialog functions are asynchronous. They request that the dialog be opened on
|
The dialog functions are asynchronous. They request that the dialog be opened on
|
||||||
the Qt side, but do not block the Racket side while the dialog is shown. This is
|
the Qt side, but do not block the Racket side while the dialog is shown. This is
|
||||||
@@ -503,7 +502,7 @@ Completion of a dialog is therefore observed through events, not by blocking the
|
|||||||
calling thread.
|
calling thread.
|
||||||
|
|
||||||
|
|
||||||
@defproc[(rkt_webview_choose_dir [w exact-integer?]
|
@defproc[#:link-target? #f (rkt_webview_choose_dir [w exact-integer?]
|
||||||
[title string?]
|
[title string?]
|
||||||
[base_dir string?])
|
[base_dir string?])
|
||||||
exact-integer?]{
|
exact-integer?]{
|
||||||
@@ -520,7 +519,7 @@ reported through events.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_file_open [w exact-integer?]
|
@defproc[#:link-target? #f (rkt_webview_file_open [w exact-integer?]
|
||||||
[title string?]
|
[title string?]
|
||||||
[base_dir string?]
|
[base_dir string?]
|
||||||
[permitted_exts string?])
|
[permitted_exts string?])
|
||||||
@@ -538,7 +537,7 @@ reported through events.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_file_save [w exact-integer?]
|
@defproc[#:link-target? #f (rkt_webview_file_save [w exact-integer?]
|
||||||
[title string?]
|
[title string?]
|
||||||
[base_dir string?]
|
[base_dir string?]
|
||||||
[permitted_exts string?])
|
[permitted_exts string?])
|
||||||
@@ -556,7 +555,7 @@ reported through events.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(rkt_webview_message_box [w exact-integer?]
|
@defproc[#:link-target? #f (rkt_webview_message_box [w exact-integer?]
|
||||||
[title string?]
|
[title string?]
|
||||||
[message string?]
|
[message string?]
|
||||||
[submessage string?]
|
[submessage string?]
|
||||||
@@ -575,7 +574,7 @@ reported through events.
|
|||||||
Return value: @tt{result_t}.
|
Return value: @tt{result_t}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "rktwebview-api-remarks"]{Remarks}
|
@section{Remarks}
|
||||||
|
|
||||||
The API is queue-based. Commands are issued through function calls; events are
|
The API is queue-based. Commands are issued through function calls; events are
|
||||||
retrieved explicitly using @tt{rkt_webview_events_waiting()} and
|
retrieved explicitly using @tt{rkt_webview_events_waiting()} and
|
||||||
|
|||||||
@@ -1,114 +1,265 @@
|
|||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
|
@(require racket/runtime-path)
|
||||||
|
|
||||||
@defmodule{racket-webview/internals}
|
@bold{Internal component:} @tt{racket-webview/internals}
|
||||||
|
|
||||||
@title[#:tag "rktwebviewqt-internals"]{Qt WebView Backend Architecture}
|
@title{Qt WebView Backend Architecture}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-overview"]{Overview}
|
@section{Introduction}
|
||||||
|
|
||||||
The runtime consists of two processes. The Racket process owns the public API,
|
It would, of course, be preferable to place everything within a single process,
|
||||||
contexts used by the local HTTPS server, handle wrappers, and user callbacks.
|
under a unified structure. This would be elegant. It is not how things are.
|
||||||
The helper process @tt{rktwebview_prg} owns @tt{QApplication}, Qt widgets,
|
|
||||||
@tt{QWebEngineProfile} objects, and all browser state.
|
|
||||||
|
|
||||||
The processes communicate through standard pipes. There is no shared library,
|
@(define-runtime-path img-path ".")
|
||||||
shared-memory queue, or callback from native code into the Racket runtime.
|
@(define img (path->string (build-path img-path
|
||||||
|
"rktwebview-shared-memory-diagram-simple.svg"
|
||||||
|
)))
|
||||||
|
@;@(displayln (format "image: '~a'" img))
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-standard-streams"]{Standard Streams}
|
@centered{
|
||||||
|
@image[#:scale 0.45]{@img}
|
||||||
|
}
|
||||||
|
|
||||||
The streams have fixed roles:
|
Qt WebEngine establishes its own order: threads, event loops, internal state.
|
||||||
|
Once set in motion, it does not easily yield. It persists, and it expects its
|
||||||
|
environment to adapt accordingly. These conditions are accepted.
|
||||||
|
|
||||||
@itemlist[#:style 'compact
|
The Racket process, however, is of a different nature. It is light, precise,
|
||||||
@item{child stdin carries commands from Racket}
|
capable of starting and stopping without residue. It must remain so.
|
||||||
@item{child stdout carries the handshake, command results, and events}
|
|
||||||
@item{child stderr carries diagnostic logging}]
|
|
||||||
|
|
||||||
Messages on stdin and stdout are compact UTF-8 JSON objects, one object per
|
So a boundary is drawn.
|
||||||
line. Stdout must not contain ordinary logging because the Racket protocol
|
|
||||||
reader treats every value there as a protocol record.
|
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-protocol-compatibility"]{Protocol Compatibility}
|
On one side, Qt: a large, immovable instrument—something like an organ. Once it
|
||||||
|
begins to sound, it fills the space, and it is not easily silenced. On the other,
|
||||||
|
Racket: a violin, agile and expressive, able to begin and end a phrase at will.
|
||||||
|
|
||||||
The command identifiers in @tt{rkt_protocol.h} and the JSON objects used as
|
They do not become the same instrument. They are allowed to play together.
|
||||||
command payloads are unchanged. The process transport adds an outer message
|
Communication is arranged accordingly. A shared memory region, containing three
|
||||||
object with a type and, for commands and results, a request identifier.
|
queues: commands, results, and events. A command is issued. It crosses the boundary. It is taken up and executed. A result returns.
|
||||||
|
Events also arise, independently, and must be handled when they appear.
|
||||||
|
|
||||||
A command has this form:
|
Within this structure, the violin may move freely—provided it does not attempt to
|
||||||
|
reconfigure the organ. No attempt is made to unify the instruments. Such efforts would not improve the music. Instead, the composition is written so that each plays its part.
|
||||||
|
|
||||||
@verbatim|{{"type":"command","id":17,"command":6,"data":{"wv":1,"url":"https://example.test"}}}|
|
From the outside, one hears only a simple exchange: a call, a response. Internally, the balance is carefully maintained. For now, this is sufficient. And it holds.
|
||||||
|
|
||||||
A corresponding result has this form:
|
@section[#:tag "rktwebview-qt-internals-overview"]{Overview}
|
||||||
|
|
||||||
@verbatim|{{"type":"result","id":17,"result":0,"data":null}}|
|
This backend provides a webview implementation by delegating all GUI and browser
|
||||||
|
functionality to a separate Qt process.
|
||||||
|
|
||||||
The request identifier prevents concurrent calls from consuming each other's
|
The embedding Racket process does not manipulate Qt widgets directly. Instead,
|
||||||
results. It is not visible in the public API.
|
it communicates with a helper process that owns the Qt event loop and all
|
||||||
|
@tt{QWebEngine} objects.
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-command-execution"]{Command Execution}
|
This design exists to work around limitations of Qt WebEngine in combination with
|
||||||
|
the lifecycle model of the DrRacket environment.
|
||||||
|
|
||||||
A Racket caller registers a private response channel, writes a command while
|
@section{Execution Model}
|
||||||
holding the transport write lock, and waits on that channel.
|
|
||||||
|
|
||||||
The Qt process has a command-reader thread. That thread parses stdin records and
|
The runtime consists of two processes: the embedding Racket process and a helper
|
||||||
calls the existing @tt{Rktwebview_qt} methods. Those methods post commands to the
|
process running Qt and Qt WebEngine.
|
||||||
Qt GUI thread and wait for completion. The command-reader thread then writes the
|
|
||||||
result record to stdout.
|
|
||||||
|
|
||||||
The Racket stdout reader is the only thread that reads protocol output. It uses
|
All GUI state lives in the helper process. The embedding side holds no direct
|
||||||
the request identifier to forward each result to the correct waiting caller.
|
references to Qt objects. Communication is explicit and happens through shared
|
||||||
|
memory.
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-event-delivery"]{Event Delivery}
|
@section{Shared Memory and Queues}
|
||||||
|
|
||||||
Qt events remain the original JSON strings. The Qt callback converts them only
|
A shared memory region is created during initialization. Inside that region,
|
||||||
into this transport record:
|
three FIFO queues are established: a command queue, a result queue, and an event
|
||||||
|
queue.
|
||||||
|
|
||||||
@verbatim|{{"type":"event","wv":1,"data":"{...original event JSON...}"}}|
|
Each message consists of a numeric code and a payload, typically JSON:
|
||||||
|
|
||||||
The stdout reader captures the callback associated with the handle and places
|
@centerline{@tt{(code, payload)}}
|
||||||
the callback plus event string on an asynchronous Racket channel. A separate
|
|
||||||
event thread invokes user callbacks. Therefore a slow user callback cannot block
|
|
||||||
protocol parsing or prevent command results from being received.
|
|
||||||
|
|
||||||
Capturing the callback while the event record is read is important for window
|
The queues have distinct roles. The @italic{command queue} carries requests from the embedding process to the Qt process, for example creating a window, loading a URL, or executing JavaScript. The @italic{result queue} carries direct replies to those commands. A synchronous call on the embedding side blocks until a corresponding result is available. The @italic{event queue} carries asynchronous notifications generated by the Qt side, such as page load completion, navigation requests, window movement, or events
|
||||||
closure. Qt writes the @tt{"closed"} event before the result of the close command.
|
originating from JavaScript.
|
||||||
The handle can be removed immediately after the result reaches its caller, but
|
|
||||||
the already captured callback still receives the event.
|
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-contexts-and-handles"]{Contexts and Handles}
|
@section{Command Execution}
|
||||||
|
|
||||||
A context corresponds to a @tt{QWebEngineProfile} and is identified by an
|
A function call on the embedding side is translated into a command and written to
|
||||||
integer. A webview or tray icon is also identified by an integer handle. Only
|
the command queue.
|
||||||
these numbers cross the process boundary; Qt object pointers never do.
|
|
||||||
|
|
||||||
The existing Racket @tt{rkt-wv} structure is retained so the higher layers do
|
From there the flow is fixed: (1) the command is read by a worker thread in the
|
||||||
not need to change.
|
helper process, (2) it is reposted onto the Qt GUI thread, (3) the GUI thread
|
||||||
|
executes the operation, and (4) the result is written back to the result queue.
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-lifecycle-and-failure-handling"]{Lifecycle and Failure Handling}
|
The worker thread never manipulates Qt objects. All GUI work happens on the GUI
|
||||||
|
thread.
|
||||||
|
|
||||||
The backend writes a protocol-version handshake after initialization. Racket
|
From the caller’s perspective, a synchronous call returns only after the GUI
|
||||||
waits for this handshake before sending application commands.
|
thread has completed the action.
|
||||||
|
|
||||||
For normal shutdown, Racket closes open handles, sends the existing quit
|
@section[#:tag "rktwebview-qt-internals-event-delivery"]{Event Delivery}
|
||||||
command, waits for its result, closes the child's stdin, and waits for the child
|
|
||||||
process and reader threads.
|
|
||||||
|
|
||||||
If Racket terminates unexpectedly, the operating system closes the stdin pipe.
|
Many relevant events are not tied to a specific command. Page loading, navigation
|
||||||
The command-reader thread sees EOF, closes all Qt windows, and quits the Qt event
|
attempts, window movement, and JavaScript-originated events are delivered through
|
||||||
loop. This replaces the former shared-memory alive and alive-ack queues.
|
the event queue.
|
||||||
|
|
||||||
If stdout closes unexpectedly, Racket marks all handles invalid and delivers an
|
Events are retrieved explicitly by polling.
|
||||||
exception to every command still waiting for a result.
|
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-logging"]{Logging}
|
Each event contains a name, an identifier, and optional fields depending on its
|
||||||
|
type. Events are delivered in FIFO order.
|
||||||
|
|
||||||
The Qt backend writes its existing log messages to stderr. Racket forwards that
|
@section{Contexts and Webviews}
|
||||||
stream to its current error port. The @racket[rkt-webview-info] result reports
|
|
||||||
@tt{"stderr"} as the backend log destination and retains zero-valued
|
|
||||||
shared-memory metrics for compatibility with callers that display those fields.
|
|
||||||
|
|
||||||
@section[#:tag "rktwebviewqt-internals-development-override"]{Development Override}
|
The backend uses a two-level model consisting of contexts and webviews.
|
||||||
|
|
||||||
Setting @tt{RKT_WEBVIEW_PRG} to a backend executable bypasses the installed
|
A context represents a browser environment and corresponds to a
|
||||||
release directory. This makes it possible to test a newly compiled backend
|
@tt{QWebEngineProfile}. It defines how pages run, including injected scripts and optional trust configuration using explicitly trusted self-signed certificates.
|
||||||
without repackaging or changing the downloader first.
|
|
||||||
|
Each context is identified by an integer handle.
|
||||||
|
|
||||||
|
Within a context, one or more webviews can be created. A webview represents a
|
||||||
|
window containing a browser view. Webviews are also identified by integer
|
||||||
|
handles.
|
||||||
|
|
||||||
|
A webview always belongs to exactly one context. When creating a webview, the
|
||||||
|
context handle must be provided.
|
||||||
|
|
||||||
|
Webviews may optionally have a parent webview. If a parent is specified, the
|
||||||
|
resulting window is created as a modal child of that parent; otherwise it is
|
||||||
|
created as a top-level window.
|
||||||
|
|
||||||
|
From the Racket side, this means that a context must be created first. That
|
||||||
|
context handle is then used to create webviews, which are subsequently addressed
|
||||||
|
through their own handles.
|
||||||
|
|
||||||
|
All Qt objects remain internal to the helper process; only these integer handles
|
||||||
|
cross the process boundary.
|
||||||
|
|
||||||
|
@section{JavaScript Bridge}
|
||||||
|
|
||||||
|
Each context installs a small JavaScript bridge into every page, allowing
|
||||||
|
JavaScript code to send structured data to the host via:
|
||||||
|
|
||||||
|
@centerline{@tt{window.rkt_send_event(obj)}}
|
||||||
|
|
||||||
|
The objects are collected and forwarded to the event queue.
|
||||||
|
|
||||||
|
@section{Navigation and Window Behavior}
|
||||||
|
|
||||||
|
User actions are not always executed immediately; navigation initiated by the
|
||||||
|
user may result in a @tt{"navigation-request"} event instead of being followed
|
||||||
|
automatically, and closing a window may result in a @tt{"can-close?"} event. The
|
||||||
|
Racket side is expected to decide how to handle these situations.
|
||||||
|
|
||||||
|
@section{Design Considerations}
|
||||||
|
|
||||||
|
@bold{Qt WebEngine lifecycle.}
|
||||||
|
Qt WebEngine cannot be safely reinitialized within a single process.
|
||||||
|
|
||||||
|
In practice, once a @tt{QApplication} using WebEngine has been started and shut
|
||||||
|
down, the WebEngine runtime cannot be started again. This is a known and
|
||||||
|
documented limitation (see for example QTBUG-70519, QTBUG-87460,
|
||||||
|
QTBUG-145033). The underlying cause is that WebEngine starts internal threads
|
||||||
|
and resources that are not fully released, even after application shutdown.
|
||||||
|
|
||||||
|
Attempts to reinitialize WebEngine in the same process result in undefined
|
||||||
|
behavior, including crashes, hangs, or inconsistent state.
|
||||||
|
|
||||||
|
In the DrRacket environment, where components may be restarted under a
|
||||||
|
custodian, this makes an in-process design fundamentally unsuitable. Even when
|
||||||
|
libraries are loaded and unloaded using @tt{#:custodian}, the WebEngine runtime
|
||||||
|
cannot be reset to a clean state.
|
||||||
|
|
||||||
|
By moving Qt and WebEngine into a separate process, this limitation is avoided
|
||||||
|
entirely: each start of the backend creates a fresh runtime, and terminating the
|
||||||
|
helper process guarantees that all associated threads and resources are released
|
||||||
|
by the operating system.
|
||||||
|
|
||||||
|
@bold{Event loop and threading.}
|
||||||
|
Qt requires that GUI operations are performed on the Qt GUI thread.
|
||||||
|
|
||||||
|
Instead of attempting to integrate Qt’s event loop with Racket, the design
|
||||||
|
isolates Qt completely and runs it in its own process.
|
||||||
|
|
||||||
|
Within that process, a worker thread receives commands and forwards them to the
|
||||||
|
GUI thread using Qt’s event mechanism (via @tt{postEvent}). The Racket side never
|
||||||
|
interacts with Qt objects directly.
|
||||||
|
|
||||||
|
@bold{Failure isolation.}
|
||||||
|
Qt WebEngine is a large subsystem with its own internal processes (including the
|
||||||
|
Chromium-based @tt{QtWebEngineProcess}) and is generally stable in practice.
|
||||||
|
|
||||||
|
Running the Qt side in a separate process provides isolation: if the helper
|
||||||
|
process terminates, the embedding Racket process remains unaffected and can
|
||||||
|
decide how to recover.
|
||||||
|
|
||||||
|
@bold{Shared memory communication.}
|
||||||
|
The communication pattern consists of commands, results, and events, mapped onto
|
||||||
|
shared memory FIFO queues, keeping the model simple and explicit.
|
||||||
|
|
||||||
|
@bold{JSON encoding.}
|
||||||
|
All payloads are encoded as JSON, providing a natural bridge between JavaScript,
|
||||||
|
Qt/C++, and Racket: JavaScript produces JSON natively, Qt maps it to variant
|
||||||
|
types, and Racket can decode it easily.
|
||||||
|
|
||||||
|
For control commands, payload sizes are small and infrequent, so serialization
|
||||||
|
cost is negligible compared to GUI-thread execution and WebEngine processing; for
|
||||||
|
dynamic data such as JavaScript results and custom events, JSON is the
|
||||||
|
appropriate representation. A binary protocol would reduce overhead but increase
|
||||||
|
complexity and reduce inspectability.
|
||||||
|
|
||||||
|
@section{Shared Memory Architecture}
|
||||||
|
|
||||||
|
Communication between the Racket process and @tt{rktwebview_prg} is implemented
|
||||||
|
using a shared memory region. This region serves three purposes at once: it
|
||||||
|
stores shared data structures, it provides a simple allocator, and it hosts the
|
||||||
|
FIFO queues used for message passing.
|
||||||
|
|
||||||
|
At the start of the shared memory block, a small administration area is stored,
|
||||||
|
including a pointer to the current end of allocated memory, a list of active
|
||||||
|
allocations, a free list, and a fixed slot table. The slot table acts as a
|
||||||
|
directory of shared objects; queues are created once, stored in slots, and can be
|
||||||
|
retrieved by both processes using only the slot number.
|
||||||
|
|
||||||
|
Memory allocation inside the shared block is intentionally simple. Each
|
||||||
|
allocation is preceded by a small header containing size information and links
|
||||||
|
for a double-linked list. Allocation first attempts to reuse a block from the
|
||||||
|
free list; if no suitable block is available, memory is taken from the unused
|
||||||
|
tail of the region. Freed blocks are returned to the free list and may later be
|
||||||
|
reused. Blocks are not compacted or coalesced. This is not a general-purpose
|
||||||
|
heap; it is a small, predictable allocator for queue items and payload strings.
|
||||||
|
|
||||||
|
Shared objects are referenced primarily through offsets (via @tt{ShmPlace})
|
||||||
|
rather than raw pointers. This makes the layout independent of the virtual
|
||||||
|
address at which the shared memory is mapped in each process.
|
||||||
|
|
||||||
|
Queues are built directly on top of this allocator. Each queue consists of a
|
||||||
|
small header containing the first item, the last item, and a count, followed by a
|
||||||
|
linked list of queue items. Each item stores a numeric command or event code, a
|
||||||
|
pointer to its payload in shared memory, and links to neighboring items.
|
||||||
|
|
||||||
|
Synchronization is split into two layers. A shared lock protects allocator and
|
||||||
|
queue metadata, while each queue has its own semaphore indicating whether items
|
||||||
|
are available. One mechanism protects the structure; the other tells you whether
|
||||||
|
there is anything worth reading.
|
||||||
|
|
||||||
|
On POSIX systems such as Linux, shared memory is implemented using
|
||||||
|
@tt{shm_open}, @tt{ftruncate}, and @tt{mmap}, with synchronization via named
|
||||||
|
POSIX semaphores created using @tt{sem_open}. The owner process initializes these
|
||||||
|
objects and removes them again using @tt{shm_unlink} and @tt{sem_unlink}.
|
||||||
|
|
||||||
|
On Windows, the same model is implemented using @tt{CreateFileMappingA} and
|
||||||
|
@tt{MapViewOfFile} for shared memory, and @tt{CreateSemaphoreA} or
|
||||||
|
@tt{OpenSemaphoreA} for synchronization. The design is identical, but the kernel
|
||||||
|
objects follow the Windows lifetime model and are released when the last handle
|
||||||
|
is closed.
|
||||||
|
|
||||||
|
The shared memory region has a fixed size (currently 10MB) and is not resized at
|
||||||
|
runtime. Although the use of @tt{ShmPlace} offsets would in principle allow
|
||||||
|
relocation, resizing would require coordinated remapping in both processes while
|
||||||
|
all activity is paused. The current design therefore treats the region as
|
||||||
|
fixed-size and relies on reuse of freed blocks.
|
||||||
|
|
||||||
|
This implies that the communication channel is bounded. Payloads such as
|
||||||
|
@tt{set_html} or large JavaScript results must fit within the available free
|
||||||
|
space in the shared memory block. In practice, the usable limit is somewhat below
|
||||||
|
the nominal 10MB due to allocator overhead, queue administration, and concurrent
|
||||||
|
messages.
|
||||||
|
|
||||||
|
This is a message transport, not an infinite sack of HTML.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"../wv-context.rkt"
|
"../wv-context.rkt"
|
||||||
))
|
))
|
||||||
|
|
||||||
@title[#:tag "wv-context"]{wv-context}
|
@title{wv-context}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/wv-context]
|
@defmodule[racket-webview/wv-context]
|
||||||
@@ -22,7 +22,7 @@ The library is organized around two main concepts: contexts and windows.
|
|||||||
A context represents the shared runtime environment for one or more webview
|
A context represents the shared runtime environment for one or more webview
|
||||||
windows. It owns the underlying webview context, provides the base URL used by
|
windows. It owns the underlying webview context, provides the base URL used by
|
||||||
those windows, and gives access to persistent settings through
|
those windows, and gives access to persistent settings through
|
||||||
@racketmodname[wv-settings].
|
@racketmodname[racket-webview/wv-settings].
|
||||||
|
|
||||||
A context stores both JavaScript and CSS boilerplate. The JavaScript boilerplate
|
A context stores both JavaScript and CSS boilerplate. The JavaScript boilerplate
|
||||||
is passed to the native runtime, while the CSS boilerplate is injected into HTML
|
is passed to the native runtime, while the CSS boilerplate is injected into HTML
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"../wv-dialog.rkt"
|
"../wv-dialog.rkt"
|
||||||
"../wv-window.rkt"))
|
"../wv-window.rkt"))
|
||||||
|
|
||||||
@title[#:tag "wv-dialog"]{wv-dialog}
|
@title{wv-dialog}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/wv-dialog]
|
@defmodule[racket-webview/wv-dialog]
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
Dialog-window wrapper built on top of @racket[wv-window%].
|
Dialog-window wrapper built on top of @racket[wv-window%].
|
||||||
|
|
||||||
This module exports the @racket[wv-dialog%] class. It is a specialized window
|
This module exports the @racket[wv-dialog%] class. It is a specialized window
|
||||||
class whose initial size and position are derived from its parent window. Closing a dialog does not quit the application by default.
|
class whose initial size and position are derived from its parent window.
|
||||||
|
|
||||||
@section[#:tag "wv-dialog-overview"]{Overview}
|
@section[#:tag "wv-dialog-overview"]{Overview}
|
||||||
|
|
||||||
@@ -25,9 +25,9 @@ a dialog relative to its parent window.
|
|||||||
|
|
||||||
The class inherits the window lifecycle, event handling, navigation, and dialog
|
The class inherits the window lifecycle, event handling, navigation, and dialog
|
||||||
support from @racket[wv-window%]. Its only specialization in the current source
|
support from @racket[wv-window%]. Its only specialization in the current source
|
||||||
is the implementation of @racket[init-size]. A dialog also changes the inherited @racket[quit-on-close] default to @racket[#f].
|
is the implementation of @racket[init-size].
|
||||||
|
|
||||||
@section[#:tag "wv-dialog-class-wv-dialog"]{Class: wv-dialog%}
|
@section{Class: wv-dialog%}
|
||||||
|
|
||||||
@defclass[wv-dialog% wv-window% ()]{
|
@defclass[wv-dialog% wv-window% ()]{
|
||||||
|
|
||||||
@@ -37,14 +37,12 @@ The class inherits the fields @racket[parent], @racket[settings],
|
|||||||
@racket[wv-context], @racket[html-path], @racket[x], @racket[y],
|
@racket[wv-context], @racket[html-path], @racket[x], @racket[y],
|
||||||
@racket[width], and @racket[height] from @racket[wv-window%].
|
@racket[width], and @racket[height] from @racket[wv-window%].
|
||||||
|
|
||||||
@defconstructor[([quit-on-close any/c #f])]{
|
@defconstructor[()]{
|
||||||
|
|
||||||
Creates a dialog window.
|
Creates a dialog window.
|
||||||
|
|
||||||
The inherited @racket[quit-on-close] initialization argument defaults to
|
The constructor does not define additional initialization arguments of its own.
|
||||||
@racket[#f] for dialogs. Closing a dialog therefore closes only that dialog and
|
Construction is delegated to @racket[wv-window%] through @racket[super-new].
|
||||||
does not release @racket[webview-wait-for-quit]. Pass @racket[#t] explicitly
|
|
||||||
only when closing this dialog should terminate the application.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@defmethod[(init-size) any/c]{
|
@defmethod[(init-size) any/c]{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"../racket-webview.rkt"
|
"../racket-webview.rkt"
|
||||||
"../wv-element.rkt"))
|
"../wv-element.rkt"))
|
||||||
|
|
||||||
@title[#:tag "wv-element"]{wv-element}
|
@title{wv-element}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/wv-element]
|
@defmodule[racket-webview/wv-element]
|
||||||
@@ -37,7 +37,7 @@ of its methods therefore follow the contracts of those lower-level functions.
|
|||||||
The class also stores per-element event callbacks used by @racket[wv-window%]
|
The class also stores per-element event callbacks used by @racket[wv-window%]
|
||||||
when dispatching JavaScript events received from the browser.
|
when dispatching JavaScript events received from the browser.
|
||||||
|
|
||||||
@section[#:tag "wv-element-class-wv-element"]{Class: wv-element%}
|
@section{Class: wv-element%}
|
||||||
|
|
||||||
@defclass[wv-element% object% ()]{
|
@defclass[wv-element% object% ()]{
|
||||||
|
|
||||||
|
|||||||
+55
-13
@@ -11,7 +11,7 @@
|
|||||||
"../wv-input.rkt"
|
"../wv-input.rkt"
|
||||||
"../rgba.rkt"))
|
"../rgba.rkt"))
|
||||||
|
|
||||||
@title[#:tag "wv-input"]{wv-input}
|
@title{wv-input}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/wv-input]
|
@defmodule[racket-webview/wv-input]
|
||||||
@@ -39,7 +39,7 @@ lower-level DOM/value API from @racketmodname[racket-webview]. Their accepted
|
|||||||
argument shapes and result values therefore follow the contracts of those
|
argument shapes and result values therefore follow the contracts of those
|
||||||
lower-level functions.
|
lower-level functions.
|
||||||
|
|
||||||
@section[#:tag "wv-input-common-structure"]{Common Structure}
|
@section{Common Structure}
|
||||||
|
|
||||||
All input wrapper classes have the same constructor shape:
|
All input wrapper classes have the same constructor shape:
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ Each class provides two public methods:
|
|||||||
@item{@racket[get], returning the current typed value}
|
@item{@racket[get], returning the current typed value}
|
||||||
@item{@racket[set!], writing a new value through @racket[webview-set-value!]}]
|
@item{@racket[set!], writing a new value through @racket[webview-set-value!]}]
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-text"]{Class: wv-input/text%}
|
@section{Class: wv-input/text%}
|
||||||
|
|
||||||
@defclass[wv-input/text% wv-element% ()]{
|
@defclass[wv-input/text% wv-element% ()]{
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-number"]{Class: wv-input/number%}
|
@section{Class: wv-input/number%}
|
||||||
|
|
||||||
@defclass[wv-input/number% wv-element% ()]{
|
@defclass[wv-input/number% wv-element% ()]{
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-boolean"]{Class: wv-input/boolean%}
|
@section{Class: wv-input/boolean%}
|
||||||
|
|
||||||
@defclass[wv-input/boolean% wv-element% ()]{
|
@defclass[wv-input/boolean% wv-element% ()]{
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-date"]{Class: wv-input/date%}
|
@section{Class: wv-input/date%}
|
||||||
|
|
||||||
@defclass[wv-input/date% wv-element% ()]{
|
@defclass[wv-input/date% wv-element% ()]{
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-time"]{Class: wv-input/time%}
|
@section{Class: wv-input/time%}
|
||||||
|
|
||||||
@defclass[wv-input/time% wv-element% ()]{
|
@defclass[wv-input/time% wv-element% ()]{
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-datetime"]{Class: wv-input/datetime%}
|
@section{Class: wv-input/datetime%}
|
||||||
|
|
||||||
@defclass[wv-input/datetime% wv-element% ()]{
|
@defclass[wv-input/datetime% wv-element% ()]{
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-range"]{Class: wv-input/range%}
|
@section{Class: wv-input/range%}
|
||||||
|
|
||||||
@defclass[wv-input/range% wv-element% ()]{
|
@defclass[wv-input/range% wv-element% ()]{
|
||||||
|
|
||||||
@@ -287,7 +287,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-check"]{Class: wv-input/check%}
|
@section{Class: wv-input/check%}
|
||||||
|
|
||||||
@defclass[wv-input/check% wv-element% ()]{
|
@defclass[wv-input/check% wv-element% ()]{
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-radio"]{Class: wv-input/radio%}
|
@section{Class: wv-input/radio%}
|
||||||
|
|
||||||
@defclass[wv-input/radio% wv-element% ()]{
|
@defclass[wv-input/radio% wv-element% ()]{
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ Writes @racket[v] by delegating to:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-input-class-wv-input-color"]{Class: wv-input/color%}
|
@section{Class: wv-input/color%}
|
||||||
|
|
||||||
@defclass[wv-input/color% wv-element% ()]{
|
@defclass[wv-input/color% wv-element% ()]{
|
||||||
|
|
||||||
@@ -384,4 +384,46 @@ Writes @racket[v] by delegating to:
|
|||||||
(webview-set-value! wv element-id v)
|
(webview-set-value! wv element-id v)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section{Class: wv-input/select%}
|
||||||
|
|
||||||
|
@defclass[wv-input/select% wv-element% ()]{
|
||||||
|
|
||||||
|
Wrapper for an HTML select element.
|
||||||
|
|
||||||
|
@defconstructor[([window (is-a?/c wv-window%)]
|
||||||
|
[element-id symbol?])]{
|
||||||
|
|
||||||
|
Creates a select-element wrapper.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defmethod[(get) (or/c string? boolean?)]{
|
||||||
|
|
||||||
|
Returns the value of the selected option by delegating to
|
||||||
|
@racket[webview-value].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defmethod[(set! [v (or/c symbol? string? number? boolean?
|
||||||
|
g:date? g:time? g:datetime? rgba?)])
|
||||||
|
any/c]{
|
||||||
|
|
||||||
|
Selects the option whose value equals @racket[v] by delegating to
|
||||||
|
@racket[webview-set-value!].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defmethod[(set-options! [options list?]
|
||||||
|
[selected any/c #f])
|
||||||
|
any/c]{
|
||||||
|
|
||||||
|
Replaces the option elements. Each entry in @racket[options] is a list
|
||||||
|
containing a value and a label. When @racket[selected] is not @racket[#f],
|
||||||
|
the option with that value is selected.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defmethod[(on-change! [callback-func procedure?]) any/c]{
|
||||||
|
|
||||||
|
Binds @racket[callback-func] to the HTML @tt{change} event. The callback
|
||||||
|
receives the newly selected value.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
racket/file
|
racket/file
|
||||||
"../wv-settings.rkt"))
|
"../wv-settings.rkt"))
|
||||||
|
|
||||||
@title[#:tag "wv-settings"]{wv-settings}
|
@title{wv-settings}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/wv-settings]
|
@defmodule[racket-webview/wv-settings]
|
||||||
@@ -30,7 +30,7 @@ A @racket[wv-settings%] object combines:
|
|||||||
|
|
||||||
Keys used in this class are symbols.
|
Keys used in this class are symbols.
|
||||||
|
|
||||||
@section[#:tag "wv-settings-class-wv-settings"]{Class: wv-settings%}
|
@section{Class: wv-settings%}
|
||||||
|
|
||||||
@defclass[wv-settings% object% ()]{
|
@defclass[wv-settings% object% ()]{
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
"../wv-tray.rkt"
|
"../wv-tray.rkt"
|
||||||
"../menu.rkt"))
|
"../menu.rkt"))
|
||||||
|
|
||||||
@title[#:tag "wv-tray"]{Tray Icons}
|
@title{Tray Icons}
|
||||||
|
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
|
|||||||
+12
-11
@@ -15,7 +15,7 @@
|
|||||||
"../rgba.rkt"
|
"../rgba.rkt"
|
||||||
"../wv-window.rkt"))
|
"../wv-window.rkt"))
|
||||||
|
|
||||||
@title[#:tag "wv-window"]{wv-window}
|
@title{wv-window}
|
||||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||||
|
|
||||||
@defmodule[racket-webview/wv-window]
|
@defmodule[racket-webview/wv-window]
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
Window abstraction built on top of @racketmodname[racket-webview].
|
Window abstraction built on top of @racketmodname[racket-webview].
|
||||||
|
|
||||||
This module exports the @racket[wv-window%] class and re-exports the APIs from
|
This module exports the @racket[wv-window%] class and re-exports the APIs from
|
||||||
@racketmodname[wv-element], @racketmodname[wv-input], @racketmodname[rgba], and
|
@racketmodname[racket-webview/wv-element], @racketmodname[racket-webview/wv-input], @racketmodname[racket-webview/rgba], and
|
||||||
@racketmodname[wv-settings].
|
@racketmodname[racket-webview/wv-settings].
|
||||||
|
|
||||||
@section[#:tag "wv-window-overview"]{Overview}
|
@section[#:tag "wv-window-overview"]{Overview}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ element wrappers through @racket[element].
|
|||||||
The class also provides synchronous wrappers around the asynchronous dialog
|
The class also provides synchronous wrappers around the asynchronous dialog
|
||||||
interface by using continuations internally.
|
interface by using continuations internally.
|
||||||
|
|
||||||
@section[#:tag "wv-window-class-wv-window"]{Class: wv-window%}
|
@section{Class: wv-window%}
|
||||||
|
|
||||||
@defclass[wv-window% object% ()]{
|
@defclass[wv-window% object% ()]{
|
||||||
|
|
||||||
@@ -133,6 +133,7 @@ following classes is instantiated:
|
|||||||
@item{@racket[wv-input/check%] for @racket['checkbox]}
|
@item{@racket[wv-input/check%] for @racket['checkbox]}
|
||||||
@item{@racket[wv-input/radio%] for @racket['radio]}
|
@item{@racket[wv-input/radio%] for @racket['radio]}
|
||||||
@item{@racket[wv-input/color%] for @racket['color]}
|
@item{@racket[wv-input/color%] for @racket['color]}
|
||||||
|
@item{@racket[wv-input/select%] for an HTML @tt{select} element}
|
||||||
@item{@racket[wv-element%] otherwise}]
|
@item{@racket[wv-element%] otherwise}]
|
||||||
|
|
||||||
Newly created wrappers receive this window through the @racket[window] init
|
Newly created wrappers receive this window through the @racket[window] init
|
||||||
@@ -499,7 +500,7 @@ This handler reacts to the following event kinds produced by the lower layer:
|
|||||||
|
|
||||||
Unhandled events are printed to the console by the current implementation.
|
Unhandled events are printed to the console by the current implementation.
|
||||||
|
|
||||||
@section[#:tag "wv-window-function-root-file-not-found-handler"]{Function: root-file-not-found-handler}
|
@section{Function: root-file-not-found-handler}
|
||||||
|
|
||||||
@defproc[(root-file-not-found-handler [standard-file string?]
|
@defproc[(root-file-not-found-handler [standard-file string?]
|
||||||
[not-found-handler procedure?] ...)
|
[not-found-handler procedure?] ...)
|
||||||
@@ -527,14 +528,14 @@ Its behavior is as follows:
|
|||||||
Only the first optional @racket[not-found-handler] is used.
|
Only the first optional @racket[not-found-handler] is used.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section[#:tag "wv-window-re-exports"]{Re-exports}
|
@section{Re-exports}
|
||||||
|
|
||||||
This module also re-exports the public APIs from:
|
This module also re-exports the public APIs from:
|
||||||
|
|
||||||
@itemlist[#:style 'compact
|
@itemlist[#:style 'compact
|
||||||
@item{@racketmodname[wv-element]}
|
@item{@racketmodname[racket-webview/wv-element]}
|
||||||
@item{@racketmodname[wv-input]}
|
@item{@racketmodname[racket-webview/wv-input]}
|
||||||
@item{@racketmodname[rgba]}
|
@item{@racketmodname[racket-webview/rgba]}
|
||||||
@item{@racketmodname[wv-settings]}]
|
@item{@racketmodname[racket-webview/wv-settings]}]
|
||||||
|
|
||||||
It also re-exports @racket[webview-version].
|
It also re-exports @racket[webview-version].
|
||||||
|
|||||||
+1
-2
@@ -14,10 +14,9 @@
|
|||||||
|
|
||||||
(define wv-dialog%
|
(define wv-dialog%
|
||||||
(class wv-window%
|
(class wv-window%
|
||||||
(init [quit-on-close #f])
|
|
||||||
(inherit-field parent settings wv-context html-path x y width height)
|
(inherit-field parent settings wv-context html-path x y width height)
|
||||||
|
|
||||||
(super-new [quit-on-close quit-on-close])
|
(super-new)
|
||||||
|
|
||||||
(define/override (init-size)
|
(define/override (init-size)
|
||||||
(dbg-webview "init-size")
|
(dbg-webview "init-size")
|
||||||
|
|||||||
+41
-1
@@ -1,6 +1,7 @@
|
|||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
(require racket/class
|
(require racket/class
|
||||||
|
xml
|
||||||
"wv-element.rkt"
|
"wv-element.rkt"
|
||||||
"racket-webview.rkt"
|
"racket-webview.rkt"
|
||||||
)
|
)
|
||||||
@@ -14,7 +15,8 @@
|
|||||||
wv-input/range%
|
wv-input/range%
|
||||||
wv-input/check%
|
wv-input/check%
|
||||||
wv-input/radio%
|
wv-input/radio%
|
||||||
wv-input/color%)
|
wv-input/color%
|
||||||
|
wv-input/select%)
|
||||||
|
|
||||||
|
|
||||||
(define-syntax mk-cl
|
(define-syntax mk-cl
|
||||||
@@ -55,3 +57,41 @@
|
|||||||
(mk-cl wv-input/check% webview-set-value! webview-value/boolean)
|
(mk-cl wv-input/check% webview-set-value! webview-value/boolean)
|
||||||
(mk-cl wv-input/radio% webview-set-value! webview-value/boolean)
|
(mk-cl wv-input/radio% webview-set-value! webview-value/boolean)
|
||||||
(mk-cl wv-input/color% webview-set-value! webview-value/color)
|
(mk-cl wv-input/color% webview-set-value! webview-value/color)
|
||||||
|
|
||||||
|
(define wv-input/select%
|
||||||
|
(class wv-element%
|
||||||
|
(inherit-field window
|
||||||
|
element-id)
|
||||||
|
|
||||||
|
(super-new)
|
||||||
|
|
||||||
|
(define wv (hash-ref (send window info) 'wv))
|
||||||
|
|
||||||
|
(define/public (get)
|
||||||
|
(webview-value wv element-id))
|
||||||
|
|
||||||
|
(define/public (set! value)
|
||||||
|
(webview-set-value! wv element-id value))
|
||||||
|
|
||||||
|
(define/public (set-options! options [selected #f])
|
||||||
|
(send this
|
||||||
|
set-innerHTML!
|
||||||
|
(apply
|
||||||
|
string-append
|
||||||
|
(for/list ((option (in-list options)))
|
||||||
|
(let ((value (car option))
|
||||||
|
(label (cadr option)))
|
||||||
|
(xexpr->string
|
||||||
|
(list 'option
|
||||||
|
(list
|
||||||
|
(list 'value
|
||||||
|
(format "~a" value)))
|
||||||
|
(format "~a" label)))))))
|
||||||
|
(when selected
|
||||||
|
(send this set! selected)))
|
||||||
|
|
||||||
|
(define/public (on-change! callback-func)
|
||||||
|
(send window bind! element-id 'change
|
||||||
|
(λ (element event data)
|
||||||
|
(callback-func (send this get)))))
|
||||||
|
))
|
||||||
|
|||||||
+5
-3
@@ -100,7 +100,7 @@
|
|||||||
" return a;\n"
|
" return a;\n"
|
||||||
" } else { \n"
|
" } else { \n"
|
||||||
" if (a === null) {\n"
|
" if (a === null) {\n"
|
||||||
" return false;\n"
|
" return el.tagName;\n"
|
||||||
" } else {\n"
|
" } else {\n"
|
||||||
" return a;\n"
|
" return a;\n"
|
||||||
" }"
|
" }"
|
||||||
@@ -124,6 +124,7 @@
|
|||||||
((eq? type 'checkbox) wv-input/check%)
|
((eq? type 'checkbox) wv-input/check%)
|
||||||
((eq? type 'radio) wv-input/radio%)
|
((eq? type 'radio) wv-input/radio%)
|
||||||
((eq? type 'color) wv-input/color%)
|
((eq? type 'color) wv-input/color%)
|
||||||
|
((eq? type 'select) wv-input/select%)
|
||||||
((eq? type 'unknown) (begin
|
((eq? type 'unknown) (begin
|
||||||
(err-webview "Element with id '~a' not found"
|
(err-webview "Element with id '~a' not found"
|
||||||
id)
|
id)
|
||||||
@@ -383,7 +384,9 @@
|
|||||||
|
|
||||||
(define/public (bind! selector events callback . no-prevent-default)
|
(define/public (bind! selector events callback . no-prevent-default)
|
||||||
(let* ((npd (if (null? no-prevent-default) #f (car no-prevent-default)))
|
(let* ((npd (if (null? no-prevent-default) #f (car no-prevent-default)))
|
||||||
(items (webview-bind! wv selector events npd))
|
(items (or (webview-bind!
|
||||||
|
wv selector events npd)
|
||||||
|
'()))
|
||||||
(events* (if (symbol? events) (list events) events))
|
(events* (if (symbol? events) (list events) events))
|
||||||
)
|
)
|
||||||
;(dbg-webview "No-prevent-default = ~a" npd)
|
;(dbg-webview "No-prevent-default = ~a" npd)
|
||||||
@@ -602,4 +605,3 @@
|
|||||||
handler)
|
handler)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user