Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gnome/gtk/   (Gnome Linux Desktop Version 4.23.2©)  Datei vom 30.5.2026 mit Größe 11 kB image not shown  

Quelle  gtkwidgetpaintable.c

  Sprache: C
 

/*
 * Copyright © 2018 Benjamin Otte
 *
 * 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 2.1 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
 * Lesser 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 <http://www.gnu.org/licenses/>.
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */


#include "config.h"

#include "gtkwidgetpaintableprivate.h"

#include "gtksnapshot.h"
#include "gtkrendernodepaintableprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkprivate.h"

/**
 * GtkWidgetPaintable:
 *
 * A `GdkPaintable` that displays the contents of a widget.
 *
 * `GtkWidgetPaintable` will also take care of the widget not being in a
 * state where it can be drawn (like when it isn't shown) and just draw
 * nothing or where it does not have a size (like when it is hidden) and
 * report no size in that case.
 *
 * Of course, `GtkWidgetPaintable` allows you to monitor widgets for size
 * changes by emitting the [signal@Gdk.Paintable::invalidate-size] signal
 * whenever the size of the widget changes as well as for visual changes by
 * emitting the [signal@Gdk.Paintable::invalidate-contents] signal whenever
 * the widget changes.
 *
 * You can use a `GtkWidgetPaintable` everywhere a `GdkPaintable` is allowed,
 * including using it on a `GtkPicture` (or one of its parents) that it was
 * set on itself via gtk_picture_set_paintable(). The paintable will take care
 * of recursion when this happens. If you do this however, ensure that the
 * [property@Gtk.Picture:can-shrink] property is set to %TRUE or you might
 * end up with an infinitely growing widget.
 */

struct _GtkWidgetPaintable
{
  GObject parent_instance;

  GtkWidget *widget;
  guint snapshot_count;

  guint         pending_update_cb;      /* the idle source that updates the valid image to be the new current image */

  GdkPaintable *current_image;          /* the image that we are presenting */
  GdkPaintable *pending_image;          /* the image that we should be presenting */
};

struct _GtkWidgetPaintableClass
{
  GObjectClass parent_class;
};

enum {
  PROP_0,
  PROP_WIDGET,

  N_PROPS,
};

static GParamSpec *properties[N_PROPS] = { NULL, };

static void
gtk_widget_paintable_paintable_snapshot (GdkPaintable *paintable,
                                         GdkSnapshot  *snapshot,
                                         double        width,
                                         double        height)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);

  if (self->snapshot_count > 4)
    return;
  else if (self->snapshot_count > 0)
    {
      graphene_rect_t bounds;

      gtk_snapshot_push_clip (snapshot,
                              &GRAPHENE_RECT_INIT(00, width, height));

      if (gtk_widget_compute_bounds (self->widget, self->widget, &bounds))
        {
          gtk_snapshot_scale (snapshot, width / bounds.size.width, height / bounds.size.height);
          gtk_snapshot_translate (snapshot, &bounds.origin);
        }

      gtk_widget_snapshot (self->widget, snapshot);

      gtk_snapshot_pop (snapshot);
    }
  else
    {
      gdk_paintable_snapshot (self->current_image, snapshot, width, height);
    }
}

static GdkPaintable *
gtk_widget_paintable_paintable_get_current_image (GdkPaintable *paintable)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);

  return g_object_ref (self->current_image);
}

static int
gtk_widget_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);

  return gdk_paintable_get_intrinsic_width (self->current_image);
}

static int
gtk_widget_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);

  return gdk_paintable_get_intrinsic_height (self->current_image);
}

static void
gtk_widget_paintable_paintable_init (GdkPaintableInterface *iface)
{
  iface->snapshot = gtk_widget_paintable_paintable_snapshot;
  iface->get_current_image = gtk_widget_paintable_paintable_get_current_image;
  iface->get_intrinsic_width = gtk_widget_paintable_paintable_get_intrinsic_width;
  iface->get_intrinsic_height = gtk_widget_paintable_paintable_get_intrinsic_height;
}

G_DEFINE_TYPE_EXTENDED (GtkWidgetPaintable, gtk_widget_paintable, G_TYPE_OBJECT, 0,
                        G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
                                               gtk_widget_paintable_paintable_init))

static void
gtk_widget_paintable_set_property (GObject      *object,
                                   guint         prop_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)

