Trying to the Tauri GUI on Rust : Meet WRY

mar-m. nakamura
6 min readOct 3, 2021

--

This time, I’ll take a short detour and try WRY.

WRY is described as “Cross-platform WebView rendering library in Rust that supports all major desktop platforms like Windows, macOS, and Linux.”

Maybe it stands for Webview Rendering librarY.

I will try to display HTML using WRY and refer to files from external storage by custom protocol.

Images in various formats and locations.

It is related to the previous tauri experiment.

As always, I experiment in a Windows environment.

Create a new project

with cargo.

> cargo new wry01

and Edit Cargo.toml.

[dependencies]
wry = “0.12.2”
tao = “0.5.2”

that’s it! 🎉

Run the official example

For the time being, cut and paste the example from WRY github or Crate wry.

When executed, a window opened and the top page of TAURI’s official website was displayed.

Experiment based on this example.

Adjust the window a little.

Set the window size.
There were dpi::LogicalSize and dpi::PhysicalSize, so I’ll try with “Logical Size”.

fn main() -> wry::Result<()> {
use wry::{
application::{
dpi::LogicalSize, // Add This
:
let window = WindowBuilder::new()
.with_inner_size(LogicalSize::new(400, 600)) // and This.
:

Follow resizing.

Added webview.resize () inside the event loop.

Resize the WebView manually. This is only required on Windows because its WebView API doesn’t provide a way to resize automatically.

  :
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
:
_ => {
let _ = webview.resize(); // Modified.
}
}
});
}

Experiment to display a character string

use with_url()

I tried to pass the string of Data URI scheme to with_url() following example/dragndrop.rs. Although it is displayed, multibyte characters are URL-encoded.

