Measure of GtkGridView gives incorrect height

Gtk 4.12.3


GtkScrolledWindow::vscrollvar-policy = never
GtkGridView::vscroll-policy = natural

The problem occurs in the gtk_grid_view_measure function.

gridview does not receive the for_size value, so it cannot calculate how much width the child should occupy like _size_allocate does, and use the width to calculate the height of the child.

If you set the child’s min-width to the currently allocated width via css at this time, everything will work fine. This also verifies the above point


However, when GtkScrolledWindow uses GTK_POLICY_AUTOMATIC, everything returns to normal.
The question I asked doesn’t appear and I can’t figure out what the problem is.


log
 >## LxBaseWidgetSimple(0x555559046340) >>>>>>>>>>>>>>>>>>>>>>>>
 >for_size: 960, orientation: 1
   >
   >## LxSearchPageSonglistViewport(0x555559225b50) >>>>>>>>>>>>>>>>>>>>>>>>
   >for_size: -1, orientation: 1
     >
     >## LxGeneralDataGridView(0x555558950d10) >>>>>>>>>>>>>>>>>>>>>>>>
     >for_size: -1, orientation: 1
        

       // There are two more widgets here
       scrolledwindow > gridview

       >
       >## LxSonglistInfoCard(0x5555592385d0) >>>>>>>>>>>>>>>>>>>>>>>>
       >for_size: -1, orientation: 0
       >LxHoverImage:: minimum: 100, natural: 100
       >GtkLabel:: minimum: 12, natural: 96
       >GtkLabel:: minimum: 12, natural: 65
       >
       >minimum: 100, natural: 100
       >## LxSonglistInfoCard(0x5555592385d0) <<<<<<<<<<<<<<<<<<<<<<<<
       >
       >
       >## LxSonglistInfoCard(0x5555592385d0) >>>>>>>>>>>>>>>>>>>>>>>>
       >for_size: 100, orientation: 1
       >LxHoverImage:: minimum: 100, natural: 100
       >GtkLabel:: minimum: 18, natural: 18
       >GtkLabel:: minimum: 18, natural: 18
       >


       // The wrong height is reported here.

       >minimum: 136, natural: 136
       >## LxSonglistInfoCard(0x5555592385d0) <<<<<<<<<<<<<<<<<<<<<<<<
       >
     >
     >minimum: 152, natural: 152
     >## LxGeneralDataGridView(0x555558950d10) <<<<<<<<<<<<<<<<<<<<<<<<
     >
   >
   >minimum: 152, natural: 152
   >## LxSearchPageSonglistViewport(0x555559225b50) <<<<<<<<<<<<<<<<<<<<<<<<
   >
 >
 >minimum: 1136, natural: 1136
 >## LxBaseWidgetSimple(0x555559046340) <<<<<<<<<<<<<<<<<<<<<<<<
 >

  // in gtk_grid_view_size_allocate: 

 Since the gridview already has a width and height at this point, 
 it can be passed to the child widget as for_size. 
 So the child widget will be assigned the correct dimensions.

 >
 >## LxSonglistInfoCard(0x5555592385d0) >>>>>>>>>>>>>>>>>>>>>>>>
 >for_size: -1, orientation: 0
 >LxHoverImage:: minimum: 100, natural: 100
 >GtkLabel:: minimum: 12, natural: 96
 >GtkLabel:: minimum: 12, natural: 65
 >
 >minimum: 100, natural: 100
 >## LxSonglistInfoCard(0x5555592385d0) <<<<<<<<<<<<<<<<<<<<<<<<
 >
 >
 >## LxSonglistInfoCard(0x5555592385d0) >>>>>>>>>>>>>>>>>>>>>>>>
 >for_size: 134, orientation: 1
 >LxHoverImage:: minimum: 134, natural: 134
 >GtkLabel:: minimum: 18, natural: 18
 >GtkLabel:: minimum: 18, natural: 18
 >

 // This is the correct height

 >minimum: 170, natural: 170
 >## LxSonglistInfoCard(0x5555592385d0) <<<<<<<<<<<<<<<<<<<<<<<<
 >

