Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gtkpopoverbin.c

  Sprache: C
 

/* gtkpopoverbin.c: A single-child container with a popover
 *
 * SPDX-FileCopyrightText: 2025  Emmanuele Bassi
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */


#include "config.h"

#include "gtkpopoverbin.h"

#include "gtkbinlayout.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
#include "gtkpopovermenu.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkgestureclick.h"
#include "gtkgesturelongpress.h"
#include "gtkshortcutcontroller.h"
#include "gtkshortcutaction.h"
#include "gtkshortcuttrigger.h"

/**
 * GtkPopoverBin:
 *
 * A single child container with a popover.
 *
 * You should use `GtkPopoverBin` whenever you need to present a [class@Gtk.Popover]
 * to the user.
 *
 * ## Actions
 *
 * `GtkPopoverBin` defines the `menu.popup` action, which can be activated
 * to present the popover to the user.
 *
 * ## CSS nodes
 *
 * `GtkPopoverBin` has a single CSS node with the name `popoverbin`.
 *
 * Since: 4.22
 */


struct _GtkPopoverBin
{
  GtkWidget parent_instance;

  GtkWidget *child;
  GtkWidget *popover;

  GMenuModel *menu_model;

  gboolean handle_input;
  GtkEventController *click_gesture;
  GtkEventController *long_press_gesture;
  GtkEventController *shortcut_controller;
};

enum
{
  PROP_CHILD = 1,
  PROP_POPOVER,
  PROP_MENU_MODEL,
  PROP_HANDLE_INPUT,
  N_PROPS
};

static GParamSpec *obj_props[N_PROPS];

static GtkBuildableIface *parent_buildable_iface;

static void gtk_popover_bin_buildable_iface_init (GtkBuildableIface *iface);

G_DEFINE_FINAL_TYPE_WITH_CODE (GtkPopoverBin, gtk_popover_bin, GTK_TYPE_WIDGET,
                               G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                      gtk_popover_bin_buildable_iface_init))

static void
gtk_popover_bin_buildable_add_child (GtkBuildable *buildable,
                                     GtkBuilder   *builder,
                                     GObject      *child,
                                     const char   *type)
{
  if (GTK_IS_WIDGET (child))
    {
      if (GTK_IS_POPOVER (child))
        {
          gtk_buildable_child_deprecation_warning (buildable, builder, NULL, "popover");
          gtk_popover_bin_set_popover (GTK_POPOVER_BIN (buildable), GTK_WIDGET (child));
        }
      else
        {
          gtk_buildable_child_deprecation_warning (buildable, builder, NULL, "child");
          gtk_popover_bin_set_child (GTK_POPOVER_BIN (buildable), GTK_WIDGET (child));
        }
    }
  else
    {
      parent_buildable_iface->add_child (buildable, builder, child, type);
    }
}

static void
gtk_popover_bin_buildable_iface_init (GtkBuildableIface *iface)
{
  parent_buildable_iface = g_type_interface_peek_parent (iface);

  iface->add_child = gtk_popover_bin_buildable_add_child;
}

static void
on_popover_destroy (GtkPopoverBin *self)
{
  gtk_popover_bin_set_popover (self, NULL);
}

static void
on_popover_map (GtkPopoverBin *self)
{
  gtk_widget_add_css_class (self->child, "has-open-popup");
}

static void
on_popover_unmap (GtkPopoverBin *self)
{
  gtk_widget_remove_css_class (self->child, "has-open-popup");
}

static void
gtk_popover_bin_popup_at_position (GtkPopoverBin   *self,
                                   gdouble          x,
                                   gdouble          y)
{
  GdkRectangle rect;

  if (self->popover == NULL)
    return;

  if (x > -0.5 && y > -0.5) {
    rect.x = x;
    rect.y = y;
  } else {
    if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
      rect.x = gtk_widget_get_width (GTK_WIDGET (self));
    else
      rect.x = 0.0;

    rect.y = gtk_widget_get_height (GTK_WIDGET (self));
  }

  rect.width = 0.0;
  rect.height = 0.0;

  gtk_popover_set_pointing_to (GTK_POPOVER (self->popover), &rect);

  gtk_popover_popup (GTK_POPOVER (self->popover));
}

