Displaying images in bottom bars (original) (raw)

February 13, 2025, 1:54pm 1

Hi!
As elaborated in Bottom sheet bottom bar with GtkPicture is broken (#995) · Issues · GNOME / libadwaita · GitLab I would like to display images with unknown sizes and aspect ratios (namely album covers) inside the bottom bar of a libadwaita bottom sheet. Previously this was possible by using a GtkPicture which automatically filled all the space available in the bottom bar. Unfortunately GtkPicture is now increasing the height of the bottom bar further. I’d like the height of the bottom bar to be automatically determined by the labels and the buttons in it and the image should then just take the available space and not request more.
In the issue mentioned above it was suggested to use a GtkImage at a fixed size. I’m not a fan of this idea as I think GTK generally does a great job at dynamically sizing the widgets and I really don’t want do use some magic fixed pixel sizes which could lead to unreadable text if someone changes the font size either through the large text option or manually.

gwillems February 13, 2025, 3:15pm 2

Hi,

In the past I used to pack my GtkPictures as single child of a horizontal GtkBox. That breaks the weird width-for-height size ratio computation, allowing it to follow the height request of the other adjacent widgets.

Note that I don’t know if that still works with the latest allocation changes in gtk.

SoongNoonien (Martin Wagner) February 13, 2025, 3:43pm 3

Thank you for the hint! Unfortunately it doesn’t work with the new GTK version.

bugaevc (Sergey Bugaev) February 13, 2025, 3:55pm 4

This indeed was one of the things that got fixed.

SoongNoonien (Martin Wagner) February 13, 2025, 4:59pm 5

I played around with custom a WIDTH_FOR_HEIGHT widget. It seems like the problem could be solved with this but it’s very tedious to get this working with variable aspect ratios.

gwillems February 14, 2025, 2:15pm 6

Oh… so that was relying on an unexpected corner-case behavior? :slight_smile:
I’ll have to recheck for a proper solution then. Thanks for the info!

two

February 23, 2025, 3:43pm 7

maybe you could use ConstraintLayout for it? i tried and got this:

the foot

#! /usr/bin/env -S vala --pkg libadwaita-1
class MyBar : Gtk.Widget {
    Gtk.ConstraintLayout layout = new Gtk.ConstraintLayout();
    Gtk.Widget pic = new Gtk.Image.from_file(Environment.get_user_special_dir(DOWNLOAD)+"/gnome-logo-text.svg");
    Gtk.Widget btn = new Gtk.Button.from_icon_name("media-playback-start-symbolic") { tooltip_text = "Play" };
    Gtk.Widget title = new Gtk.Label("The Foot") { halign = START, css_classes = { "title-4" } };
    Gtk.Widget subtitle = new Gtk.Label("GNOME Foundation") { halign = START };
    Gtk.Widget[] widgets { owned get { return { pic, btn, title, subtitle }; } }
    construct {
        foreach (var w in widgets)w.set_parent(this);
        this.layout_manager = layout;
        layout.add_constraints_from_description({
            "H:|-[pic]-[title]-[btn]-|",
            "H:|-[pic]-[subtitle]-[btn]-|",
            "V:|-[title]-4-[subtitle]-|",
            "V:|-[pic]-|",
            "V:|-[btn]-|",
        }, 8, 8, title: title, subtitle: subtitle, btn: btn, pic: pic);
        layout.add_constraint(new Gtk.Constraint(btn, WIDTH, EQ, btn, HEIGHT, 1, 0, Gtk.ConstraintStrength.REQUIRED));
        layout.add_constraint(new Gtk.Constraint(pic, WIDTH, EQ, pic, HEIGHT, 1, 0, Gtk.ConstraintStrength.REQUIRED));
        layout.add_constraint(new Gtk.Constraint(title, START, EQ, subtitle, START, 1, 0, Gtk.ConstraintStrength.REQUIRED));
    }
    ~MyBar() {
        foreach (var w in widgets)w.unparent();
    }
}
void main() {
    // Environment.set_variable("GTK_DEBUG", "interactive", false);
    var app = new Adw.Application("org.gnome.Example", 0);
    app.activate.connect(() => {
        var sheet = new Adw.BottomSheet() {
            bottom_bar = new MyBar(),
        };
        var tview = new Adw.ToolbarView() {
            content = sheet,
        };
        tview.add_top_bar(new Adw.HeaderBar());
        var win = new Adw.ApplicationWindow(app) {
            content = tview
        };
        win.present();
    });
    app.run();
}

SoongNoonien (Martin Wagner) March 2, 2025, 9:41pm 8

Thank you very much! I think I can work with this.

SoongNoonien (Martin Wagner) March 24, 2025, 7:05pm 9

Ok, I finally got around to this again. I translated your vala code to roughly equivalent python code. The only thing I left out for now should be the unparenting at the end. Unfortunately the bottom bar doesn’t show up but I also get no error messages. Does anyone have an idea what I could be doing wrong?

#!/usr/bin/python3
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, GLib, Gdk, Adw

class MyBar(Gtk.Widget):
    def __init(self):
        super().__init__()
        layout=Gtk.ConstraintLayout()
        pic=Gtk.Image.new_from_file("gnome-logo-text.svg")
        btn=Gtk.Button(icon_name="media-playback-start-symbolic", tooltip_text="Play")
        title=Gtk.Label(text="The Foot", halign=Gtk.Align.START, css_class=["title-4"])
        subtitle=Gtk.Label(text="GNOME Foundation", halign=Gtk.Align.START)
        for w in (pic, btn, title, subtitle):
            w.set_parent(self)
        self.set_layout_manager(layout)
        layout.add_constraints_from_description([
            "H:|-[pic]-[title]-[btn]-|",
            "H:|-[pic]-[subtitle]-[btn]-|",
            "V:|-[title]-4-[subtitle]-|",
            "V:|-[pic]-|",
            "V:|-[btn]-|"], 8, 8, {"title": title, "subtitle": subtitle, "btn": btn, "pic": pic})
        layout.add_constraint(Gtk.Constraint(btn, Gtk.ConstraintAttribute.WIDTH, Gtk.ConstraintRelation.EQ, btn, Gtk.ConstraintAttribute.HEIGHT, 1, 0, Gtk.ConstraintStrength.REQUIRED))
        layout.add_constraint(Gtk.Constraint(pic, Gtk.ConstraintAttribute.WIDTH, Gtk.ConstraintRelation.EQ, pic, Gtk.ConstraintAttribute.HEIGHT, 1, 0, Gtk.ConstraintStrength.REQUIRED))
        layout.add_constraint(Gtk.Constraint(title, Gtk.ConstraintAttribute.START, Gtk.ConstraintRelation.EQ, subtitle, Gtk.ConstraintAttribute.START, 1, 0, Gtk.ConstraintStrength.REQUIRED))

application=Adw.Application(application_id="com.example.picture")
def on_activate(application):
    sheet=Adw.BottomSheet()
    sheet.set_bottom_bar(MyBar())

    view=Adw.ToolbarView()
    view.add_top_bar(Adw.HeaderBar())
    view.set_content(sheet)

    # window
    window=Adw.ApplicationWindow(application=application, default_width=360, default_height=360)
    window.set_content(view)
    window.present()

application.connect("activate", on_activate)
application.run()

two April 20, 2025, 8:46am 10

i’m sorry for late reply

i also tried the code, and added some print statements, and it didn’t print anything, i noticed the method is called __init instead of __init__, so that’s why it didn’t run, i renamed it and it had some errors with property names and missing .new when creating constrainsts, i changed that too, and it ran correctly, same as in vala

here is the full code with the changes:

#!/usr/bin/env python3
import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, GLib, Gdk, Adw

class MyBar(Gtk.Widget):
    def __init__(self):
        super().__init__()
        layout=Gtk.ConstraintLayout()
        pic=Gtk.Image.new_from_file("gnome-logo-text.svg")
        btn=Gtk.Button(icon_name="media-playback-start-symbolic", tooltip_text="Play")
        title=Gtk.Label(label="The Foot", halign=Gtk.Align.START, css_classes=["title-4"])
        subtitle=Gtk.Label(label="GNOME Foundation", halign=Gtk.Align.START)
        self.widgets = (pic, btn, title, subtitle)
        for w in self.widgets:
            w.set_parent(self)
        self.set_layout_manager(layout)
        layout.add_constraints_from_description([
            "H:|-[pic]-[title]-[btn]-|",
            "H:|-[pic]-[subtitle]-[btn]-|",
            "V:|-[title]-4-[subtitle]-|",
            "V:|-[pic]-|",
            "V:|-[btn]-|"], 8, 8, {"title": title, "subtitle": subtitle, "btn": btn, "pic": pic})
        layout.add_constraint(Gtk.Constraint.new(btn, Gtk.ConstraintAttribute.WIDTH, Gtk.ConstraintRelation.EQ, btn, Gtk.ConstraintAttribute.HEIGHT, 1, 0, Gtk.ConstraintStrength.REQUIRED))
        layout.add_constraint(Gtk.Constraint.new(pic, Gtk.ConstraintAttribute.WIDTH, Gtk.ConstraintRelation.EQ, pic, Gtk.ConstraintAttribute.HEIGHT, 1, 0, Gtk.ConstraintStrength.REQUIRED))
        layout.add_constraint(Gtk.Constraint.new(title, Gtk.ConstraintAttribute.START, Gtk.ConstraintRelation.EQ, subtitle, Gtk.ConstraintAttribute.START, 1, 0, Gtk.ConstraintStrength.REQUIRED))
    def do_dispose(self):
        # print('disposing')
        for w in self.widgets:
            w.unparent()

application=Adw.Application(application_id="com.example.picture")
def on_activate(application):
    sheet=Adw.BottomSheet()
    sheet.set_bottom_bar(MyBar())

    view=Adw.ToolbarView()
    view.add_top_bar(Adw.HeaderBar())
    view.set_content(sheet)

    # window
    window=Adw.ApplicationWindow(application=application, default_width=360, default_height=360)
    window.set_content(view)
    window.present()

application.connect("activate", on_activate)
application.run()

tho i’m not sure if unparenting works correctly because do_dispose requires python3-gi 3.52 and i don’t have it yet, so i have a warning “MyBar still has children left” when closing the window, but it should probably work without warning on newer python packages

SoongNoonien (Martin Wagner) April 20, 2025, 9:05am 11

Thank you! What a dump mistake…

two April 20, 2025, 11:59am 12

maybe python should warn about this, are identifiers that start with __ but not end with it actually used anywhere?

two April 20, 2025, 11:59am 13

@monster said on matrix that it exits without a warning, thanks, so do_dispose must work

ebassi (Emmanuele Bassi) April 20, 2025, 1:32pm 14

You need to chain up:

def do_dispose(self):
    for w in self.widgets:
        w.unparent()
    super().do_dispose()

Otherwise you’ll leak the rest of the instance memory.

monster (Jamie Gravendeel) April 20, 2025, 2:03pm 15

are identifiers that start with __ but not end with it actually used anywhere?

Yes, from PEP 8:

__double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).

SoongNoonien (Martin Wagner) April 20, 2025, 2:42pm 16

For me it’s still exiting with the warning, even though I have version 3.52.3. Adding a print statement shows that do_dispose isn’t run.

ebassi (Emmanuele Bassi) April 20, 2025, 2:45pm 17

If the do_dispose() implementation is not being called then it means something is holding a reference on the widget.