This comprehensive guide demonstrates how to use the Ring WebView library to build desktop applications with HTML/CSS/JavaScript frontends and Ring backends.
Every WebView application follows this basic pattern:
load "webview.ring"
# Create and configure the webview
oWebView = new WebView()
oWebView {
setTitle("My Application")
setSize(800, 600, WEBVIEW_HINT_NONE)
setHtml(`
<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body>
<h1>Hello, Ring WebView!</h1>
<button onclick="alert('Hello from JavaScript!')">Click Me</button>
</body>
</html>
`)
run()
}The WebView library uses a global configuration object that you can customize before creating instances:
# Customize global configuration before creating instances
aWebViewConfig = [
:debug = false, # Enable/disable debug mode (default: true)
:window = NULL # Parent window handle (default: NULL for new window)
]oWebView = new WebView()
# Window properties
oWebView.setTitle("My Application")
oWebView.setSize(800, 600, WEBVIEW_HINT_NONE) # width, height, hints
# Get native window handle (for platform integration)
nativeWindow = oWebView.getWindow()Set HTML content directly:
oWebView.setHtml(`
<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body>
<h1>Welcome to Ring WebView</h1>
<p>This content is generated by Ring!</p>
</body>
</html>
`)Load HTML from local files:
# Load HTML content from file
htmlContent = read("assets/index.html")
oWebView.setHtml(htmlContent)Navigate to local files:
# Navigate to a local file URL
oWebView.navigate("file:///path/to/your/file.html")Navigate to websites:
# Navigate to a URL
oWebView.navigate("https://www.example.com")Expose Ring functions to JavaScript using the bind() method:
load "webview.ring"
load "simplejson.ring"
# Global variables
oWebView = NULL
# Define functions to expose to JavaScript
aBindList = [
["sayHello", :handleGreeting],
["calculate", :doMath]
]
func main()
oWebView = new WebView()
oWebView {
setTitle("Ring-JavaScript Communication")
setSize(600, 400, WEBVIEW_HINT_NONE)
setHtml(`
<!DOCTYPE html>
<html>
<head><title>Communication Demo</title></head>
<body>
<h1>Ring-JavaScript Communication</h1>
<button onclick="callRingHello()">Say Hello</button>
<button onclick="callRingCalc()">Calculate</button>
<div id="result"></div>
<script>
async function callRingHello() {
try {
const response = await window.sayHello('World');
document.getElementById('result').innerText = response;
} catch (error) {
console.error('Error:', error);
}
}
async function callRingCalc() {
try {
const result = await window.calculate(5, 3, 'add');
document.getElementById('result').innerText = 'Result: ' + result;
} catch (error) {
console.error('Error:', error);
}
}
</script>
</body>
</html>
`)
run()
}
# Ring functions called from JavaScript
func handleGreeting(id, req)
# req is a JSON string: ["World"]
see "Greeting requested: " + req + nl
# Send response back to JavaScript
oWebView.wreturn(id, WEBVIEW_ERROR_OK, '"Hello from Ring!"')
func doMath(id, req)
# Parse arguments from JSON
aArgs = json_decode(req) # Expected format: [5, 3, "add"]
n1 = number(aArgs[1])
n2 = number(aArgs[2])
operation = aArgs[3]
switch operation
on "add"
result = n1 + n2
on "subtract"
result = n1 - n2
on "multiply"
result = n1 * n2
on "divide"
result = n1 / n2
off
oWebView.wreturn(id, WEBVIEW_ERROR_OK, string(result))Bind methods from Ring objects to JavaScript:
# Usage
oCounter = new Counter()
oWebView.bind(oCounter, [
["increment", :increment],
["decrement", :decrement],
["getValue", :getValue]
])
class Counter
value = 0
func increment(id, req)
self.value++
oWebView.wreturn(id, WEBVIEW_ERROR_OK, string(self.value))
func decrement(id, req)
self.value--
oWebView.wreturn(id, WEBVIEW_ERROR_OK, string(self.value))
func getValue(id, req)
oWebView.wreturn(id, WEBVIEW_ERROR_OK, string(self.value))Bind anonymous functions directly:
# Bind an anonymous function
oWebView.bind("showAlert", func (id, req) {
see "Anonymous function called: " + req + nl
oWebView.wreturn(id, WEBVIEW_ERROR_OK, '"Message received!"')
})The bindMany() method allows binding multiple functions at once:
# Global binding list (automatically used if it exists)
aBindList = [
["simpleFunc", :simpleFunction],
[oMyObject, [
["objMethod1", :method1],
["objMethod2", :method2]
]]
]
# Or bind manually
oWebView.bindMany(aBindList)Execute JavaScript code from Ring:
# Simple JavaScript execution
oWebView.evalJS("alert('Hello from Ring!');")
# Update DOM elements
oWebView.evalJS("document.getElementById('status').innerText = 'Updated!';")
# Call JavaScript functions
oWebView.evalJS("myJavaScriptFunction('argument');")Inject JavaScript to run before page loads:
# Inject JavaScript that runs before the main content
oWebView.injectJS("
window.myGlobalVar = 'Hello from injected JS!';
console.log('Injected JS executed');
")Execute Ring code on the main UI thread:
# Dispatch code to main thread
oWebView.dispatch("updateUI()")
func updateUI()
# This runs on the main thread
oWebView.evalJS("document.getElementById('counter').innerText = 'Updated';")JavaScript calls to Ring functions receive two parameters:
id: Callback ID for sending responses back to JavaScriptreq: JSON string containing arguments from JavaScript
func myFunction(id, req)
# Parse JSON arguments
aArgs = json_decode(req) # Converts JSON string to Ring list
# Access arguments
arg1 = aArgs[1]
arg2 = aArgs[2]
# Process the request...
# Send response back to JavaScript
oWebView.wreturn(id, WEBVIEW_ERROR_OK, '"Response from Ring"')Always use wreturn() to send responses back to JavaScript:
# String response
oWebView.wreturn(id, WEBVIEW_ERROR_OK, '"Hello from Ring!"')
# Number response
oWebView.wreturn(id, WEBVIEW_ERROR_OK, string(42))
# JSON response
oWebView.wreturn(id, WEBVIEW_ERROR_OK, '{"success": true, "data": "value"}')load "webview.ring"
oWebView = NULL
func main()
oWebView = new WebView()
oWebView {
setTitle("Counter Example")
setSize(400, 300, WEBVIEW_HINT_NONE)
# Bind counter methods
bind(new Counter, [
["increment", :increment]
])
setHtml(`
<!DOCTYPE html>
<html>
<head><title>Counter</title></head>
<body>
<h1>Counter: <span id="counter">0</span></h1>
<button onclick="window.increment()">Increment</button>
</body>
</html>
`)
run()
}
class Counter
value = 0
func increment(id, req)
self.value++
# Update UI and send response
oWebView.evalJS("document.getElementById('counter').innerText = " + string(self.value))
oWebView.wreturn(id, WEBVIEW_ERROR_OK, "")load "webview.ring"
load "simplejson.ring"
# Global variable to hold the WebView instance.
oWebView = NULL
func main()
oWebView = new WebView()
oWebView {
setTitle("Form Example")
setSize(500, 400, WEBVIEW_HINT_NONE)
bind("submitForm", :handleForm)
setHtml(`
<!DOCTYPE html>
<html>
<head><title>Contact Form</title></head>
<body>
<h1>Contact Us</h1>
<form name="contactForm">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<button type="button" onclick="submitFormData()">Submit</button>
</form>
<div id="response"></div>
<script>
async function submitFormData() {
const form = document.forms.contactForm;
const formData = [
form.name.value,
form.email.value
];
try {
const result = await window.submitForm(formData);
document.getElementById('response').innerText = result;
} catch (error) {
document.getElementById('response').innerText = 'Error: ' + error;
}
}
</script>
</body>
</html>
`)
run()
}
func handleForm(id, req)
# Parse form data from JSON
aData = json_decode(req)
? aData
name = aData[1][1]
email = aData[1][2]
see "Form submitted - Name: " + name + ", Email: " + email + nl
# Process form (save to database, send email, etc.)
oWebView.wreturn(id, WEBVIEW_ERROR_OK, '"Form submitted successfully!"')Enable debug mode to get detailed logging:
aWebViewConfig[:debug] = true