fn main() -> wry::Result<()> {
let contents = r#”data:text/html;charset=UTF-8,
<!DOCTYPE html><html lang=”ja”>
<head><meta charset=”UTF-8"></head>
<body>WRYYY!<br>ウリィィィ!</body>
</html>
“#;
:
let _webview = WebViewBuilder::new(window)?
.with_url(contents)?
:
multibyte characters are URL-encoded.

Looking at the developer tools, it seems that the HTML source that was poured is already URL-encoded. I don’t know how to fix it. 😫

use with_html()

fn main() -> wry::Result<()> {
let contents = “WRYYY!<br>ウリィィィ!”;

:
let _webview = WebViewBuilder::new(window)?
.with_html(contents)?
:
Multibyte characters were also displayed correctly.

It was displayed smoothly. convenience. I like this! 😀

External text file

That means we can read it from an external file.
Create an assets folder and put html.txt .

wry01/assets/html.txtはろーウリィ😍<br>It is text file.<br>

Read with fs::read_to_string("./assets/html.txt") in main.rs .
Naturally it is displayed correctly. Same.

fn main() -> wry::Result<()> {
use std::fs;
let contents = fs::read_to_string(“./assets/html.txt”)
.expect(“File read error.”);
:
Display from a text file with HTML tags.

For example, I thought that I could prepare only the template as a text file and replace it in Rust to display it. Instead, we have to be very careful about security when viewing external files.

External file reference

I’m in trouble if I can’t refer to external files such as .js and .css from HTML.
For the sake of clarity, let’s experiment with images.

Place cat.png in the assets folder.

cat.png (I drew it.)

And edit assets/html.txt .

はろーウリィ😍<br>It is text file.<br>
cat <img src=”./assets/cat.png”>

Can not be displayed. It is not referenced in the developer tools either.

Can’t see the cat.

This can be solved by using a custom protocol.

Custom protocol

Add it according to the example/custom_protocol.rs .
Use .with_custom_protocol( ... ) as it is.

wry01/src/main.rsfn main() -> wry::Result<()> {
use std::fs::{canonicalize, read}; // Add it.
:
use wry::{
http::ResponseBuilder, // Add it.
:
let _webview = WebViewBuilder::new(window)?
// Add custom protocol handler.
.with_custom_protocol(“wry”.into(), move |request| {
let path = request.uri().replace(“wry://”, “”);
let content = read(canonicalize(&path)?)?;
let (data, meta) = if path.ends_with(“.html”) {
(content, “text/html”)
} else if path.ends_with(“.js”) {
(content, “text/javascript”)
} else if path.ends_with(“.png”) {
(content, “image/png”)
} else {
unimplemented!();
};
ResponseBuilder::new().mimetype(meta).body(data)
})
.with_html(contents)?
.build()?;
:

Now we can freely process the data with any protocol name. This is convenient.

Using the protocol name “wry” provided in the example as it is, to refer to the image. Modify it so that it starts with https://wry. .

wry01/assets/html.txtはろーウリィ😍<br>It is text file.<br>
cat <img src=”https://wry.assets/cat.png">

or https://wry../assets/cat.png .

There was a cat!

The image is displayed😀

Data format and location

Increase the corresponding Data format.

In the example, it is judged by the extension. Unsupported extensions are panicked with unimplemented!();.

Let’s add an extension judgment so that “.gif” is displayed.
Just add the condition inside with_custom_protocol( ... ).

  :
} else if path.ends_with(“.gif”) {
(content, “image/gif”)
:

Actually, the mime type of the image seems to be displayed even if it is not very correct.

  :
} else if path.ends_with(“.png”) || path.ends_with(“.gif”) {
(content, “tada!”)
:

Then place the animated GIF in the assets folder.

lum.gif (I drew it.)

And edit assets/html.txt .

はろーウリィ😍<br>It is text file.<br>
cat <img src=”https://wry.assets/cat.png">
lum <img src="https://wry.assets/lum.gif">
Lovely!

I was able to display it.

I wonder if I can do anything

With a custom protocol, we may be can do whatever we want.

Let’s create a protocol called “https://foo.” And send out an image of another storage drive, that is Out of range of the current URI.

q.png (I drew it.)

As a side note, I will also experiment with reading external css.

Edit src/main.rs .
And place “q.png” file on another storage drive somewhere. In the example, it is placed in “D:\” .

  :
let _webview = WebViewBuilder::new(window)?
.with_custom_protocol(“wry”.into(), move |request| {
:
} else if path.ends_with(“.css”) {
(content, “text/css”) // Add css data format
:
})
// Add a protocol “foo”.
.with_custom_protocol(“foo”.into(), move |_request| {
let local_img = read(“D:/q.png”)?;
ResponseBuilder::new().mimetype(“image/png”).body(local_img)
})
.with_html(contents)?
.build()?;
:

Create assets/default.css .

*{background-color: cornflowerblue; color: white;}

Edit assets/html.txt .
Added css link at the beginning and foo.q.png image at the end. I also tried changing the image size.

<head><link rel=”stylesheet” href=”https://wry.assets/default.css"></head>
はろーウリィ😍<br>It is text file.<br>
cat <img src=”https://wry.assets/cat.png">
lum <img src=”https://wry.assets/lum.gif">
<br><img src=”https://foo.q.png" width=”250"><br>Q the Spook. (Outside the normal uri range)
This is good!

Everything has been applied.😀

The code for this example is:

Misc feelings

  • I like with_html(). Generating and displaying HTML on the Rust side is close to what I want to do.
  • The custom protocol is intense.
  • There was register_uri_scheme_protocol() in the tauri similar to wry’s custom protocol.
  • As with web apps, I felt that security needs attention.
  • As with web apps, I felt that it was necessary to take measures against caching and utilize the cache.
  • I’ve been considering WebView just because I want to use an IME with a GUI, but what a hassle.😓 However, it is close to the ideal.
  • Ah javascript! I’m not sureeeee..

--

--

mar-m. nakamura

CatShanty2 Author. I want to develop a modern “3” ! But … My brain is still 8-bit / 32KB.😅