static void
pressed_cb (GtkPopoverBin *self,
            int            n_press,
            double         x,
            double         y,
            GtkGesture    *gesture)
{
  GdkEventSequence *current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
  GdkEvent *event = gtk_gesture_get_last_event (gesture, current);

  if (gdk_event_triggers_context_menu (event))
    {
      gtk_popover_bin_popup_at_position (self, x, y);
      gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
      gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));

      return;
    }

  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
}

static void
long_pressed_cb (GtkPopoverBin *self,
                 double         x,
                 double         y,
                 GtkGesture    *gesture)
{
  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
  gtk_popover_bin_popup_at_position (self, x, y);
}

static void
popup_action (GtkWidget  *widget,
              const char *action_name,
              GVariant   *parameters)
{
  GtkPopoverBin *self = GTK_POPOVER_BIN (widget);

  gtk_popover_bin_popup (self);
}

static void
gtk_popover_bin_dispose (GObject *gobject)
{
  GtkPopoverBin *self = GTK_POPOVER_BIN (gobject);

  if (self->popover != NULL)
    {
      g_signal_handlers_disconnect_by_func (self->popover,
                                            on_popover_destroy,
                                            self);
      g_signal_handlers_disconnect_by_func (self->popover,
                                            on_popover_map,
                                            self);
      g_signal_handlers_disconnect_by_func (self->popover,
                                            on_popover_unmap,
                                            self);
    }

  g_clear_pointer (&self->popover, gtk_widget_unparent);
  g_clear_pointer (&self->child, gtk_widget_unparent);

  g_clear_object (&self->menu_model);

  G_OBJECT_CLASS (gtk_popover_bin_parent_class)->dispose (gobject);
}

static void
gtk_popover_bin_set_property (GObject *gobject,
                              unsigned int prop_id,
                              const GValue *value,
                              GParamSpec *pspec)
{
  GtkPopoverBin *self = GTK_POPOVER_BIN (gobject);

  switch (prop_id)
    {
      case PROP_MENU_MODEL:
        gtk_popover_bin_set_menu_model (self, g_value_get_object (value));
        break;

      case PROP_POPOVER:
        gtk_popover_bin_set_popover (self, g_value_get_object (value));
        break;

      case PROP_CHILD:
        gtk_popover_bin_set_child (self, g_value_get_object (value));
        break;

      case PROP_HANDLE_INPUT:
        gtk_popover_bin_set_handle_input (self, g_value_get_boolean (value));
        break;

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
    }
}

static void
gtk_popover_bin_get_property (GObject *gobject,
                              unsigned int prop_id,
                              GValue *value,
                              GParamSpec *pspec)
{
  GtkPopoverBin *self = GTK_POPOVER_BIN (gobject);

  switch (prop_id)
    {
      case PROP_MENU_MODEL:
        g_value_set_object (value, self->menu_model);
        break;

      case PROP_POPOVER:
        g_value_set_object (value, self->popover);
        break;

      case PROP_CHILD:
        g_value_set_object (value, self->child);
        break;

      case PROP_HANDLE_INPUT:
        g_value_set_boolean (value, self->handle_input);
        break;

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
    }
}

static void
gtk_popover_bin_compute_expand (GtkWidget *widget,
                                gboolean *hexpand_p,
                                gboolean *vexpand_p)
{
  GtkPopoverBin *self = GTK_POPOVER_BIN (widget);
  gboolean hexpand = FALSE, vexpand = FALSE;

  if (self->child != NULL)
    {
      hexpand = gtk_widget_compute_expand (self->child, GTK_ORIENTATION_HORIZONTAL);
      vexpand = gtk_widget_compute_expand (self->child, GTK_ORIENTATION_VERTICAL);
    }

  if (hexpand_p != NULL)
    *hexpand_p = hexpand;
  if (vexpand_p != NULL)
    *vexpand_p = vexpand;
}

