Rakshith Ravi
Rakshith Ravi

Reputation: 385

C# Gtk get rendered size

In Gtk# (using Mono), I sometimes need to allocate the default size to a widget (I understand you do that by widget.SetSizeRequest(-1, -1);. Someone please correct me if this is not the proper way). I do this to ensure that the size of a, say, Button widget is just as expected. I don't want it to be too big or too small. Allocating the default size ensures that the button is not too big but not too small either.

So when I allocate the default size to a widget, the widget.GetSizeRequest(out width, out height); returns -1 and -1 for width and height respectively.
Which is expected but not what I need. I need to know the rendered size (width and height separately) as I need to place another widget relative to it. So unless I know about its size, I can't place the other widget in the right position.

Although there are other instances where knowing about the rendered size would be helpful, this is the main reason.

I hope you understand what I mean by rendered size.

Update:
Since the answers seem to suggest using widget.Allocation although I've tried (see my comment), here's what I've tried:

Widget[] widgets = GetWidgets();//I initialize the widgets somewhere and get them as an array

//fix is a Gtk.Fixed container
foreach(Widget widget in widgets)
{
    //although this is not the exact way I place the widgets, this is just an example
    fix.Put(widget, GetPosition(widget).X, GetPosition(widget).Y);
    widget.SetSizeRequest(-1, -1);
    Console.WriteLine(w.Allocation.Size);
}
//later on, I need to place a few widgets relative to another

The output of this code is:

{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}

However, when I print the Allocation of a, say, Button widget in the Clicked event of the Button, it prints the rendered size as expected. However, in the above scenario, it just prints 1 and 1.
Can anyone identify what I'm doing wrong?

Upvotes: 0

Views: 1908

Answers (3)

Skyler Brandt
Skyler Brandt

Reputation: 338

What you're looking for is in widget.Allocation, however the size is only set at the Expose Event. That gives you the rendered size of the widget. I'm guessing you're also using a Fixed container though. widget.SetSizeRequest(-1, -1) clears any programmed sizing and lets the Gtk library do what it wants with the size. Unless you really need tight control where each widget is placed, you're best off using containers such as VBox and HBox. One of the advantages of Gtk is that it does a lot of the finer alignment of widgets for you.

EDIT:

You could just draw one widget at a time. Its sort of, kind of "recursion". Once the Gtk run time calls the expose event handler, the size of the widget has already been determined so you can use that to place the next widget. Here is a really simplified example that draws three buttons at a downward diagonal line.

int idx;
string[] userDefinedText;
Fixed fix;

public MainWindow () : base (Gtk.WindowType.Toplevel) {
    Build ();

    // I'm not really sure how you store the text or just a list of widgets 
    // but for the sake of simplicity I'm just using a string array
    idx = 0;
    userDefinedText = new string[3] { "Medium text length", "Long text that goes on and on", "Short" };

    fix = new Fixed ();
    fix.SetSizeRequest (400, 400);
    Add (fix);
    fix.Show ();

    Button b = new Button ();
    b.SetSizeRequest (-1, -1);
    b.Label = userDefinedText [idx];
    b.ExposeEvent += OnButtonExposeEvent;
    fix.Put (b, 5, 5);
    b.Show ();

    ShowAll ();
}

protected void OnButtonExposeEvent (object sender, ExposeEventArgs args) {
    if (idx < (userDefinedText.Length - 1)) {
        // There are still widgets to be placed on the screen
        ++idx;

        Button b = sender as Button;
        int x = b.Allocation.Right;
        int y = b.Allocation.Bottom;

        Button b2 = new Button ();
        b2.SetSizeRequest (-1, -1);
        b2.Label = userDefinedText [idx];
        b2.ExposeEvent += OnButtonExposeEvent;
        fix.Put (b2, x + 5, y + 5);
        b2.Show ();
    }
}

Upvotes: 0

andlabs
andlabs

Reputation: 11588

One last note, and only if you're using GTK+ 3:

I really don't know what you're trying to get the sizes for, buut if you're implementing your own container and want to see what size the widget wants to be (in a way better than RobertN's "real hack") is to use the preferred size system. GTK+ 3 has methods such as get_preferred_width() and get_preferred_height(), which return both a minimum (the absolute minimum, so just the width of ... for a label, for example) and a natural (the more reasonable size that the widget prefers to be to keep every part of it visible) size that you can use; see which one fits your use case more. There's also equivalents for height-for-width and width-for-height geometry (if you ever decide to do that).

I do not know what these methods are called in C#.

Upvotes: 0

SushiHangover
SushiHangover

Reputation: 74174

You are looking the Allocation property (get_allocation in gtk).

Example:

protected void OnButton2Clicked (object sender, EventArgs e)
{
    var rect = button.Allocation;
    PropertyInfo[] properties = rect.GetType().GetProperties();
    var sb = new System.Text.StringBuilder();
    foreach (PropertyInfo pi in properties)
    {
        sb.Append(
            string.Format("Name: {0} | Value: {1}\n", 
                pi.Name, 
                pi.GetValue(rect, null)
            ) 
        );
    }
    Console.WriteLine (sb);
}

Output:

Name: GType | Value: GdkRectangle
Name: Top | Value: 50
Name: Bottom | Value: 82
Name: Right | Value: 69
Name: Left | Value: 30
Name: IsEmpty | Value: False
Name: Size | Value: {Width=40, Height=33}
Name: Location | Value: (30,50)

Update:

A widget that has not been rendered (this includes Visible=false) does not have any container allocation, as, well it does not need any and thus packing will occur correctly for the rest of the visible widgets.

Name: GType | Value: GdkRectangle
Name: Top | Value: -1
Name: Bottom | Value: -1
Name: Right | Value: -1
Name: Left | Value: -1
Name: IsEmpty | Value: False
Name: Size | Value: {Width=1, Height=1}
Name: Location | Value: (-1,-1)

If you really need a true rendered size (with current fonts, border, etc...), you need to render it somewhere. A real hack would be to place it on a double-buffered fixed container and 'render' it but never display it. Get and save the allocation from it (maybe in a dictionary), delete the widget and do it for the next one. Now you have a dictionary of widget sizes that match the current runtime environment..., like I said, a real hack...

Also asking this question a Gtk tagged C language question, or on their mailing list, might get you a better answer, their answer would be in C vs C#, transform it as needed. Make sure you mention that you need to do this in Gtk2 as Gtk3 is really different.

Upvotes: 1

Related Questions