Hi, can you please provide a minimal example to reproduce the behavior you describe?

That will help us to diagnostic the problem. Thanks!

win2 has min-width set, so the correct height is assigned.

But when the window width changes, the error will happen again

code
#include <gtk/gtk.h>

#if 1  // LxCard gobject definition

G_DECLARE_FINAL_TYPE( LxCard, lx_card, LX, CARD, GtkWidget )

LxCard* lx_card_new( GtkStringObject* str );

struct _LxCard {
	GtkWidget parent_instance;

	GtkImage* image;
	GtkLabel* label;
};

G_DEFINE_TYPE( LxCard, lx_card, gtk_widget_get_type() )

#define _SELF2( _SELF, _NAME ) LxCard* _SELF = ( gpointer )_NAME
#define _SELF( _NAME )         _SELF2( self, _NAME );

#if 1  // static function

#endif

#if 1  // base class virtual function

static GtkSizeRequestMode lx_card_get_request_mode( GtkWidget* widget )
{
	return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}

static void lx_card_measure( GtkWidget*     widget,
                             GtkOrientation orientation,
                             int            for_size,
                             int*           mininum,
                             int*           natural,
                             int*           mininum_baseline,
                             int*           natural_baseline )
{
	_SELF( widget );

	if ( orientation == GTK_ORIENTATION_HORIZONTAL ) {
		*mininum = 100;
		*natural = 100;
	}
	else {
		if ( for_size < 0 )
			for_size = 100;

		int min = 0, nat = 0;
		gtk_widget_measure( ( gpointer )self->label, orientation, for_size, &min, &nat, NULL, NULL );

		*mininum = for_size + min;
		*natural = for_size + nat;
	}
}

static void lx_card_size_allocate( GtkWidget* widget, int width, int height, int baseline )
{
	_SELF( widget );

	int min = 0, nat = 0;
	gtk_widget_measure( ( gpointer )self->label, GTK_ORIENTATION_VERTICAL, width, &min, &nat, NULL, NULL );

	GtkAllocation allocation = { .x = 0, .y = 0, .width = width, .height = width };
	gtk_widget_size_allocate( ( gpointer )self->image, &allocation, baseline );

	allocation = ( GtkAllocation ) { .x = 0, .y = width, .width = width, .height = min };
	gtk_widget_size_allocate( ( gpointer )self->label, &allocation, baseline );
}

static void lx_card_init( LxCard* self )
{
	self->label = ( gpointer )gtk_label_new( NULL );
	self->image = ( gpointer )gtk_image_new();

	gtk_widget_insert_after( ( gpointer )self->label, ( gpointer )self, NULL );
	gtk_widget_insert_after( ( gpointer )self->image, ( gpointer )self, NULL );
}

static void lx_card_class_init( LxCardClass* klass )
{
	GObjectClass*   base_class   = ( GObjectClass* )klass;
	GtkWidgetClass* widget_class = ( GtkWidgetClass* )klass;
	GtkWidgetClass* parent_class = ( GtkWidgetClass* )klass;

	parent_class->get_request_mode = lx_card_get_request_mode;
	parent_class->measure          = lx_card_measure;
	parent_class->size_allocate    = lx_card_size_allocate;

	gtk_widget_class_set_css_name( widget_class, "LxCard" );
}

#endif

#if 1  // public function

LxCard* lx_card_new( GtkStringObject* str )
{
	LxCard* self = g_object_new( lx_card_get_type(), NULL );

	gtk_label_set_label( self->label, gtk_string_object_get_string( str ) );

	return self;
}

#endif

#undef _SELF2
#undef _SELF

#endif

static void _factory_bind( GtkListItemFactory* factory, GtkListItem* item, gpointer user_data )
{
	gtk_list_item_set_child( item, ( gpointer )lx_card_new( gtk_list_item_get_item( item ) ) );
}