static void
gtk_popover_bin_class_init (GtkPopoverBinClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  gobject_class->set_property = gtk_popover_bin_set_property;
  gobject_class->get_property = gtk_popover_bin_get_property;
  gobject_class->dispose = gtk_popover_bin_dispose;

  widget_class->focus = gtk_widget_focus_child;
  widget_class->grab_focus = gtk_widget_grab_focus_child;
  widget_class->compute_expand = gtk_popover_bin_compute_expand;

  /**
   * GtkPopoverBin:menu-model:
   *
   * The `GMenuModel` from which the popup will be created.
   *
   * See [method@Gtk.PopoverBin.set_menu_model] for the interaction
   * with the [property@Gtk.PopoverBin:popover] property.
   *
   * Since: 4.22
   */

  obj_props[PROP_MENU_MODEL] =
      g_param_spec_object ("menu-model", NULL, NULL,
                           G_TYPE_MENU_MODEL,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkPopoverBin:popover:
   *
   * The `GtkPopover` that will be popped up when calling
   * [method@Gtk.PopoverBin.popup].
   *
   * Since: 4.22
   */

  obj_props[PROP_POPOVER] =
      g_param_spec_object ("popover", NULL, NULL,
                           GTK_TYPE_POPOVER,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkPopoverBin:child:
   *
   * The child widget of the popover bin.
   *
   * Since: 4.22
   */

  obj_props[PROP_CHILD] =
      g_param_spec_object ("child", NULL, NULL,
                           GTK_TYPE_WIDGET,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkPopoverBin:handle-input:
   *
   * Whether the popover bin will handle input
   * to trigger the popup.
   *
   * Since: 4.22
   */

  obj_props[PROP_HANDLE_INPUT] =
      g_param_spec_boolean ("handle-input", NULL, NULL,
                            FALSE,
                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  g_object_class_install_properties (gobject_class, N_PROPS, obj_props);

  gtk_widget_class_install_action (widget_class, "menu.popup", NULL, popup_action);

  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);

  gtk_widget_class_set_css_name (widget_class, "popoverbin");
}

static void
gtk_popover_bin_init (GtkPopoverBin *self)
{
}

/**
 * gtk_popover_bin_new:
 *
 * Creates a new popover bin widget.
 *
 * Returns: (transfer floating): the newly created popover bin
 *
 * Since: 4.22
 */

GtkWidget *
gtk_popover_bin_new (void)
{
  return g_object_new (GTK_TYPE_POPOVER_BIN, NULL);
}

/**
 * gtk_popover_bin_set_child:
 * @self: a popover bin
 * @child: (nullable): the child of the popover bin
 *
 * Sets the child of the popover bin.
 *
 * Since: 4.22
 */

void
gtk_popover_bin_set_child (GtkPopoverBin *self,
                           GtkWidget     *child)
{
  g_return_if_fail (GTK_IS_POPOVER_BIN (self));
  g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));

  if (self->child == child)
    return;

  g_clear_pointer (&self->child, gtk_widget_unparent);

  if (child != NULL)
    {
      gtk_widget_set_parent (child, GTK_WIDGET (self));
      self->child = child;
    }

  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CHILD]);
}

/**
 * gtk_popover_bin_get_child:
 * @self: a popover bin
 *
 * Retrieves the child widget of the popover bin.
 *
 * Returns: (transfer none) (nullable): the child widget
 *
 * Since: 4.22
 */

GtkWidget *
gtk_popover_bin_get_child (GtkPopoverBin *self)
{
  g_return_val_if_fail (GTK_IS_POPOVER_BIN (self), NULL);

  return self->child;
}

/**
 * gtk_popover_bin_set_menu_model:
 * @self: a popover bin
 * @model: (nullable): a menu model
 *
 * Sets the menu model used to create the popover that will be
 * presented when calling [method@Gtk.PopoverBin.popup].
 *
 * If @model is `NULL`, the popover will be unset.
 *
 * A [class@Gtk.Popover] will be created from the menu model with
 * [ctor@Gtk.PopoverMenu.new_from_model]. Actions will be connected
 * as documented for this function.
 *
 * If [property@Gtk.PopoverBin:popover] is already set, it will be
 * dissociated from the popover bin, and the property is set to `NULL`.
 *
 * See: [method@Gtk.PopoverBin.set_popover]
 *
 * Since: 4.22
 */

