Gimp/libgimpwidgets/gimpunitcombobox.c
balooii balooii b0ee5ddfce Issue #12491: Fix GimpUnitComboBox immediately closing on certain platforms
I noticed that the issue of the combo box popup getting immeidately
closed on Linux X11 (and probably macOS as well) only happens when using
GimpUnitComboBox as used in gimppropwidgets.c and gimpsizeentry.c while
there is no issue when used in gimpstatusbar.c

The only noticable difference I saw was that in status bar was that
the wrap width of combo box was set to 1 via
gtk_combo_box_set_wrap_width. Which was added as a hack for another
issue.

I did the same for the other two uses of GimpUnitComboBox and the issue
disappeared so I moved this hack to GimpUnitComboBox itself as it seems
to be generally useful.

The change doesn't make much sense to me and is a workaround for
something that should probably be changed in GTK but well.

As explored in !2654 I think we shouldn't use GtkComboBox but reimplement
using a Popover. This would get rid of these workarounds as we wouldn't rely
on internal behaviour of GtkComboBox.

Couldn't test macOS or Windows but Linux X11 and Wayland work just fine.
Tested by: GDK_BACKEND=x11 gimp and GDK_BACKEND=wayland gimp

Fixes #12491

Superseedes !2654
2026-03-10 00:11:37 +01:00

239 lines
6.4 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
*
* gimpunitcombobox.c
* Copyright (C) 2004, 2008 Sven Neumann <sven@gimp.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gimpwidgetstypes.h"
#include "gimpunitcombobox.h"
#include "gimpunitstore.h"
/**
* SECTION: gimpunitcombobox
* @title: GimpUnitComboBox
* @short_description: A #GtkComboBox to select a #GimpUnit.
* @see_also: #GimpUnit, #GimpUnitStore
*
* #GimpUnitComboBox selects units stored in a #GimpUnitStore.
**/
struct _GimpUnitComboBox
{
GtkComboBox parent_instance;
};
static void gimp_unit_combo_box_popup_shown (GimpUnitComboBox *widget);
static void gimp_unit_combo_box_constructed (GObject *object);
G_DEFINE_TYPE (GimpUnitComboBox, gimp_unit_combo_box, GTK_TYPE_COMBO_BOX)
#define parent_class gimp_unit_combo_box_parent_class
static void
gimp_unit_combo_box_class_init (GimpUnitComboBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gimp_unit_combo_box_constructed;
}
static void
gimp_unit_combo_box_init (GimpUnitComboBox *combo)
{
g_signal_connect (combo, "notify::popup-shown",
G_CALLBACK (gimp_unit_combo_box_popup_shown),
NULL);
}
static void
gimp_unit_combo_box_constructed (GObject *object)
{
GimpUnitComboBox *combo = GIMP_UNIT_COMBO_BOX (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
gimp_unit_combo_box_popup_shown (combo);
/* Hack to fix issue #2642 and #12491 */
gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (combo), 1);
}
static void
gimp_unit_combo_box_popup_shown (GimpUnitComboBox *widget)
{
GimpUnitStore *store;
gboolean shown;
GtkCellRenderer *cell;
g_object_get (widget,
"model", &store,
"popup-shown", &shown,
NULL);
if (store)
{
if (shown)
_gimp_unit_store_sync_units (store);
g_object_unref (store);
}
gtk_cell_layout_clear (GTK_CELL_LAYOUT (widget));
cell = gtk_cell_renderer_text_new ();
if (shown)
{
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell,
"text", GIMP_UNIT_STORE_UNIT_LONG_FORMAT,
NULL);
/* XXX This is ugly but it seems that GtkComboBox won't resize its popup
* menu when the contents changes (it only resizes the main "chosen item"
* area). We force a redraw by popping down then up after a contents
* change.
*/
g_signal_handlers_disconnect_by_func (widget,
G_CALLBACK (gimp_unit_combo_box_popup_shown),
NULL);
gtk_combo_box_popdown (GTK_COMBO_BOX (widget));
gtk_combo_box_popup (GTK_COMBO_BOX (widget));
g_signal_connect (widget, "notify::popup-shown",
G_CALLBACK (gimp_unit_combo_box_popup_shown),
NULL);
}
else
{
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, FALSE);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell,
"text", GIMP_UNIT_STORE_UNIT_SHORT_FORMAT,
NULL);
}
}
/**
* gimp_unit_combo_box_new:
*
* Returns: a new #GimpUnitComboBox.
**/
GtkWidget *
gimp_unit_combo_box_new (void)
{
GtkWidget *combo_box;
GimpUnitStore *store;
store = gimp_unit_store_new (0);
combo_box = g_object_new (GIMP_TYPE_UNIT_COMBO_BOX,
"model", store,
"id-column", GIMP_UNIT_STORE_UNIT_LONG_FORMAT,
NULL);
g_object_unref (store);
return combo_box;
}
/**
* gimp_unit_combo_box_new_with_model:
* @model: a #GimpUnitStore
*
* Returns: a new #GimpUnitComboBox.
**/
GtkWidget *
gimp_unit_combo_box_new_with_model (GimpUnitStore *model)
{
return g_object_new (GIMP_TYPE_UNIT_COMBO_BOX,
"model", model,
"id-column", GIMP_UNIT_STORE_UNIT_LONG_FORMAT,
NULL);
}
/**
* gimp_unit_combo_box_get_active:
* @combo: a #GimpUnitComboBox
*
* Returns the #GimpUnit currently selected in the combo box.
*
* Returns: (transfer none): The selected #GimpUnit.
**/
GimpUnit *
gimp_unit_combo_box_get_active (GimpUnitComboBox *combo)
{
GtkTreeIter iter;
GimpUnit *unit;
g_return_val_if_fail (GIMP_IS_UNIT_COMBO_BOX (combo), NULL);
gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter);
gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)), &iter,
GIMP_UNIT_STORE_UNIT, &unit,
-1);
return unit;
}
/**
* gimp_unit_combo_box_set_active:
* @combo: a #GimpUnitComboBox
* @unit: a #GimpUnit
*
* Sets @unit as the currently selected #GimpUnit on @combo.
**/
void
gimp_unit_combo_box_set_active (GimpUnitComboBox *combo,
GimpUnit *unit)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean iter_valid;
g_return_if_fail (GIMP_IS_UNIT_COMBO_BOX (combo));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
_gimp_unit_store_sync_units (GIMP_UNIT_STORE (model));
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
GimpUnit *iter_unit;
gtk_tree_model_get (model, &iter,
GIMP_UNIT_STORE_UNIT, &iter_unit,
-1);
if (unit == iter_unit)
{
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
break;
}
}
}