I am creating a GtkGridView with a list of widgets. Each widget contains a picture, which can be of arbitrary dimensions and aspect ratio. To ensure that every row has the same height, I want each image to fit inside a square frame that is as large as the width of the widget. How would I go about achieving this?
I have tried using GtkAspectFrame (which only seems to fit the picture in a smaller square, which does not touch the left and right sides of the box), and GtkConstraintLayout, (which led to glitchy results, as the widget randomly refused to grow in height in order to hold the picture, but only sometimes)
I’m sure there is probably some simple solution which i’m not seeing due to my inexperience with GTK (this is my first project), so any help would be appreciated.
I am currently using GtkPicture, but GtkImage seems to give me the same problem. I would like the image to expand horizontally to fill its container, but also expand vertically to maintain a square aspect ratio. GtkImage seems to only expand horizontally.
I don’t know if I understand correctly, you have several images of different aspect ratios and you want them to fit inside a square frame?
If the template is square, it will always have the same height and width. If the aspect ratio of the image is not square (height != width) there are three ways to insert the image:
Ensure that the highest value between height and width will touch the border, consequently the lowest value will not touch the border and the remaining space will be filled with black stripes.
Allow the image to warp to assume a square aspect ratio, touching all edges.
Crop the image to make the smallest value touch the edge and the part of the largest value that crosses the dimension is cropped.
It seems to me that what you want is 2. If that’s the case, just set GtkPicture:content-fit to GTK_CONTENT_FIT_FILL. If not, I really don’t understand what the desired behavior is.
I think I’m just not explaining myself well. The widgets do not have a fixed width that I know beforehand (otherwise this would be as simple as setting the height of the image). Since the widgets are in a GtkGridView their width can vary as the window is resized. Each widget consists of a vertical GtkBox holding the image and some labels. The images can vary in size and aspect ratio, and essentially I want the height of the pictures to be equal to the width of the widget (otherwise rows containing widgets with very tall pictures will stretch vertically to hold them, which leaves the other widgets in the row with a lot of ugly whitespace).
Option 2 is basically what I want. If I can get the picture to have a square aspect ratio then it’s just a matter of setting content-fit to GTK_CONTENT_FIT_CONTAIN, which should letterbox the images horizontally if they are too tall, and vertically if they are too wide. Square images would perfectly touch the edges of the widget.
Currently I just set a fixed height-request as a band-aid solution, but when the window is resized and the columns become wider the picture height does not increase, which leaves the images smaller than they could be, and not flush with the edges of each widget.
Okay, now I think I get it. There are two things that might work:
Bind the Gtk.Box width-request with the image height-request, ensuring that both will always have the same value.
Add a GtkConstraint between the Gtk.Box width-request and the image height-request with exactly the same idea.
The difference between the two is that in 1 the attribute values will always be the same, and in 2 it is possible to use the value of an attribute with a multiplier or adder factor.
Sadly this doesn’t work, as GtkGridView doesn’t seem to set the width-request property of its children, leaving it at the default of -1.
This was one of my first attempts, creating a GtkConstraintLayout and setting the appropriate constraints, and I do think that it is theoretically correct, but in practice it lead to strange, glitchy behavior. The widgets look correct when first loaded, but after scrolling their height resets to 0 (This probably has something to do with the way GtkGridView recycles its widgets as you scroll).
What I ended up doing is implementing a subclass of GtkWidget, which holds the GtkPicture and overrides the gtk_widget_measure method, always returning square dimensions. Here’s the relevant code (in Rust as those are the bindings I’m using):
Thank you very much for your help @kriptolix, I do think that the GtkConstraint solution is probably the most idiomatic, but it doesn’t seem to play nice with GtkGridView, which I think may be a bug in GTK rather than an error in the solution.