Search code examples
pythonpopupgtkgtk3wayland

Is `Gtk.Window.set_attached_to` really the correct way to position a popup relative to a widget in wayland?


set_attached_to seems to be the correct way to position a popup window relative to a widget in wayland:

Examples of places where specifying this relation is useful are for instance [...] a completion popup window created by Gtk.Entry [...]

Unfortunately this only yields an error

Gdk-Message: 12:13:16.143: Window 0x1822340 is a temporary window without parent, application will not be able to position it on screen.

Trying to uncomment the popup.set_parent(entry) line only adds a warning:

(try_entry_popup.py:4539): Gtk-WARNING **: 12:17:34.185: Can't set a parent on a toplevel widget

followed by the same error.

Here is a minimal example:

#!/usr/bin/env python
# stripped down from https://gitlab.gnome.org/GNOME/gtk/issues/1541#note_396391

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

entry = Gtk.Entry()
popup = Gtk.Window(type=Gtk.WindowType.POPUP)
#popup.set_parent(entry)
popup.set_attached_to(entry)
popup.show_all()

layout = Gtk.VBox()
layout.pack_start(entry, False, True, 0)

window = Gtk.Window()
window.connect("destroy", Gtk.main_quit)
window.add(layout)
window.show_all()

Gtk.main()

From the entry completion source it looks like it definitely should work. Is it using private features ? Or what am I missing ?


Solution

  • Well, not really: gtk_window_set_attached_to has nothing to do with positioning, it's important for accessibility (a11y) and to apply theming in a correct way. If you want to position your popup window you can follow what it's done in https://gitlab.gnome.org/GNOME/gtk/blob/075dcc142aa525778268165095de019b736f3efa/gtk/gtkentrycompletion.c#L1597

    Here's a very simple implementation:

    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    
    def on_button_clicked(widget):
      popup = Gtk.Window(type=Gtk.WindowType.POPUP)
      # optionally you may set an appropriate type hint, but it's not required.
      popup.set_attached_to(entry)
      popup.set_transient_for(window)
    
      gdk_window = entry.get_window()
      gdk_window_origin = gdk_window.get_origin()
      x = gdk_window_origin[1]
      y = gdk_window_origin[2]
      allocation = entry.get_allocation()
      x += allocation.x
      y += allocation.y + allocation.height
    
      popup.move(x, y)
      popup.show_all()
    
    button = Gtk.Button()
    button.connect('clicked', on_button_clicked)
    entry = Gtk.Entry()
    layout = Gtk.VBox()
    layout.pack_start(button, False, True, 0)
    layout.pack_start(entry, False, True, 0)
    
    window = Gtk.Window()
    window.connect("destroy", Gtk.main_quit)
    window.add(layout)
    window.show_all()
    
    Gtk.main()