emilk / emilk/egui

Window transparency not working in eframe on Windows OS

Open
#4,451 25 comments 2 reactions 0 assignees View on GitHub
eframe egui-winit native-windows
Dominant language
Rust
Stars
30.6k
Forks
2.1k
Avg merge
1d 9h
Merged PRs (30d)
72

Description

### Discussed in https://github.com/emilk/egui/discussions/4446

Originally posted by **GeneralBombe** May 3, 2024
Does anyone know how i can make a window transparent and use as a overlay? I am using egui, eframe and egui_glow. if i switch everytrhing to transparent, i only have a black window, where it should be transparent.
I think the Window Building is not the problem:
```rust
fn main() -> eframe::Result<()> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).

let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_fullscreen(true)
.with_transparent(true) // Make the window transparent
.with_decorations(false),
..Default::default()
};

eframe::run_native(
"esp",
native_options,
Box::new(|cc| Box::new(RustEsp::TemplateApp::new(cc).unwrap())),
)
}

```
This is the app.rs i am using:
```rust
use std::sync::Arc;

use eframe::{egui_glow, glow};
use egui::{mutex::Mutex, text_selection::visuals, Color32, Vec2};

/// We derive Deserialize/Serialize so we can persist app state on shutdown.
//if we add new fields, give them default values when deserializing old state
pub struct TemplateApp {
// Example stuff:
rotating_triangle: Arc>,
angle: f32,
}

impl TemplateApp {
/// Called once before the first frame.
pub fn new<'a>(cc: &'a eframe::CreationContext<'a>) -> Option {
let gl = cc.gl.as_ref()?;
Some(Self {
rotating_triangle: Arc::new(Mutex::new(RotatingTriangle::new(gl)?)),
angle: 0.0,
})
}

fn custom_painting(&mut self, ui: &mut egui::Ui) {
let (rect, response) =
ui.allocate_exact_size(egui::Vec2::new(ui.available_width()/2.0, ui.available_height()/2.0), egui::Sense::drag());

self.angle += response.drag_motion().x * 0.01;
// Clone locals so we can move them into the paint callback:
let angle = self.angle;
let rotating_triangle = self.rotating_triangle.clone();

let cb = egui_glow::CallbackFn::new(move |_info, painter| {
rotating_triangle.lock().paint(painter.gl(), angle);
});

let callback = egui::PaintCallback {
rect,
callback: Arc::new(cb),
};
ui.painter().add(callback);
}
}

impl eframe::App for TemplateApp {
/// Called by the frame work to save state before shutdown.
fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] {
let u8_array = visuals.panel_fill.to_array();
// Convert each u8 value to f32
let f32_array: [f32; 4] = [
u8_array[0] as f32,
u8_array[1] as f32,
u8_array[2] as f32,
u8_array[3] as f32,
];
f32_array
}
/// Called each time the UI needs repainting, which may be many times per s
/// econd.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {

let mut style = (*ctx.style()).clone();
ctx.set_visuals(egui::Visuals {
//window_fill: egui::Color32::TRANSPARENT,
panel_fill: egui::Color32::TRANSPARENT,
..Default::default()
});


let my_frame = egui::containers::Frame {

fill: egui::Color32::TRANSPARENT,
..Default::default()
};


egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::both()
.auto_shrink(false)
.show(ui, |ui| {

egui::Frame::canvas(ui.style()).show(ui, |ui| {
self.custom_painting(ui);
});
ui.label("Drag to rotate!");

});
});
}

fn on_exit(&mut self, gl: Option<&glow::Context>) {
if let Some(gl) = gl {
self.rotating_triangle.lock().destroy(gl);
}
}
}

struct RotatingTriangle {
program: glow::Program,
vertex_array: glow::VertexArray,
}

#[allow(unsafe_code)] // we need unsafe code to use glow
impl RotatingTriangle {
fn new(gl: &glow::Context) -> Option {
use glow::HasContext as _;

let shader_version = egui_glow::ShaderVersion::get(gl);

unsafe {
let program = gl.create_program().expect("Cannot create program");

if !shader_version.is_new_shader_interface() {
log::warn!(
"Custom 3D painting hasn't been ported to {:?}",
shader_version
);
return None;
}

let (vertex_shader_source, fragment_shader_source) = (
r#"
const vec2 verts[3] = vec2[3](
vec2(0.0, 1.0),
vec2(-1.0, -1.0),
vec2(1.0, -1.0)
);
const vec4 colors[3] = vec4[3](
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0)
);
out vec4 v_color;
uniform float u_angle;
void main() {
v_color = colors[gl_VertexID];
gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0);
gl_Position.x *= cos(u_angle);
}
"#,
r#"
precision mediump float;

uniform float backgroundAlpha; // Alpha value for background transparency

in vec4 v_color;
out vec4 out_color;

void main() {
// Check if the color is black
if (all(equal(v_color.rgb, vec3(0.0)))) {
// Set alpha to backgroundAlpha for black pixels
out_color = v_color;

} else {
// Keep original color for non-black pixels#
out_color = vec4(0.0, 200.0, 0.0, backgroundAlpha);

}
}

"#,
);

let shader_sources = [
(glow::VERTEX_SHADER, vertex_shader_source),
(glow::FRAGMENT_SHADER, fragment_shader_source),
];

let shaders: Vec<_> = shader_sources
.iter()
.map(|(shader_type, shader_source)| {
let shader = gl
.create_shader(*shader_type)
.expect("Cannot create shader");
gl.shader_source(
shader,
&format!(
"{}\n{}",
shader_version.version_declaration(),
shader_source
),
);
gl.compile_shader(shader);
assert!(
gl.get_shader_compile_status(shader),
"Failed to compile custom_3d_glow {shader_type}: {}",
gl.get_shader_info_log(shader)
);

gl.attach_shader(program, shader);
shader
})
.collect();

gl.link_program(program);
assert!(
gl.get_program_link_status(program),
"{}",
gl.get_program_info_log(program)
);

for shader in shaders {
gl.detach_shader(program, shader);
gl.delete_shader(shader);
}

let vertex_array = gl
.create_vertex_array()
.expect("Cannot create vertex array");

unsafe {
gl.enable(glow::BLEND);
gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
}

Some(Self {
program,
vertex_array,
})
}
}

fn destroy(&self, gl: &glow::Context) {
use glow::HasContext as _;
unsafe {
gl.delete_program(self.program);
gl.delete_vertex_array(self.vertex_array);
}
}

fn paint(&self, gl: &glow::Context, angle: f32) {
use glow::HasContext as _;

unsafe {
//gl.clear_color(0.0, 0.0, 200.0, 0.0);
//gl.clear(glow::COLOR_BUFFER_BIT);
gl.use_program(Some(self.program));
gl.uniform_1_f32(
gl.get_uniform_location(self.program, "u_angle").as_ref(),
angle,
);


gl.bind_vertex_array(Some(self.vertex_array));
gl.draw_arrays(glow::TRIANGLES, 0, 3);


}
}
}
```
All colors work except transparent, thats why im thinking its eframe, but i really do not understand what the issue is.
Also in alt tab, it shows as transparent(i think). The eframe window is fullscreen:

![4feda3e3-d014-4e11-93c7-6e4e77fa6bfd](https://github.com/emilk/egui/assets/72839268/73b57556-c54c-4001-bba8-3191964b2a0c)

I am not sure if the issue is ony my side or not, but i'd appreciate any help!

Contributor guide

Open the contributing guide

Research direction

Start with eframe's NativeOptions and ViewportBuilder::with_transparent configuration, then inspect how clear_color and the custom egui_glow painting interact on Windows in fullscreen mode. Reproduce the reported black background while the window appears transparent in Alt-Tab; done means the background is transparent while the rendered content remains visible.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
desktop, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.