Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: async by default and native return types #25

Merged
merged 8 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
packages: libgtk-3-dev libwebkit2gtk-4.0-dev
version: 1.0
- name: Test
run: xvfb-run v test ${{ env.MOD_PATH }}/tests/
run: xvfb-run v -stats test ${{ env.MOD_PATH }}/tests/

test-sanitized:
needs: test
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ const html = '<!DOCTYPE html>
</script>
</html>'

fn my_v_func(e &webview.Event) {
fn my_v_func(e &webview.Event) string {
println('Hello from V from V!')
e.eval("console.log('Hello from V from JS!');")
str_arg := e.string(0) // Get string arg at index `0`
e.@return(str_arg + ' Hello back from V!')
return str_arg + ' Hello back from V!'
}

w := webview.create(debug: true)
Expand Down
2 changes: 1 addition & 1 deletion examples/emoji-picker/main.v
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn play_sound(_ &webview.Event) {
w := webview.create(debug: true)
w.set_title('Emoji Picker')
w.set_size(352, 435, .@none)
w.bind('play_sound', play_sound)
w.bind[voidptr]('play_sound', play_sound)
w.navigate('file://${@VMODROOT}/index.html')
w.run()
w.destroy()
45 changes: 10 additions & 35 deletions examples/project-structure/src/api.v
Original file line number Diff line number Diff line change
@@ -1,61 +1,36 @@
import webview { Event, Webview }
import time
import os

fn (mut app App) bind(w &Webview) {
// The first string arg names the functions for JS usage. E.g. use JS's `camelCase` convention if you prefer it.
w.bind('get_settings', app.get_settings)
w.bind_ctx('toggle_setting', toggle, app) // Alternatively use the ctx ptr to pass a struct.
w.bind('login', login)
w.bind('knock_knock', knock_knock)
w.bind('fetch_news', fetch_news)
}

// Returns a value when it's called from JS.
// This examples uses an `App` method, leaving the data ptr available for other potential uses.
fn (app App) get_settings(e &Event) {
e.@return(app.settings)
fn (app App) get_settings(_ &Event) Settings {
return app.settings
}

// Returns a value when it's called from JS.
// This examples uses the context argument to receive the app struct.
fn toggle(e &Event, mut app App) {
fn toggle(_ &Event, mut app App) bool {
app.settings.toggle = !app.settings.toggle
dump(app.settings.toggle)
e.@return(app.settings.toggle)
return app.settings.toggle
}

// Handles received arguments.
fn login(e &Event) {
mut status := webview.ReturnKind.error
mut resp := 'An error occurred'
defer {
e.@return(resp, kind: status)
}
fn login(e &Event) string {
name := e.string(0)
println('Hello ${name}!')
resp = 'Data received: Check your terminal.'
status = .value
return 'Data received: Check your terminal.'
}

// An operation that takes some time to process like this one
// will block the UI if it is not run from a thread.
// The `fetch_news` example below shows how to do async processing.
fn knock_knock(e &Event) {
println('Follow the white rabbit 🐇')
time.sleep(1 * time.second)
for i in 0 .. 3 {
print('\r${i + 1}...')
os.flush()
time.sleep(1 * time.second)
}
println('\rKnock, Knock, Neo.')
}

// Spawns a thread and returns a JS result from it.
// This helps to avoid interferences with the UI when calling a function that can take some time to process
// (E.g., it allows to keep updating the content and animations running in the meantime).
fn fetch_news(e &Event) {
// Use `async()` if you want to return a JS result form another thread.
spawn fetch_news_(e.async()) // `fetch_news_` function is in `news.v` file
// Performs a longer taking task.
fn fetch_news(_ &Event) Article {
// `fetch_news_()` is in the `news.v` file
return fetch_news_()
}
12 changes: 7 additions & 5 deletions examples/project-structure/src/main.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import webview

struct App {
mut:
settings struct {
mut:
toggle bool
}
settings Settings
}

struct Settings {
mut:
toggle bool
}

fn main() {
w := webview.create(debug: true)
mut app := App{
settings: struct {true}
settings: Settings{true}
}
app.bind(w)
w.set_title('V webview examples')
Expand Down
27 changes: 13 additions & 14 deletions examples/project-structure/src/news.v
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import time
import json
import rand
import webview
import net.http

struct News {
struct Article {
title string
body string
}

fn fetch_news_(e &webview.Event) {
mut result := News{}
defer {
// Artificially delay the result to simulate a function that does some extended processing.
time.sleep(time.second * 3)
e.@return(result)
}

fn fetch_news_() Article {
mut result := Article{}
resp := http.get('https://jsonplaceholder.typicode.com/posts') or {
eprintln('Failed fetching news.')
return
return result
}
news := json.decode([]News, resp.body) or {

// Further delay the return using the sleep function,
// simulating a longer taking fetch or expensive computing.
time.sleep(time.second * 2)

news := json.decode([]Article, resp.body) or {
eprintln('Failed decoding news.')
return
return result
}
// Get a random article from the articles array.
result = news[rand.int_in_range(0, news.len - 1) or { return }]
result = news[rand.int_in_range(0, news.len - 1) or { return result }]
return result
}
23 changes: 5 additions & 18 deletions examples/project-structure/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,11 @@ <h2>2. Sent data to V</h2>
<sub id="submit-info"></sub>
</section>
<section>
<h2>3. Async operations in V</h2>
<div>
<h3>3.1 [Issue]: Long processing operations block the UI</h3>
<div>
<span class="dot one">.</span>
<span class="dot two">.</span>
<span class="dot three">.</span>
</div>
<div id="animation">
<button onclick="window.knock_knock()">Knock, knock.</button>
</div>
</div>
<div>
<h3>3.2 [Solution]: Run them in a thread</h3>
<div id="article">
<h4>My Article: Simple things</h4>
<p>Breathe some air. Drink some water. Write some code.</p>
</div>
<h2>3. Async operations</h2>
<p>Return results from time extensive tasks in V without blocking the UI</p>
<div id="article">
<h4>My Article: Simple Things.</h4>
<p>Breathe some air. Drink some water. Write some code.</p>
</div>
<button onclick="fetchArticle()">Fetch another Article</button>
</section>
Expand Down
7 changes: 3 additions & 4 deletions examples/project-structure/ui/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ const submitInfo = document.getElementById('submit-info');
const heading = document.querySelector('#article h4');
const content = document.querySelector('#article p');

// V functions (in the scope of this example, prefixed with `window.`)
//
// V functions in the scope of this example are prefixed with `window.`
// The `window.` prefix is not required. But it is a common pattern in webview examples.
// For semantic reasons and to help distinguish functions as your project grows, this can be a helpful pattern.
// Just use the style and convention you prefer when naming the webview JS functions.
Expand Down Expand Up @@ -35,8 +34,8 @@ async function fetchArticle() {
}
heading.innerHTML = 'Loading';
content.innerHTML = `<span class="dot one">.</span>
<span class="dot two">.</span>
<span class="dot three">.</span>`;
<span class="dot two">.</span>
<span class="dot three">.</span>`;
// V function call
const news = await window.fetch_news();
heading.innerHTML = news.title;
Expand Down
7 changes: 7 additions & 0 deletions examples/project-structure/ui/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ section {
margin-top: 40px;
}

#article {
border: 1px solid rgba(240, 248, 255, 0.2);
border-radius: 0.5em;
margin: 10px;
padding: 0px 10px 5px;
}

.dot {
display: inline;
margin-left: 0.2em;
Expand Down
32 changes: 12 additions & 20 deletions examples/v-js-interop-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
section {
margin-top: 40px;
}
#article {
border: 1px solid rgba(240, 248, 255, 0.2);
border-radius: 0.5em;
margin: 10px;
padding: 0px 10px 5px;
}
.dot {
display: inline;
margin-left: 0.2em;
Expand Down Expand Up @@ -61,24 +67,11 @@ <h2>2. Sent data to V</h2>
<sub id="submit-info"></sub>
</section>
<section>
<h2>3. Async operations in V</h2>
<div>
<h3>3.1 [Issue]: Long processing operations block the UI</h3>
<div>
<span class="dot one">.</span>
<span class="dot two">.</span>
<span class="dot three">.</span>
</div>
<div id="animation">
<button onclick="window.knock_knock()">Knock, knock.</button>
</div>
</div>
<div>
<h3>3.2 [Solution]: Run them in a thread</h3>
<div id="article">
<h4>My Article: Simple things</h4>
<p>Breathe some air. Drink some water. Write some code.</p>
</div>
<h2>3. Async operations</h2>
<p>Return results from time extensive tasks in V without blocking the UI</p>
<div id="article">
<h4>My Article: Simple Things.</h4>
<p>Breathe some air. Drink some water. Write some code.</p>
</div>
<button onclick="fetchArticle()">Fetch another Article</button>
</section>
Expand All @@ -91,8 +84,7 @@ <h4>My Article: Simple things</h4>
const heading = document.querySelector('#article h4');
const content = document.querySelector('#article p');

// V functions (in the scope of this example, prefixed with `window.`)
//
// V functions in the scope of this example are prefixed with `window.`
// The `window.` prefix is not required. But it is a common pattern in webview examples.
// For semantic reasons and to help distinguish functions as your project grows, this can be a helpful pattern.
// Just use the style and convention you prefer when naming the webview JS functions.
Expand Down
Loading