static GtkWidget* win_body()
{
	GtkBox* box = ( gpointer )gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );

	GListStore* store = g_list_store_new( GTK_TYPE_STRING_OBJECT );
	for ( int i = 0; i < 10; i++ ) {
		GtkStringObject* str = gtk_string_object_new( g_strdup_printf( "index: %d", i ) );
		g_list_store_append( store, str );
		g_object_unref( str );
	}

	GtkNoSelection*     model   = gtk_no_selection_new( ( gpointer )store );
	GtkListItemFactory* factory = gtk_signal_list_item_factory_new();
	g_signal_connect( factory, "bind", G_CALLBACK( _factory_bind ), NULL );
	GtkGridView* view = ( gpointer )gtk_grid_view_new( ( gpointer )model, factory );
	gtk_grid_view_set_max_columns( view, 8 );
	gtk_grid_view_set_min_columns( view, 6 );

	GtkScrolledWindow* scrolled_window = ( gpointer )gtk_scrolled_window_new();
	gtk_scrolled_window_set_child( scrolled_window, ( gpointer )view );
	gtk_widget_set_hexpand( ( gpointer )scrolled_window, TRUE );

	gtk_scrolled_window_set_policy( scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER );

	gtk_box_append( box, ( gpointer )scrolled_window );

	return ( gpointer )box;
}

static void win1()
{
	GtkWindow* window = ( gpointer )gtk_window_new();
	gtk_widget_set_size_request( ( gpointer )window, 800, 600 );
	gtk_widget_add_css_class( ( gpointer )window, "win1" );
	gtk_window_set_title( window, "win1" );

	gtk_window_set_child( window, ( gpointer )win_body() );
	gtk_window_present( window );
}

static void win2()
{
	GtkWindow* window = ( gpointer )gtk_window_new();
	gtk_widget_set_size_request( ( gpointer )window, 800, 600 );
	gtk_widget_add_css_class( ( gpointer )window, "win2" );
	gtk_window_set_title( window, "win2" );

	GtkBox* box = ( gpointer )win_body();

	char* tip = "window width = 800\n"
	            "gridview > child padding-width = 6\n"
	            "gridview > child > LxCard min-width = 800 / 6 = 133 - padding-width = 127"
	  //
	  ;

	gtk_box_append( box, gtk_label_new( tip ) );

	gtk_window_set_child( window, ( gpointer )box );
	gtk_window_present( window );
}

int main( int argc, char** argv )
{
	gtk_init();

	GtkCssProvider* provider   = gtk_css_provider_new();
	const char*     global_css = "LxCard > image { background: #999999; }"
	                             ".win2 LxCard { min-width: 127px; }";

	gtk_css_provider_load_from_string( provider, global_css );
	gtk_style_context_add_provider_for_display(
	  gdk_display_get_default(), GTK_STYLE_PROVIDER( provider ), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION );

	win1();
	win2();

	while ( TRUE )
		g_main_context_iteration( NULL, TRUE );
}

Thanks!

Well, TBH I’m personally not used to all the subtleties of the widget allocation…
Maybe others can tell if it’s expected or not.

Is anything blocking on your side from using GTK_POLICY_AUTOMATIC?
(using “never” is quite uncommon for a ScrolledWindow)

Well, TBH I’m personally not used to all the subtleties of the widget allocation…

I don’t understand what this means, can you explain it?

Unless there are clear development guidelines, all kinds of nesting will occur in the container. Including the current situation.

I changed the structure to try to reduce unnecessary nesting. But still can’t solve the gridview “error”. (I don’t know why gridview doesn’t implement get_request_mode)

In my opinion, ‘never’ is not mysterious. This is a reasonable feature and easy to implement.

I easily solved this problem by adding a few lines of code to the gridview

static GtkSizeRequestMode
gtk_grid_view_get_request_mode(GtkWidget*widget)
{
  GtkGridView *self = GTK_GRID_VIEW (widget);

  if (GTK_ORIENTATION_VERTICAL == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
      return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
  else
      return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}

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