Gtk3: fixed crash of comboBox, added check to avoid infinte loop when setting allocation, simplified code.

This commit is contained in:
zeljan1 2025-01-29 12:20:07 +01:00
parent 5141571608
commit 37bb0cb225
2 changed files with 44 additions and 74 deletions

View File

@ -190,24 +190,6 @@ begin
end;
function GetOwnerComboBox(CellView: PGtkCellView): PGtkComboBox;
var
Parent: PGtkWidget;
begin
Result := nil;
Parent := gtk_widget_get_parent(PGtkWidget(CellView));
while Assigned(Parent) do
begin
if Gtk3IsComboBox(Parent) then
begin
Result := PGtkComboBox(Parent);
Exit;
end;
Parent := gtk_widget_get_parent(Parent);
end;
end;
function GTK_IS_CELL_RENDERER_TEXT(cell: PGtkCellRenderer): Boolean;
begin
Result := Assigned(cell) and
@ -329,31 +311,19 @@ begin
CellClass := PLCLIntfCellRendererClass(cell^.g_type_instance.g_class);
CellClass^.DefaultGetPreferredWidth(cell, widget, minimum_size, natural_size);
ACombo := GetOwnerComboBox(PGtkCellView(widget));
if ACombo = nil then
exit;
AWidget := TGtk3Widget(HwndFromGtkWidget(aCombo));
AWidget := TGtk3Widget(HwndFromGtkWidget(widget));
if (AWidget = nil) or not (wtCombobox in AWidget.WidgetType) or
not Gtk3IsComboBox(AWidget.Widget) then
exit;
if aWidget.InUpdate or (g_object_get_data(cell,'lclwidget') = nil) or
not GTK_IS_CELL_RENDERER_TEXT(cell) then
exit; // can crash without InUpdate check, because SetBounds() calls size_allocate()
ACombo := PGtkCombobox(AWidget.Widget);
W := 0; // width of button area
if not Assigned(aWidget) then
begin
{$IFDEF GTK3DEBUGCELLRENDERER}
writeln('********** warning TGtk3Widget not assigned ! **************** Widget=',G_OBJECT_TYPE_NAME(widget));
{$ENDIF}
exit;
end;
if not Assigned(aWidget.LCLObject) then
begin
{$IFDEF GTK3DEBUGCELLRENDERER}
writeln('********* warning LCLObject not assigned ! ***************');
{$ENDIF}
exit;
end;
if not aWidget.WidgetMapped then
exit; // there's no reason to recalculate size while widget isn't mapped yet, triggers too many times.
if Assigned(ACombo) then
begin
{$IFDEF GTK3DEBUGCELLRENDERER}
@ -377,7 +347,7 @@ begin
begin
TGtk3ComboBox(aWidget).GetArrowWidget^.get_allocation(@Alloc);
//PROBLEM: when combo parent form modal window arrow returns width 1 :(, widget is mapped,realized and visible, so how it's possible
if GTK_IS_CELL_RENDERER_TEXT(cell) and (Alloc.width <= 1) then
if (Alloc.width <= 1) then
begin
// This combo won't be shown as expected, usually happens with combos on modal windows or frames parented to modal win.
{$warning fix this case, maybe alloc primitive combobox when creating widgetset with other widgets for GetSystemMetrics. We need accurate button area width}
@ -400,46 +370,39 @@ begin
{$ENDIF}
end;
if GTK_IS_CELL_RENDERER_TEXT(cell) then
APangoText := GetTextFromCellView(widget);
APangoLayout := pango_layout_new(gtk_widget_get_pango_context(Widget));
if Assigned(APangoLayout) then
begin
APangoText := GetTextFromCellView(widget);
APangoLayout := pango_layout_new(gtk_widget_get_pango_context(Widget));
pango_layout_set_spacing(APangoLayout, 2);
if APangoText = nil then
pango_layout_set_text(APangoLayout, 'Wj' , -1)
else
pango_layout_set_text(APangoLayout, APangoText , -1);
pango_layout_set_ellipsize(APangoLayout, PANGO_ELLIPSIZE_END);
if APangoText <> nil then
g_free(APangoText);
if Assigned(APangoLayout) then
begin
pango_layout_set_spacing(APangoLayout, 2);
if APangoText = nil then
pango_layout_set_text(APangoLayout, 'Wj' , -1)
else
pango_layout_set_text(APangoLayout, APangoText , -1);
pango_layout_set_ellipsize(APangoLayout, PANGO_ELLIPSIZE_END);
if APangoText <> nil then
g_free(APangoText);
pango_layout_get_size(APangoLayout, @APangoWidth, @APangoHeight);
pango_layout_get_size(APangoLayout, @APangoWidth, @APangoHeight);
APangoWidth := APangoWidth div PANGO_SCALE;
APangoHeight := APangoHeight div PANGO_SCALE;
APangoWidth := APangoWidth div PANGO_SCALE;
APangoHeight := APangoHeight div PANGO_SCALE;
cell^.get_padding(@xpad, @ypad);
cell^.get_padding(@xpad, @ypad);
g_object_unref(APangoLayout);
g_object_unref(APangoLayout);
if Assigned(minimum_size) then
minimum_size^ := aCombo^.get_allocated_width - W - xpad;
if Assigned(natural_size) then
natural_size^ := aCombo^.get_allocated_width - W - xpad;
if Assigned(minimum_size) then
minimum_size^ := aWidget.LCLObject.Width - W - xpad;
if Assigned(natural_size) then
natural_size^ := aWidget.LCLObject.Width - W - xpad;
//sometimes only way to have properly sized and painted csDropDownList and owner drawn for combos on modal windows.
//leave this commented code here, I need it for testing various cases.
//if TComboBox(aWidget.LCLObject).Style = csDropDownList then
// cell^.set_fixed_size(aWidget.LCLObject.Width - W - xpad, APangoHeight + ypad + ypad + 1); // Min(APangoHeight - ypad, AWidget.LCLObject.Height - ypad));
//cell^.set_alignment(0, 0);
//cell^.set_padding(2, 0);
end;
end else
DebugLn('BUG: LCLIntfCellRenderer_GetPreferredWidth() cell is not PGtkCellRendererText !');
//sometimes only way to have properly sized and painted csDropDownList and owner drawn for combos on modal windows.
//leave this commented code here, I need it for testing various cases.
//if TComboBox(aWidget.LCLObject).Style = csDropDownList then
// cell^.set_fixed_size(aWidget.LCLObject.Width - W - xpad, APangoHeight + ypad + ypad + 1); // Min(APangoHeight - ypad, AWidget.LCLObject.Height - ypad));
end;
{$IFDEF GTK3DEBUGCELLRENDERER}
if (minimum_size = nil) or (natural_size =nil) then

View File

@ -7685,6 +7685,13 @@ begin
gtk_cell_layout_set_cell_data_func(PGtkCellLayout(FCentralWidget), renderer,
@LCLIntfCellRenderer_CellDataFunc, Self, nil);
if Assigned(PGtkComboBox(Result)^.priv3^.cell_view) then
g_object_set_data(PGObject(PGtkComboBox(Result)^.priv3^.cell_view), 'lclwidget', Self);
if Assigned(PGtkComboBox(Result)^.priv3^.button) then
g_object_set_data(PGObject(PGtkComboBox(Result)^.priv3^.button), 'lclwidget', Self);
if Assigned(PGtkComboBox(Result)^.priv3^.arrow) then
g_object_set_data(PGObject(PGtkComboBox(Result)^.priv3^.arrow), 'lclwidget', Self);
FCentralWidget := nil; //FWidget will be returned from getContainerWidget
// we need cell renderer, but we need f***g GtkEventBox too
// maybe an workaround is possible for csDropDownList (use entry with readonly param).