{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (object);

  switch (prop_id)
    {
    case PROP_WIDGET:
      gtk_widget_paintable_set_widget (self, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_widget_paintable_get_property (GObject    *object,
                                   guint       prop_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (object);

  switch (prop_id)
    {
    case PROP_WIDGET:
      g_value_set_object (value, self->widget);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_widget_paintable_unset_widget (GtkWidgetPaintable *self)
{
  if (self->widget == NULL)
    return;

  self->widget->priv->paintables = g_slist_remove (self->widget->priv->paintables,
                                                   self);

  self->widget = NULL;

  g_clear_object (&self->pending_image);
  g_clear_handle_id (&self->pending_update_cb, g_source_remove);
}

static void
gtk_widget_paintable_dispose (GObject *object)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (object);

  gtk_widget_paintable_unset_widget (self);

  G_OBJECT_CLASS (gtk_widget_paintable_parent_class)->dispose (object);
}

static void
gtk_widget_paintable_finalize (GObject *object)
{
  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (object);

  g_object_unref (self->current_image);

  G_OBJECT_CLASS (gtk_widget_paintable_parent_class)->finalize (object);
}

static void
gtk_widget_paintable_class_init (GtkWidgetPaintableClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->get_property = gtk_widget_paintable_get_property;
  gobject_class->set_property = gtk_widget_paintable_set_property;
  gobject_class->dispose = gtk_widget_paintable_dispose;
  gobject_class->finalize = gtk_widget_paintable_finalize;

  /**
   * GtkWidgetPaintable:widget:
   *
   * The observed widget or %NULL if none.
   */

  properties[PROP_WIDGET] =
    g_param_spec_object ("widget", NULL, NULL,
                         GTK_TYPE_WIDGET,
                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);

  g_object_class_install_properties (gobject_class, N_PROPS, properties);
}

static void
gtk_widget_paintable_init (GtkWidgetPaintable *self)
{
  self->current_image = gdk_paintable_new_empty (00);
}

/**
 * gtk_widget_paintable_new:
 * @widget: (nullable) (transfer none): a `GtkWidget`
 *
 * Creates a new widget paintable observing the given widget.
 *
 * Returns: (transfer full) (type GtkWidgetPaintable): a new `GtkWidgetPaintable`
 */

GdkPaintable *
gtk_widget_paintable_new (GtkWidget *widget)
{
  g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);

  return g_object_new (GTK_TYPE_WIDGET_PAINTABLE,
                       "widget", widget,
                       NULL);
}

static GdkPaintable *
gtk_widget_paintable_snapshot_widget (GtkWidgetPaintable *self)
{
  graphene_rect_t bounds;

  if (self->widget == NULL)
    return gdk_paintable_new_empty (00);

  if (!gtk_widget_compute_bounds (self->widget, self->widget, &bounds))
    return gdk_paintable_new_empty (00);

  if (self->widget->priv->render_node == NULL)
    return gdk_paintable_new_empty (bounds.size.width, bounds.size.height);
  
  return gtk_render_node_paintable_new (self->widget->priv->render_node, &bounds);
}

/**
 * gtk_widget_paintable_get_widget:
 * @self: a `GtkWidgetPaintable`
 *
 * Returns the widget that is observed or %NULL if none.
 *
 * Returns: (transfer none) (nullable): the observed widget.
 */

GtkWidget *
gtk_widget_paintable_get_widget (GtkWidgetPaintable *self)
{
  g_return_val_if_fail (GTK_IS_WIDGET_PAINTABLE (self), NULL);

  return self->widget;
}

/**
 * gtk_widget_paintable_set_widget:
 * @self: a `GtkWidgetPaintable`
 * @widget: (nullable): the widget to observe
 *
 * Sets the widget that should be observed.
 */

void
gtk_widget_paintable_set_widget (GtkWidgetPaintable *self,
                                 GtkWidget          *widget)
{

  g_return_if_fail (GTK_IS_WIDGET_PAINTABLE (self));
  g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));

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

  gtk_widget_paintable_unset_widget (self);

  /* We do not ref the widget to not cause ref cycles when a widget
   * is told to observe itself or one of its parent.
   */

  self->widget = widget;

  if (widget)
    widget->priv->paintables = g_slist_prepend (widget->priv->paintables, self);

  g_object_unref (self->current_image);
  self->current_image = gtk_widget_paintable_snapshot_widget (self);

  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WIDGET]);
  gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
  gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
}

static gboolean
gtk_widget_paintable_update_func (gpointer data)
{
  GtkWidgetPaintable *self = data;
  GdkPaintable *old_image;

  if (self->current_image != self->pending_image)
    {
      old_image = self->current_image;
      self->current_image = self->pending_image;
      self->pending_image = NULL;
      self->pending_update_cb = 0;

      if (gdk_paintable_get_intrinsic_width (self->current_image) != gdk_paintable_get_intrinsic_width (old_image) ||
          gdk_paintable_get_intrinsic_height (self->current_image) != gdk_paintable_get_intrinsic_height (old_image))
        gdk_paintable_invalidate_size (GDK_PAINTABLE (self));

      g_object_unref (old_image);

      gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
    }
  else
    {
      g_clear_object (&self->pending_image);
      self->pending_update_cb = 0;
    }

  return G_SOURCE_REMOVE;
}

void
gtk_widget_paintable_update_image (GtkWidgetPaintable *self)
{
  GdkPaintable *pending_image;

  if (self->pending_update_cb == 0)
    {
      self->pending_update_cb = g_idle_add_full (G_PRIORITY_HIGH,
                                                 gtk_widget_paintable_update_func,
                                                 self,
                                                 NULL);
      gdk_source_set_static_name_by_id (self->pending_update_cb, "[gtk] gtk_widget_paintable_update_func");
    }

  pending_image = gtk_widget_paintable_snapshot_widget (self);
  g_set_object (&self->pending_image, pending_image);
  g_object_unref (pending_image);
}

void
gtk_widget_paintable_push_snapshot_count (GtkWidgetPaintable *self)
{
  self->snapshot_count++;
}

void
gtk_widget_paintable_pop_snapshot_count (GtkWidgetPaintable *self)
{
  self->snapshot_count--;
}

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

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-07-02) ¤

*© 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.