void
gtk_popover_bin_set_menu_model (GtkPopoverBin *self,
                                GMenuModel    *model)
{
  g_return_if_fail (GTK_IS_POPOVER_BIN (self));
  g_return_if_fail (model == NULL || G_IS_MENU_MODEL (model));

  if (self->menu_model == model)
    return;

  g_object_freeze_notify (G_OBJECT (self));

  if (model != NULL)
    g_object_ref (model);

  if (model != NULL)
    {
      GtkWidget *popover;

      popover = gtk_popover_menu_new_from_model (model);
      gtk_popover_set_position (GTK_POPOVER (popover), GTK_POS_BOTTOM);
      gtk_popover_bin_set_popover (self, popover);
    }
  else
    {
      gtk_popover_bin_set_popover (self, NULL);
    }

  self->menu_model = model;
  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MENU_MODEL]);

  g_object_thaw_notify (G_OBJECT (self));
}

/**
 * gtk_popover_bin_get_menu_model:
 * @self: a popover bin
 *
 * Retrieves the menu model set using [method@Gtk.PopoverBin.set_menu_model].
 *
 * Returns: (transfer none) (nullable): the menu model for the popover
 *
 * Since: 4.22
 */

GMenuModel *
gtk_popover_bin_get_menu_model (GtkPopoverBin *self)
{
  g_return_val_if_fail (GTK_IS_POPOVER_BIN (self), NULL);

  return self->menu_model;
}

/**
 * gtk_popover_bin_set_popover:
 * @self: a popover bin
 * @popover: (nullable) (type Gtk.Popover): a `GtkPopover`
 *
 * Sets the `GtkPopover` that will be presented when calling
 * [method@Gtk.PopoverBin.popup].
 *
 * If @popover is `NULL`, the popover will be unset.
 *
 * If [property@Gtk.PopoverBin:menu-model] is set before calling
 * this function, then the menu model property will be unset.
 *
 * See: [method@Gtk.PopoverBin.set_menu_model]
 *
 * Since: 4.22
 */

void
gtk_popover_bin_set_popover (GtkPopoverBin *self,
                             GtkWidget     *popover)
{
  g_return_if_fail (GTK_IS_POPOVER_BIN (self));
  g_return_if_fail (popover == NULL || GTK_IS_POPOVER (popover));

  g_object_freeze_notify (G_OBJECT (self));

  g_clear_object (&self->menu_model);

  if (self->popover != NULL)
    {
      gtk_widget_set_visible (self->popover, FALSE);

      g_signal_handlers_disconnect_by_func (self->popover,
                                            on_popover_destroy,
                                            self);
      g_signal_handlers_disconnect_by_func (self->popover,
                                            on_popover_map,
                                            self);
      g_signal_handlers_disconnect_by_func (self->popover,
                                            on_popover_unmap,
                                            self);

      gtk_widget_unparent (self->popover);
    }

  self->popover = popover;

  if (popover != NULL)
    {
      gtk_widget_set_parent (self->popover, GTK_WIDGET (self));
      g_signal_connect_swapped (self->popover, "destroy",
                                G_CALLBACK (on_popover_destroy),
                                self);
      g_signal_connect_swapped (self->popover, "map",
                                G_CALLBACK (on_popover_map),
                                self);
      g_signal_connect_swapped (self->popover, "unmap",
                                G_CALLBACK (on_popover_unmap),
                                self);
    }

  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POPOVER]);
  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MENU_MODEL]);

  g_object_thaw_notify (G_OBJECT (self));
}

/**
 * gtk_popover_bin_get_popover:
 * @self: a popover bin
 *
 * Retrieves the `GtkPopover` set using [method@Gtk.PopoverBin.set_popover].
 *
 * Returns: (transfer none) (nullable) (type Gtk.Popover): a popover widget
 *
 * Since: 4.22
 */

