Skip to content

Commit 63939f9

Browse files
CianticrjwittamsJAicewizard
authored andcommitted
Transparent background for tooltips, dropdowns etc. (linebender#1583)
* Windows with transparent background * Mac support for transparency Web stub implementation to allow compilation Signed-off-by: Jari Pennanen <ciantic@oksidi.com> * add transparance on linux!!! Signed-off-by: Jari Pennanen <ciantic@oksidi.com> * Win 7 transparency fix: optionalize DCompositionCreateDevice * Transparency touch-ups Co-authored-by: Robert Wittams <robert@wittams.com> Co-authored-by: jaap aarts <jaap.aarts1@gmail.com>
1 parent 55be52b commit 63939f9

File tree

12 files changed

+335
-23
lines changed

12 files changed

+335
-23
lines changed

druid-shell/src/platform/gtk/window.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub(crate) struct WindowBuilder {
116116
min_size: Option<Size>,
117117
resizable: bool,
118118
show_titlebar: bool,
119+
transparent: bool,
119120
}
120121

121122
#[derive(Clone)]
@@ -137,6 +138,7 @@ pub(crate) struct WindowState {
137138
window: ApplicationWindow,
138139
scale: Cell<Scale>,
139140
area: Cell<ScaledArea>,
141+
is_transparent: Cell<bool>,
140142
/// Used to determine whether to honor close requests from the system: we inhibit them unless
141143
/// this is true, and this gets set to true when our client requests a close.
142144
closing: Cell<bool>,
@@ -180,6 +182,7 @@ impl WindowBuilder {
180182
min_size: None,
181183
resizable: true,
182184
show_titlebar: true,
185+
transparent: false,
183186
}
184187
}
185188

@@ -203,6 +206,10 @@ impl WindowBuilder {
203206
self.show_titlebar = show_titlebar;
204207
}
205208

209+
pub fn set_transparent(&mut self, transparent: bool) {
210+
self.transparent = transparent;
211+
}
212+
206213
pub fn set_position(&mut self, position: Point) {
207214
self.position = Some(position);
208215
}
@@ -232,7 +239,16 @@ impl WindowBuilder {
232239

233240
window.set_title(&self.title);
234241
window.set_resizable(self.resizable);
242+
window.set_app_paintable(true);
235243
window.set_decorated(self.show_titlebar);
244+
let mut can_transparent = false;
245+
if self.transparent {
246+
if let Some(screen) = window.get_screen() {
247+
let visual = screen.get_rgba_visual();
248+
can_transparent = visual.is_some();
249+
window.set_visual(visual.as_ref());
250+
}
251+
}
236252

237253
// Get the scale factor based on the GTK reported DPI
238254
let scale_factor =
@@ -254,6 +270,7 @@ impl WindowBuilder {
254270
window,
255271
scale: Cell::new(scale),
256272
area: Cell::new(area),
273+
is_transparent: Cell::new(self.transparent & can_transparent),
257274
closing: Cell::new(false),
258275
drawing_area,
259276
surface: RefCell::new(None),
@@ -409,6 +426,19 @@ impl WindowBuilder {
409426
Inhibit(false)
410427
}));
411428

429+
win_state.drawing_area.connect_screen_changed(
430+
clone!(handle => move |widget, _prev_screen| {
431+
if let Some(state) = handle.state.upgrade() {
432+
433+
if let Some(screen) = widget.get_screen(){
434+
let visual = screen.get_rgba_visual();
435+
state.is_transparent.set(visual.is_some());
436+
widget.set_visual(visual.as_ref());
437+
}
438+
}
439+
}),
440+
);
441+
412442
win_state.drawing_area.connect_button_press_event(clone!(handle => move |_widget, event| {
413443
if let Some(state) = handle.state.upgrade() {
414444
state.with_handler(|handler| {
@@ -712,7 +742,11 @@ impl WindowState {
712742
*surface = None;
713743

714744
if let Some(w) = self.drawing_area.get_window() {
715-
*surface = w.create_similar_surface(cairo::Content::Color, width, height);
745+
if self.is_transparent.get() {
746+
*surface = w.create_similar_surface(cairo::Content::ColorAlpha, width, height);
747+
} else {
748+
*surface = w.create_similar_surface(cairo::Content::Color, width, height);
749+
}
716750
if surface.is_none() {
717751
return Err(anyhow!("create_similar_surface failed"));
718752
}

druid-shell/src/platform/mac/window.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use std::time::Instant;
2424

2525
use block::ConcreteBlock;
2626
use cocoa::appkit::{
27-
CGFloat, NSApp, NSApplication, NSAutoresizingMaskOptions, NSBackingStoreBuffered, NSEvent,
28-
NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowStyleMask,
27+
CGFloat, NSApp, NSApplication, NSAutoresizingMaskOptions, NSBackingStoreBuffered, NSColor,
28+
NSEvent, NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowStyleMask,
2929
};
3030
use cocoa::base::{id, nil, BOOL, NO, YES};
3131
use cocoa::foundation::{
@@ -119,6 +119,7 @@ pub(crate) struct WindowBuilder {
119119
window_state: Option<WindowState>,
120120
resizable: bool,
121121
show_titlebar: bool,
122+
transparent: bool,
122123
}
123124

124125
#[derive(Clone)]
@@ -170,6 +171,7 @@ impl WindowBuilder {
170171
window_state: None,
171172
resizable: true,
172173
show_titlebar: true,
174+
transparent: false,
173175
}
174176
}
175177

@@ -193,6 +195,10 @@ impl WindowBuilder {
193195
self.show_titlebar = show_titlebar;
194196
}
195197

198+
pub fn set_transparent(&mut self, transparent: bool) {
199+
self.transparent = transparent;
200+
}
201+
196202
pub fn set_level(&mut self, level: WindowLevel) {
197203
self.level = Some(level);
198204
}
@@ -246,6 +252,11 @@ impl WindowBuilder {
246252
window.setContentMinSize_(size);
247253
}
248254

255+
if self.transparent {
256+
window.setOpaque_(NO);
257+
window.setBackgroundColor_(NSColor::clearColor(nil));
258+
}
259+
249260
window.setTitle_(make_nsstring(&self.title));
250261

251262
let (view, idle_queue) = make_view(self.handler.expect("view"));

druid-shell/src/platform/web/window.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,10 @@ impl WindowBuilder {
357357
// Ignored
358358
}
359359

360+
pub fn set_transparent(&mut self, _transparent: bool) {
361+
// Ignored
362+
}
363+
360364
pub fn set_position(&mut self, _position: Point) {
361365
// Ignored
362366
}

druid-shell/src/platform/windows/paint.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub(crate) unsafe fn create_render_target_dxgi(
4646
d2d_factory: &D2DFactory,
4747
swap_chain: *mut IDXGISwapChain1,
4848
scale: Scale,
49+
transparent: bool,
4950
) -> Result<DxgiSurfaceRenderTarget, Error> {
5051
let mut buffer: *mut IDXGISurface = null_mut();
5152
as_result((*swap_chain).GetBuffer(
@@ -57,7 +58,11 @@ pub(crate) unsafe fn create_render_target_dxgi(
5758
_type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
5859
pixelFormat: D2D1_PIXEL_FORMAT {
5960
format: DXGI_FORMAT_B8G8R8A8_UNORM,
60-
alphaMode: D2D1_ALPHA_MODE_IGNORE,
61+
alphaMode: if transparent {
62+
D2D1_ALPHA_MODE_PREMULTIPLIED
63+
} else {
64+
D2D1_ALPHA_MODE_IGNORE
65+
},
6166
},
6267
dpiX: (scale.x() * SCALE_TARGET_DPI) as f32,
6368
dpiY: (scale.y() * SCALE_TARGET_DPI) as f32,

druid-shell/src/platform/windows/util.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ use std::ptr;
2424
use std::slice;
2525

2626
use lazy_static::lazy_static;
27+
use winapi::ctypes::c_void;
28+
use winapi::shared::dxgi::IDXGIDevice;
29+
use winapi::shared::guiddef::REFIID;
2730
use winapi::shared::minwindef::{BOOL, HMODULE, UINT};
2831
use winapi::shared::ntdef::{HRESULT, LPWSTR};
2932
use winapi::shared::windef::{HMONITOR, HWND, RECT};
@@ -147,6 +150,11 @@ type GetSystemMetricsForDpi =
147150
// from shcore.dll
148151
type GetDpiForMonitor = unsafe extern "system" fn(HMONITOR, MONITOR_DPI_TYPE, *mut UINT, *mut UINT);
149152
type SetProcessDpiAwareness = unsafe extern "system" fn(PROCESS_DPI_AWARENESS) -> HRESULT;
153+
type DCompositionCreateDevice = unsafe extern "system" fn(
154+
dxgiDevice: *const IDXGIDevice,
155+
iid: REFIID,
156+
dcompositionDevice: *mut *mut c_void,
157+
) -> HRESULT;
150158

151159
#[allow(non_snake_case)] // For member fields
152160
pub struct OptionalFunctions {
@@ -156,6 +164,7 @@ pub struct OptionalFunctions {
156164
pub GetDpiForMonitor: Option<GetDpiForMonitor>,
157165
pub SetProcessDpiAwareness: Option<SetProcessDpiAwareness>,
158166
pub GetSystemMetricsForDpi: Option<GetSystemMetricsForDpi>,
167+
pub DCompositionCreateDevice: Option<DCompositionCreateDevice>,
159168
}
160169

161170
#[allow(non_snake_case)] // For local variables
@@ -201,13 +210,15 @@ fn load_optional_functions() -> OptionalFunctions {
201210

202211
let shcore = load_library("shcore.dll");
203212
let user32 = load_library("user32.dll");
213+
let dcomp = load_library("dcomp.dll");
204214

205215
let mut GetDpiForSystem = None;
206216
let mut GetDpiForMonitor = None;
207217
let mut GetDpiForWindow = None;
208218
let mut SetProcessDpiAwarenessContext = None;
209219
let mut SetProcessDpiAwareness = None;
210220
let mut GetSystemMetricsForDpi = None;
221+
let mut DCompositionCreateDevice = None;
211222

212223
if shcore.is_null() {
213224
tracing::info!("No shcore.dll");
@@ -225,13 +236,20 @@ fn load_optional_functions() -> OptionalFunctions {
225236
load_function!(user32, GetSystemMetricsForDpi, "10");
226237
}
227238

239+
if dcomp.is_null() {
240+
tracing::info!("No dcomp.dll");
241+
} else {
242+
load_function!(dcomp, DCompositionCreateDevice, "8.1");
243+
}
244+
228245
OptionalFunctions {
229246
GetDpiForSystem,
230247
GetDpiForWindow,
231248
SetProcessDpiAwarenessContext,
232249
GetDpiForMonitor,
233250
SetProcessDpiAwareness,
234251
GetSystemMetricsForDpi,
252+
DCompositionCreateDevice,
235253
}
236254
}
237255

0 commit comments

Comments
 (0)