【问题标题】:gtk/cairo: Only show custom shaped drawinggtk/cairo:只显示自定义形状的绘图
【发布时间】:2020-02-27 21:47:57
【问题描述】:

我只想在绘图区域内显示 cairo 绘图,但由于某种原因,绘图周围总是有一个彩色矩形背景。我怎样才能摆脱这个?我特别不想将它设置为透明(因为我可能不支持透明度),而是只显示绘图本身。

我的例子是在 Rust 中使用 gtk-rs,但其他语言的任何提示也很棒!

extern crate cairo;
extern crate gio;
extern crate gtk;

use gtk::prelude::*;

fn main() {
    gtk::init();
    show_drawing();
    gtk::main();
}

fn show_drawing() {
    let window = gtk::Window::new(gtk::WindowType::Popup);
    window.set_default_size(500i32, 500i32);

    // Make window a Notification
    window.set_type_hint(gdk::WindowTypeHint::Notification);
    window.set_position(gtk::WindowPosition::Center);

    // Add drawing
    let drawing_area = Box::new(gtk::DrawingArea::new)();
    drawing_area.connect_draw(move |_, ctx| draw(ctx));
    window.add(&drawing_area);

    window.show_all();
}

fn draw(ctx: &cairo::Context) -> gtk::Inhibit {
    ctx.scale(500f64, 500f64);

    ctx.set_source_rgba(1.0, 0.2, 0.2, 1.0);
    ctx.arc(0.5, 0.5, 0.5, 0., 3.1414 * 2.);
    ctx.fill();

    Inhibit(false)
}

我希望它只显示圆圈,并在圆圈周围查看并保持浏览器可点击。我怎样才能做到这一点?

【问题讨论】:

    标签: window gtk gtk3 cairo gtk-rs


    【解决方案1】:

    你可以试试这个:

    #include <gtk/gtk.h>
    
    GtkWidget *window;
    GtkWidget *drawing_area;
    
    gboolean
    on_draw (GtkWidget *widget,
             cairo_t *cr,
             gpointer user_data)
    {
      guint width, height;
      GdkRGBA color;
      GtkStyleContext *context;
    
      context = gtk_widget_get_style_context (widget);
    
      width = gtk_widget_get_allocated_width (widget);
      height = gtk_widget_get_allocated_height (widget);
    
      gtk_render_background (context, cr, 0, 0, width, height);
    
      cairo_arc (cr,
                 width / 2.0, height / 2.0,
                 MIN (width, height) / 2.0,
                 0, 2 * G_PI);
    
      gtk_style_context_get_color (context,
                                   gtk_style_context_get_state (context),
                                   &color);
      gdk_cairo_set_source_rgba (cr, &color);
    
      cairo_fill (cr);
    
      return FALSE;
    }
    
    gboolean on_window_button_press (GtkWidget *window,
                                     GdkEvent *event,
                                     gpointer user)
    {
      GdkEventButton *e = (GdkEventButton*) event;
    
      if (e->type == GDK_BUTTON_PRESS && e->button == 1) {
        gtk_window_begin_move_drag (GTK_WINDOW(window),
                                    e->button,
                                    e->x_root,
                                    e->y_root,
                                    e->time);
        return TRUE;
      }
      return FALSE;
    }
    
    int
    main (int argc, char **argv) {
      gtk_init (&argc, &argv);
    
      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      drawing_area = gtk_drawing_area_new ();
    
      gtk_widget_set_size_request (drawing_area, 300, 300);
      gtk_container_add (GTK_CONTAINER(window), drawing_area);
    
      g_signal_connect (drawing_area, "draw", G_CALLBACK (on_draw), NULL);
      g_signal_connect (window, "destroy", gtk_main_quit, NULL);
    
      /*we want no window decorations*/
      gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
      /*enable use of the alpha channel on window*/
      GdkScreen *screen = gtk_widget_get_screen (window);
      GdkVisual *rgba_visual = gdk_screen_get_rgba_visual (screen);
      gtk_widget_set_visual (window, rgba_visual);
      /*now set a transparent background*/
      GtkCssProvider *css = gtk_css_provider_new ();
      gtk_css_provider_load_from_data (css, "window { background-color: transparent; }", -1, NULL);
      gtk_style_context_add_provider (gtk_widget_get_style_context (window),
                                      GTK_STYLE_PROVIDER (css),
                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
      /*or, alternatively, you can set app-paintable to skip drawing the background*/
      /*gtk_widget_set_app_paintable (window, TRUE);*/
      /*drag window around by left click*/
      gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
      g_signal_connect (window,
                        "button-press-event",
                        G_CALLBACK (on_window_button_press),
                        NULL);
    
      gtk_widget_show_all (window);
      gtk_main();
    
      return 0;
    }
    

    注意:在 X11 上,这仅适用于合成窗口管理器。另一种方法是使用gdk_window_shape_combine_region(或类似功能之一)设置窗口形状。相比之下,它使用了广泛可用的 XShape 扩展。

    希望对您有所帮助! :)

    【讨论】:

    • 虽然您的示例使用合成管理器看起来不错,但它仍然遮挡了下面的窗口。您可以看到它们,但不能选择它们。 gdk_window_shape_combine_region 有效,但提供的区域只能是一组矩形形状。使用足够多的矩形来表示圆是否可行?在我看来,这不是一个好的解决方案。
    • 是的,基于像素图而不是矩形的方法会非常有用!但我认为现在不支持。对于上面的示例,在 Windows 上,透明区域上的输入会自动通过并转到下方的窗口,这是由操作系统自动完成的。而是在 X11(可能还有 Wayland?)上没有强制执行,您应该调用 gdk_window_set_opaque_region:developer.gnome.org/gdk3/stable/…
    • 不幸的是,我确实需要一个不需要复合管理器的解决方案。感谢您的链接,这可能至少对解决方法有用。 gtk2 通过使用 GdkBitmap see gtk_widget_shape_combine_mask 支持自定义形状。 GdkBitmap 在 gtk3 中被删除,但我不知道它是否被其他功能取代。
    • 有意思,不知道gtk2居然有这样的功能!好吧,这是删除它的提交:gitlab.gnome.org/GNOME/gtk/commit/a7fec8cf。提交消息非常有用:它说明您可以借助一个小实用程序轻松获得与已删除的 gtk_widget_shape_combine_mask 函数相同的功能:gdk_cairo_region_create_from_surface (developer.gnome.org/gdk3/stable/…)
    • 另外,在 gtk2 中,gdk_window_shape_combine_mask 是通过将掩码 (GdkBitmap) 转换为区域 (GdkRegion) 然后调用 shape_combine_region (gitlab.gnome.org/GNOME/gtk/blob/2.24.32/gdk/gdkwindow.c#L8641) 来实现的。 GdkRegion 实际上与 cairo_region_t 相同,它们甚至起源于相同的代码,正如您从源代码中看到的那样:gitlab.gnome.org/GNOME/gtk/blob/2.24.32/gdk/gdkregion-generic.cgitlab.freedesktop.org/cairo/cairo/blob/1.16/src/cairo-region.c
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    • 1970-01-01
    • 2018-09-20
    • 2014-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多