John bracciano
John bracciano

Reputation: 81

Custom GTK widget with Vala

I am trying to create a simple custom GTK widget using Vala:

public class PageView : Gtk.Widget {

    public PageView () {
        //base ();
        set_name ("pageview");
        set_has_window (true);
    }


    /*
     *              Method and Signal Overrides
     */

    public override Gtk.SizeRequestMode get_request_mode () {
        return Gtk.SizeRequestMode.CONSTANT_SIZE;
            // Don’t trade height-for-width or width-for-height
    }

    public override void get_preferred_width
                                               (out int minimum,
                                                out int natural) {
        stdout.printf ("PageView#get_preferred_width\n");
        minimum = natural = pg_pixel_width;
    }

    public override void get_preferred_height
                                               (out int minimum,
                                                out int natural) {
        stdout.printf ("PageView#get_preferred_height\n");
        minimum = natural = pg_pixel_height;
    }

    public override void get_preferred_width_for_height
                                               (    int height,
                                                out int minimum,
                                                out int natural) {
        stdout.printf ("PageView#get_preferred_width_for_height\n");
        minimum = natural = pg_pixel_width;
    }

    public override void get_preferred_height_for_width
                                               (    int width,
                                                out int minimum,
                                                out int natural) {
        stdout.printf ("PageView#get_preferred_height_for_width\n");
        minimum = natural = pg_pixel_height;
    }


    public override void size_allocate (Gtk.Allocation alloc) {
        stdout.printf ("PageView#size_allocate\n");

        set_allocation (alloc);

        if (get_window() != null) {
            get_window().move_resize (
                alloc.x, alloc.y, alloc.width, alloc.height);
        }
    }

    public override void realize () {
        stdout.printf ("PageView#realize\n");

        set_realized (true);

        if (get_window() == null) {
            Gtk.Allocation allocation;
            get_allocation (out allocation);
            _window_attr = Gdk.WindowAttr () {
                x = allocation.x,
                y = allocation.y,
                width = allocation.width,
                height = allocation.height,
                event_mask = get_events() | Gdk.EventMask.EXPOSURE_MASK,
                window_type = Gdk.WindowType.CHILD,
                wclass = Gdk.WindowWindowClass.INPUT_OUTPUT
            };
            _window_attr_type =  Gdk.WindowAttributesType.X |
                                 Gdk.WindowAttributesType.Y;

            _window = new Gdk.Window (
                get_parent_window (), _window_attr, _window_attr_type);
            set_window (_window);
        }
    }

    public override void unrealize () {
        stdout.printf ("PageView#unrealize\n");
    }


    public override bool draw (Cairo.Context cr) {
        stdout.printf ("PageView#draw\n");

        Gtk.Allocation allocation;
        get_allocation (out allocation);
        get_style_context () .render_background (cr,
            allocation.x,     allocation.y,
            allocation.width, allocation.height);

        cr.save ();
            cr.scale (allocation.width, allocation.height);
            cr.set_source_rgba (255, 255, 0, 1.0);
            cr.set_line_width (10);
            cr.line_to (1, 1);
            cr.stroke ();
        cr.restore ();


        return true;
    }


    /*
     *                  Public Attributes
     */

    public int pg_pixel_width {
        get { return 480; }
    }

    public int pg_pixel_height {
        get { return 480; }
    }


    /*
     *                  Private Members
     */

    private Gdk.Window      _window;

    private Gdk.WindowAttr  _window_attr;

    private Gdk.WindowAttributesType _window_attr_type;

}

The problem is that when I add this to my main Gtk.Window I get a segmentation fault. These are the debugging messages I get:

PageView#get_preferred_height
PageView#get_preferred_width
PageView#size_allocate
PageView#realize
PageView#get_preferred_height
PageView#get_preferred_width
PageView#size_allocate
PageView#size_allocate
Segmentation fault (core dumped)

It seems that if I change the call of set_window (_window) inside realize () to set_window(null), or if I pass a null parent window to the newly created Gdk.Window, the application runs without segmentation faults (but the widget is not displayed as would be expected in either case). I mostly followed along this example to implement the virtual methods and tried porting the C code in Vala. What could be the cause of the problem?

Upvotes: 3

Views: 529

Answers (2)

Michael Gratton
Michael Gratton

Reputation: 516

A couple of suggestions here:

  1. Debug the crashing application using GDB/Nemvier/Builder to find out exactly what the problem is. I use the following command line to debug vala/gtk apps:

    G_DEBUG=fatal-warnings gdb path/to/executable

  2. Use built-in GTK functionality rather reinventing it. If you want to make a widget a specific size, use Gtk.Widget.set_size_request(), and you won't need 90% of the code above. To implement custom drawing, create a Gtk.DrawingArea, connect to the draw signal, implement that, and you're done.

Upvotes: 1

John bracciano
John bracciano

Reputation: 81

In the end, I removed set_has_window(true) from the constructor and only implemented the draw() signal. That seems to have done the trick! Below is the working code segment:

public class PageView : Gtk.Widget {

    public PageView () {
        set_name ("pageview");
    }


    /*
     *              Method and Signal Overrides
     */

    public override Gtk.SizeRequestMode get_request_mode () {
        return Gtk.SizeRequestMode.CONSTANT_SIZE;
            // Don’t trade height-for-width or width-for-height
    }

    public override void get_preferred_width
                                               (out int minimum,
                                                out int natural) {
        stdout.printf ("PageView#get_preferred_width\n");
        minimum = natural = pg_pixel_width;
    }

    public override void get_preferred_height
                                               (out int minimum,
                                                out int natural) {
        stdout.printf ("PageView#get_preferred_height\n");
        minimum = natural = pg_pixel_height;
    }

    public override void size_allocate (Gtk.Allocation alloc) {
        stdout.printf ("PageView#size_allocate\n");
        base.size_allocate (alloc);

    }

    public override void realize () {
        stdout.printf ("PageView#realize\n");
        base.realize ();
    }

    public override void unrealize () {
        stdout.printf ("PageView#unrealize\n");
        base.unrealize ();
    }


    public override bool draw (Cairo.Context cr) {
        stdout.printf ("PageView#draw\n");

        Gtk.Allocation allocation;
        get_allocation (out allocation);

        cr.set_line_width (1);
        cr.set_source_rgba (255, 255, 0, 1);    
        cr.save ();
            cr.scale (allocation.width, allocation.height);
            cr.move_to (0, 0);
            cr.line_to (1, 1);
        cr.restore ();
        cr.stroke ();

        return false;
    }

    ...

}

Upvotes: 1

Related Questions