GtkWidget *
gtk_popover_bin_get_popover (GtkPopoverBin *self)
{
  g_return_val_if_fail (GTK_IS_POPOVER_BIN (self), NULL);

  return self->popover;
}

/**
 * gtk_popover_bin_set_handle_input:
 * @self: a popover bin
 * @handle_input: whether to handle input
 *
 * Enables or disables input handling.
 *
 * If enabled, the popover bin will pop up the
 * popover on right-click or long press, as expected
 * for a context menu.
 *
 * Since: 4.22
 */

void
gtk_popover_bin_set_handle_input (GtkPopoverBin *self,
                                  gboolean       handle_input)
{
  g_return_if_fail (GTK_IS_POPOVER_BIN (self));

  if (self->handle_input == handle_input)
    return;

  self->handle_input = handle_input;

  if (handle_input)
    {
      GtkShortcutTrigger *trigger;
      GtkShortcutAction *action;

      self->click_gesture = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
      gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->click_gesture), 0);
      gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (self->click_gesture), TRUE);
      g_signal_connect_swapped (self->click_gesture, "pressed",
                                G_CALLBACK (pressed_cb), self);
      gtk_widget_add_controller (GTK_WIDGET (self), self->click_gesture);

      self->long_press_gesture = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
      gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (self->long_press_gesture), TRUE);
      gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->long_press_gesture), TRUE);
      g_signal_connect_swapped (self->long_press_gesture, "pressed", G_CALLBACK (long_pressed_cb), self);
      gtk_widget_add_controller (GTK_WIDGET (self), self->long_press_gesture);

      self->shortcut_controller = GTK_EVENT_CONTROLLER (gtk_shortcut_controller_new ());
      trigger = gtk_shortcut_trigger_create_for_menu ();
      action = gtk_named_action_new ("menu.popup");
      gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (self->shortcut_controller),
                                            gtk_shortcut_new (trigger, action));

      gtk_widget_add_controller (GTK_WIDGET (self), self->shortcut_controller);
    }
  else
    {
      gtk_widget_remove_controller (GTK_WIDGET (self), self->click_gesture);
      self->click_gesture = NULL;
      gtk_widget_remove_controller (GTK_WIDGET (self), self->long_press_gesture);
      self->long_press_gesture = NULL;
      gtk_widget_remove_controller (GTK_WIDGET (self), self->shortcut_controller);
      self->shortcut_controller = NULL;
    }

  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HANDLE_INPUT]);
}

gboolean
gtk_popover_bin_get_handle_input (GtkPopoverBin *self)
{
  g_return_val_if_fail (GTK_IS_POPOVER_BIN (self), FALSE);

  return self->handle_input;
}

/**
 * gtk_popover_bin_popup:
 * @self: a popover bin
 *
 * Presents the popover to the user.
 *
 * Use [method@Gtk.PopoverBin.set_popover] or
 * [method@Gtk.PopoverBin.set_menu_model] to define the popover.
 *
 * See: [method@Gtk.PopoverBin.popdown]
 *
 * Since: 4.22
 */

void
gtk_popover_bin_popup (GtkPopoverBin *self)
{
  g_return_if_fail (GTK_IS_POPOVER_BIN (self));

  if (self->popover == NULL)
    return;

  gtk_popover_set_pointing_to (GTK_POPOVER (self->popover), NULL);
  gtk_popover_popup (GTK_POPOVER (self->popover));
}

/**
 * gtk_popover_bin_popdown:
 * @self: a popover bin
 *
 * Hides the popover from the user.
 *
 * See: [method@Gtk.PopoverBin.popup]
 *
 * Since: 4.22
 */

void
gtk_popover_bin_popdown (GtkPopoverBin *self)
{
  g_return_if_fail (GTK_IS_POPOVER_BIN (self));

  if (self->popover != NULL)
    gtk_popover_popdown (GTK_POPOVER (self->popover));
}

Messung V0.5 in Prozent
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik