GTK Window Background Opacity

167 Views Asked by At

We are currently working on developing a GTK application, in GJS, and want to display videos with a WebView layered on top of the video. We are able to open a window and display videos using GStreamer and open a sperate window and display a WebView using WebKit2 from WebkitGTK+.

The GStreamer window is a DrawingArea inside of an ApplicationWindow. The WebView is a WebView inside of an ApplicationWindow. The WebView window is set to always be on top of the video, but has a white background so we cannot see through the webview to the video below.

We would like to change the opacity of the window displaying the Webview to make the background completely transparent, while keeping the contents of what is being displayed in the WebView. We are able to set the document of the WebView background to be completely transparent. Is there a way to make an ApplicationWindow and WebView background transparent in GTK?

I've tried using the CSSProperties, but this did not seem to affect the window opacity.

1

There are 1 best solutions below

0
Schmaehgrunza On

Hi i also needed a transparent Gtk window.
You say you use CSS and it didnt work, i also thought CSS is not working correct in GTK 3 (3.24.33), because some of my CSS Rules didnt effect the widgets, til i recognized, it was my fault paying to less attention to the priority parameter of the "add_provider" instance method of "Gtk.StyleContext" class.
i always used 0 for priority widget.get_style_context().add_provider (cssProvider, 0); but you have to set high priority values, to make it work.
use:
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - has a value of 600
Gtk.STYLE_PROVIDER_PRIORITY_USER - has a value of 800

here my code for a window with alpha channel in GTK (3.24.33)

#!/usr/bin/gjs
"use strict";
imports.gi.versions.Gtk="3.0";
const { Gtk, Gdk } = imports.gi;
Gtk.init(null);

const window = new Gtk.Window(),
      box=new Gtk.Box ({ orientation:Gtk.Orientation.VERTICAL}),
      label=new Gtk.Label ({label:"I'm a window with alpha channel'"}),
      button=new Gtk.Button ({label:"make me intransparent", halign:Gtk.Align.CENTER}),
      cssProvider=new Gtk.CssProvider();

var styleContext;

cssProvider.load_from_data("\
   window.transparent   { background-color: rgba(0,0,0,0) }\
   window.intransparent { background-color: #FFFFFF }\
   button:hover         { background-color: #FFFF00 }\
   button.pointerdown   { background-color: #FF0000 }\
   label                { background-color: #AA99EE; padding:10px }");

styleContext=window.get_style_context();
styleContext.add_provider (cssProvider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
styleContext.add_class("transparent");

label.get_style_context().add_provider (cssProvider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
button.get_style_context().add_provider (cssProvider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);

button.connect ("button-press-event", function (button, event)
   {
   const position=window.get_position();

   if (event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS || event.get_event_type() == Gdk.EventType.TRIPLE_BUTTON_PRESS) return true;
   
   button.get_style_context().add_class("pointerdown");
   if (window.isTransparent)
      {
      window.get_style_context().add_class("intransparent");
      button.label="make me transparent";
      window.set_decorated(true);
      }
   else
      {
      window.get_style_context().remove_class("intransparent");
      button.label="make me intransparent";      
      window.set_decorated(false);
      }
   
   window.move (position[0], position[1]);
   window.isTransparent=!window.isTransparent;
   });

button.connect ("button-release-event", function (button, event)
   {
   button.get_style_context().remove_class("pointerdown");
   });

window.set_title ("window with alpha channel")
window.set_size_request (640, 480);
window.set_position (Gtk.WindowPosition.CENTER);
window.set_visual (window.get_screen().get_rgba_visual());  //-- sets the alpha channel on the window!
window.set_decorated (false);                               //-- removes decoration
window.set_gravity (Gdk.Gravity.STATIC);                    //-- set the gravity for window.move and window.get_position on window content top left corner
window.isTransparent=true;

box.add(label); box.add(button); window.add(box);
window.show_all();

Gtk.main();

but the real problem is - how to let the events pass through the window, where the window is transparent, because it is still there and catches all events.
I tried "set_pass_through" method, but didnt work.
I tried constructing a new Gdk.Event but i cant set the values like coords, root_coords and window, and so on. There are only get methods no set methods.

The only thing worked is catching the event in the window and send it unchanged to a defined receiver window (can be below or where ever), using low level function "Gtk.propagate_event"
But that means only the root coords of the event are correct and you have to manage the event propagation inside the receiver window by checking all the allocations of the child widgets, if they are under pointer and then fire on them!

#!/usr/bin/gjs
"use strict";
imports.gi.versions.Gtk="3.0";
const { Gtk, Gdk } = imports.gi;
Gtk.init(null);

const window = new Gtk.Window(),
      box=new Gtk.Box ({ orientation:Gtk.Orientation.VERTICAL}),
      label=new Gtk.Label ({label:"I'm a window with alpha channel'"}),
      button=new Gtk.Button ({label:"make me intransparent", halign:Gtk.Align.CENTER}),
      cssProvider=new Gtk.CssProvider();

var styleContext;

cssProvider.load_from_data("\
   window.transparent   { background-color: rgba(0,0,0,0) }\
   window.intransparent { background-color: #FFFFFF }\
   button:hover         { background-color: #FFFF00 }\
   button.pointerdown   { background-color: #FF0000 }\
   label                { background-color: #AA99EE; padding:10px }");

styleContext=window.get_style_context();
styleContext.add_provider (cssProvider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
styleContext.add_class("transparent");

label.get_style_context().add_provider (cssProvider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
button.get_style_context().add_provider (cssProvider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);

button.connect ("button-press-event", function (button, event)
   {
   const position=window.get_position();

   if (event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS || event.get_event_type() == Gdk.EventType.TRIPLE_BUTTON_PRESS) return true;
   
   button.get_style_context().add_class("pointerdown");
   if (window.isTransparent)
      {
      window.get_style_context().add_class("intransparent");
      button.label="make me transparent";
      window.set_decorated(true);
      }
   else
      {
      window.get_style_context().remove_class("intransparent");
      button.label="make me intransparent";      
      window.set_decorated(false);
      }
   
   window.move (position[0], position[1]);
   window.isTransparent=!window.isTransparent;
   });

button.connect ("button-release-event", function (button, event)
   {
   button.get_style_context().remove_class("pointerdown");
   });

window.set_title ("window with alpha channel")
window.set_size_request (640, 480);
window.set_position (Gtk.WindowPosition.CENTER);
window.set_visual (window.get_screen().get_rgba_visual());     //-- sets the alpha channel on the window!
window.set_decorated (false);                                  //-- removes decoration
window.set_gravity (Gdk.Gravity.STATIC);                       //-- set the gravity for window.move and window.get_position on window content top left corner
window.isTransparent=true;

box.add(label); box.add(button); window.add(box);
window.show_all();

window.lastEventInfo={ time:null, type:null };                 //-- saves the time and the type of the last event on window
window.connect("button_press_event", function (button, event)
   {
   const time=event.get_time(), type=event.get_event_type ();
   var event_target_widget, pixbuf, coords;
   
   /* signal "button-press-event" and "button-release-event" fires two times per click on window, if you click on it, and there is no widget above window in the click area, which receive these signals (descendants chain)
      e.g click on box, label - this can be stopped by returning true in the handler, which stops the signal emission, but this also will stop all later connected handlers - so i check event time and event type to eliminate it*/
   if (time == window.lastEventInfo.time && type == window.lastEventInfo.type) return;
   window.lastEventInfo= { time: time, type: type };
   
   event_target_widget=Gtk.get_event_widget (event);                //-- get the event target - the widget, which received the signal
   if (event_target_widget==window)                                 //-- if target is not window, then widget, which receive this signal must be above (e.g. button), then do nothing 
      {
      //-- target == window, e.g click on label, box or directly on window, check if pixel under pointer is transparent
      coords=event.get_coords();
      pixbuf=Gdk.pixbuf_get_from_window (window.get_window(), coords[1], coords[2], 1, 1);  //-- get pixel under pointer
      if (pixbuf.get_pixels()[3] == 0)  Gtk.propagate_event (window_receive, event);        //-- if pixel is transparent (alpha=0), send event to window_receive
      }
   });

//-- receiver window   
const window_receive=new Gtk.Window ();
var window_receive_label=new Gtk.Label ({ label:"waiting for event" }), counter=0;
window_receive.add (window_receive_label);
window_receive.set_title ("window receiver");
window_receive.set_size_request(400,200);
window_receive.connect ("button_press_event", function (button, event) { window_receive_label.set_text("count: "+(++counter)+" received event at time "+event.get_time()+" - type: "+event.get_event_type()); });
window_receive.show_all();

Gtk.main();