Desktop Window Tranparency Not Rendering Correctly
Problem
Dioxus fails to rerender properly when transparency is enabled in desktop mode, leaving the old material underneath the new frame.
Steps To Reproduce
use dioxus::prelude::*;
const STYLES: &'static str = r#"
#body {
display: flex;
}
p {
font-size: 100px;
}
"#;
const IPSUM: &'static str = r#"
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
"#;
const BACON: &'static str = r#"
Bacon ipsum dolor amet jowl pig pork meatball pork belly.
Beef ribs buffalo hamburger burgdoggen. Pig pancetta pastrami
shoulder fatback bacon kevin ham hock doner sirloin prosciutto
t-bone meatloaf. Sausage swine flank pork, spare ribs pork loin
ribeye beef ribs hamburger. Tongue pork chuck, biltong spare ribs
pig jerky salami frankfurter flank drumstick leberkas pork chop
pastrami jowl. Buffalo cupim alcatra tongue prosciutto, pork chop
short ribs pancetta meatloaf corned beef chicken flank sirloin
spare ribs. Boudin meatball andouille tenderloin meatloaf.
"#;
fn gui() -> Element {
let mut scene = use_signal(|| false);
let content = if scene.with(|b| *b) { IPSUM } else { BACON };
rsx! {
style { "{STYLES}" }
div {
id: "body",
onclick: move |_| {
let value = scene.with(|b| *b);
scene.set(!value);
},
p { "{content}" }
}
}
}
fn main() {
let window = dioxus_desktop::WindowBuilder::default()
.with_decorations(false)
.with_transparent(true);
let config = dioxus_desktop::Config::default().with_window(window);
LaunchBuilder::desktop().with_cfg(config).launch(gui);
}
Screenshots
Environment:
- Dioxus version: 0.6.3
- Rust version: rustc 1.85.0 (4d91de4e4 2025-02-17)
- OS info: EndeavourOS (Hyprland v0.47.1)
- App platform: Desktop
Questionnaire
I'm interested in fixing this myself but don't know where to start.
Unsure if this is the same issue but it could be related to https://github.com/DioxusLabs/dioxus/issues/3727
If you are interested in working on this, I would check if transparency works when changing the browser content in wry which is the system webview library dioxus uses.
I can reproduce this issue on macos as well which makes me think this isn't an issue with the webview itself. On macos, the outline only changes depending on if the window is focused or not:
https://github.com/user-attachments/assets/5a228ce4-3db5-4869-9cc2-093fa68d3c7e
Interesting, appreciate the follow up thanks. I'll see if I can reproduce in wry by itself and report back.
After testing the issue does occur with wry alone, even on the latest version. So this does appear to be an issue with wry. I've posted an issue there as well thanks.
https://github.com/tauri-apps/wry/issues/1524
One thing I do find odd however is this issue also seems to occur with wry v0.28 by itself which is what dioxus 0.4.3 uses but I did not have this issue with transparency when using dioxus 0.4.3 but its a problem for both dioxus 0.5.x and 0.6.x, so I'm not sure whats changed there either.
@ealmloff would you be willing to confirm that the wry example I posted in the wry issue has transparency issues for you on MacOS as well please?
I've realized that the issue is not occurring for me when running Sway rather than Hyprland on linux which are both wayland systems. I'm curious if this can be attributed to some configuration related in wry that's just conditional or if this is perhaps a DE issue.
@ealmloff would you be willing to confirm that the wry example I posted in the wry issue has transparency issues for you on MacOS as well please?
I can reproduce an issue in dioxus desktop using wry on mac, but I can't reproduce the issue with wry when changing the HTML content. The original reproduction in the issue doesn't compile on mac, so I modified it to change the content every second and use the normal webview build method:
// tao = "0.32.8"
// wry = "0.50.5"
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use wry::WebViewBuilder;
static HTML1: &'static str = r#"
<html><body><div style="font-size: 100px">
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</div></body></html>
"#;
static HTML2: &'static str = r#"
<html><body><div style="font-size: 100px">
Bacon ipsum dolor amet jowl pig pork meatball pork belly.
Beef ribs buffalo hamburger burgdoggen. Pig pancetta pastrami
shoulder fatback bacon kevin ham hock doner sirloin prosciutto
t-bone meatloaf. Sausage swine flank pork, spare ribs pork loin
ribeye beef ribs hamburger. Tongue pork chuck, biltong spare ribs
pig jerky salami frankfurter flank drumstick leberkas pork chop
pastrami jowl. Buffalo cupim alcatra tongue prosciutto, pork chop
short ribs pancetta meatloaf corned beef chicken flank sirloin
spare ribs. Boudin meatball andouille tenderloin meatloaf.
</div></body></html>
"#;
fn main() -> wry::Result<()> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_decorations(false)
.with_transparent(false)
.build(&event_loop)
.unwrap();
let builder = WebViewBuilder::new()
.with_html(HTML1)
.with_transparent(true);
let _webview = {
builder.build(&window).unwrap()
};
let mut switch = false;
let mut last_switched = std::time::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
if last_switched.elapsed().as_secs() > 1 {
last_switched = std::time::Instant::now();
switch = !switch;
let content = if switch { HTML1 } else { HTML2 };
_webview
.load_html(content)
.expect("failed to update content");
}
if let Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} = event
{
*control_flow = ControlFlow::Exit
}
});
}
Greatly appreciate it thanks! 🙏
That leaves me with even more questions lol.
Did you happen to run the latest version of wry which is 0.50.5 or 0.45.0 which is whats tagged to dioxus v0.6.3?
If you ran 0.45.0 and its working, that seems to imply the issue is with dioxus perhaps? And yet the issue for me still occurs with wry alone within Hyprland. So odd.
Greatly appreciate it thanks! 🙏 That leaves me with even more questions lol. Did you happen to run the latest version of wry which is
0.50.5or0.45.0which is whats tagged to dioxus v0.6.3?If you ran
0.45.0and its working, that seems to imply the issue is with dioxus perhaps? And yet the issue for me still occurs with wry alone within Hyprland. So odd.
I tested with 0.50.5, but it also works if I modify it to use 0.45.0:
// tao = "0.32.8"
// wry = "0.45.0"
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use wry::WebViewBuilder;
static HTML1: &'static str = r#"
<html><body><div style="font-size: 100px">
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.
</div></body></html>
"#;
static HTML2: &'static str = r#"
<html><body><div style="font-size: 100px">
Bacon ipsum dolor amet jowl pig pork meatball pork belly.
Beef ribs buffalo hamburger burgdoggen. Pig pancetta pastrami
shoulder fatback bacon kevin ham hock doner sirloin prosciutto
t-bone meatloaf. Sausage swine flank pork, spare ribs pork loin
ribeye beef ribs hamburger. Tongue pork chuck, biltong spare ribs
pig jerky salami frankfurter flank drumstick leberkas pork chop
pastrami jowl. Buffalo cupim alcatra tongue prosciutto, pork chop
short ribs pancetta meatloaf corned beef chicken flank sirloin
spare ribs. Boudin meatball andouille tenderloin meatloaf.
</div></body></html>
"#;
fn main() -> wry::Result<()> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_decorations(false)
.with_transparent(false)
.build(&event_loop)
.unwrap();
let builder = WebViewBuilder::new(&window)
.with_html(HTML1)
.with_transparent(true);
let _webview = {
builder.build().unwrap()
};
let mut switch = false;
let mut last_switched = std::time::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
if last_switched.elapsed().as_secs() > 1 {
last_switched = std::time::Instant::now();
switch = !switch;
let content = if switch { HTML1 } else { HTML2 };
_webview
.load_html(content)
.expect("failed to update content");
}
if let Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} = event
{
*control_flow = ControlFlow::Exit
}
});
}
If you ran
0.45.0and its working, that seems to imply the issue is with dioxus perhaps? And yet the issue for me still occurs with wry alone within Hyprland. So odd.
There might be a general bug with gtk rendering on linux and a more specific case dioxus is triggering for macos