Question about positioning a child widget

Hi everyone,

some time ago I asked this question (how to position a child widget Q: custom GtkOverlay's child (GtkRevealer) position) and haven’t found a clear solution.
To get around I’ve set only x,y coordinates (not touching other measurements), it works in most cases. But sometimes there’s garbage in allocation rectangle (Gtk.Overlay::get-child-position) and returned width/height by gtk_widget_get_width()/.._height() are 0, that prevent to calculate any of preferred coordinates.

Is there a more clear way to position a child widget?

Hi,

In that case, just return FALSE from the callback.

Personally, with gtk4, when I need to position a widget on top of another one at specific coordinates, I stopped using GtkOverlay and instead just use a simple custom widget that just overrides the Gtk.Widget.size_allocate virtual method, and from there call Gtk.Widget.size_allocate on each children with their respective position inside my custom widget.

Thank you for suggestions, but

gtk_widget_get_width()/.._height() are 0

In that case, just return FALSE from the callback.

it doesn’t work in all cases with GTK4, for example on xfce4 session with next
get_child_position(GtkOverlay* self, GtkWidget* widget, GdkRectangle *allocation, gpointer user_data) callback:

g_message("<<< allocation: x=%d y=%d w=%d h=%d", allocation->x, allocation->y, allocation->width, allocation->height);
g_message(" width(widget): %d", gtk_widget_get_width(widget));
g_message("height(widget): %d", gtk_widget_get_height(widget));
g_message("  return false");
return false;

I’m getting

** Message: 22:33:17.710: <<< allocation: x=46835600 y=0 w=1897494063 h=32566
** Message: 22:33:17.710:  width(widget): 0
** Message: 22:33:17.710: height(widget): 0
** Message: 22:33:17.710:   return false

and widget drawing somewhere out of visible screen


Gtk.Widget.size_allocate

it’s another case, there’s no settings for x0, y0 position that needed.

You should use gtk_widget_allocate() and pass the position as a translation transformation.

You should use gtk_widget_allocate() and pass the position as a translation transformation.

I see that I can translate x,y position with this GskTransform. The widget width/height and surrounding coordinates are unknown beforehand. All together it becomes a bit messy.
To clarify a bit what I’m trying to achieve:
there’s two draggable with DnD widgets in overlay, initially they’re needed to position relatively to the left-top corner (the first widget) and relatively to the right-top corner (the second one). To calculate their positions I’ve used overlay’s get_child_position() callback to calculate it there, using (1) size of surrounding frame (from GdkRectangle *allocation), (2) child widget size (data returned by gtk_widget_measure()), and (3) known beforehand x0, y0 relatively to a correspondent corner.
It’s calculated easily if I have data in GdkRectangle allocation and it’s there in most cases at get_child_position() calls. Rarely on some OSes it’s not known, it’s the case.

There’s an example when it works, DnD elements in red squares are a legend on the first snapshot and control-buttons on the second, that are initially needed to position.

p.s. if it weren’t dnd elements I’d just use static css margins for them

I would personally do something like this (Python code):

import sys
import gi
gi.require_version('Gdk', '4.0')
gi.require_version('Gtk', '4.0')
from gi.repository import Gdk, Gtk

class MyOverlay(Gtk.Widget):
    def __init__(self):
        super().__init__()
        self.child = Gtk.DrawingArea(content_width=400, content_height=300)
        self.overlay = Gtk.Label(label="Hello!")
        self.child.set_parent(self)
        self.overlay.set_parent(self)
    def do_dispose(self):
        self.child.unparent()
        self.overlay.unparent()
        Gtk.Widget.do_dispose(self)
    def do_measure(self, orientation, for_size):
        # We only care of the main child. If the overlay is too big it will be cropped
        return self.child.measure(orientation, for_size)
    def do_size_allocate(self, width, height, baseline):
        rect = Gdk.Rectangle()
        # The main child is given the full allocation
        rect.x, rect.y, rect.width, rect.height = 0, 0, width, height
        self.child.size_allocate(rect, baseline)
        # The overlay will be allocated with minimum size, at (100, 50) offset
        min_w, nat_w, min_b, nat_b = self.overlay.measure(Gtk.Orientation.HORIZONTAL, -1)
        min_h, nat_h, min_b, nat_b = self.overlay.measure(Gtk.Orientation.VERTICAL, -1)
        rect.x, rect.y, rect.width, rect.height = 100, 50, min_w, min_h
        self.overlay.size_allocate(rect, baseline)

class MyAppWindow(Gtk.ApplicationWindow):
    def __init__(self):
        super().__init__()
        self.set_child(MyOverlay())
        self.present()

class MyApp(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)
        self.connect('activate', self.on_activate)
    def on_activate(self, app):
        self.add_window(MyAppWindow())

if __name__ == '__main__':
    sys.exit(MyApp().run(sys.argv))
1 Like

@gwillems
Now I got this approach.
More or less it plays the same role as a GtkOverlay. In case if it’s positioned from the right side, full allocation size needs to be known. I’ll compare it to cases when I couldn’t get that size with get_child_position().
Certainly it is an option to GtkOverlay/get_child_position().
Thank you.

This topic was automatically closed 45 days after the last reply. New replies are no longer allowed.