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 158 kB image not shown  

Bilddatei gtkscrolledwindow.c

  Sprache: C
 

/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * 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 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/>.
 */


/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
 */


#include "config.h"

#include "gtkscrolledwindow.h"

#include "gtkadjustment.h"
#include "gtkadjustmentprivate.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
#include "gtkdragsourceprivate.h"
#include "gtkeventcontrollermotion.h"
#include "gtkeventcontrollerscroll.h"
#include "gtkeventcontrollerprivate.h"
#include "gtkgesturedrag.h"
#include "gtkgesturelongpress.h"
#include "gtkgesturepan.h"
#include "gtkgesturesingle.h"
#include "gtkgestureswipe.h"
#include "gtkgestureprivate.h"
#include "gtkkineticscrollingprivate.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkprivate.h"
#include "gtkprogresstrackerprivate.h"
#include "gtkscrollable.h"
#include "gtkscrollbar.h"
#include "gtksettingsprivate.h"
#include "gtksnapshot.h"
#include "gtkrenderbackgroundprivate.h"
#include "gtkrenderborderprivate.h"
#include "gtktypebuiltins.h"
#include "gtkviewport.h"
#include "gtkwidgetprivate.h"

#include <math.h>

/**
 * GtkScrolledWindow:
 *
 * Makes its child scrollable.
 *
 * <picture>
 *   <source srcset="scrolledwindow-dark.png" media="(prefers-color-scheme: dark)">
 *   <img alt="An example GtkScrolledWindow" src="scrolledwindow.png">
 * </picture>
 *
 * It does so using either internally added scrollbars or externally
 * associated adjustments, and optionally draws a frame around the child.
 *
 * Widgets with native scrolling support, i.e. those whose classes implement
 * the [iface@Gtk.Scrollable] interface, are added directly. For other types
 * of widget, the class [class@Gtk.Viewport] acts as an adaptor, giving
 * scrollability to other widgets. [method@Gtk.ScrolledWindow.set_child]
 * intelligently accounts for whether or not the added child is a `GtkScrollable`.
 * If it isnt, then it wraps the child in a `GtkViewport`. Therefore, you can
 * just add any child widget and not worry about the details.
 *
 * If [method@Gtk.ScrolledWindow.set_child] has added a `GtkViewport` for you,
 * it will be automatically removed when you unset the child.
 * Unless [property@Gtk.ScrolledWindow:hscrollbar-policy] and
 * [property@Gtk.ScrolledWindow:vscrollbar-policy] are %GTK_POLICY_NEVER or
 * %GTK_POLICY_EXTERNAL, `GtkScrolledWindow` adds internal `GtkScrollbar` widgets
 * around its child. The scroll position of the child, and if applicable the
 * scrollbars, is controlled by the [property@Gtk.ScrolledWindow:hadjustment]
 * and [property@Gtk.ScrolledWindow:vadjustment] that are associated with the
 * `GtkScrolledWindow`. See the docs on [class@Gtk.Scrollbar] for the details,
 * but note that the step_increment and page_increment fields are only
 * effective if the policy causes scrollbars to be present.
 *
 * If a `GtkScrolledWindow` doesnt behave quite as you would like, or
 * doesnt have exactly the right layout, its very possible to set up
 * your own scrolling with `GtkScrollbar` and for example a `GtkGrid`.
 *
 * # Touch support
 *
 * `GtkScrolledWindow` has built-in support for touch devices. When a
 * touchscreen is used, swiping will move the scrolled window, and will
 * expose 'kinetic' behavior. This can be turned off with the
 * [property@Gtk.ScrolledWindow:kinetic-scrolling] property if it is undesired.
 *
 * `GtkScrolledWindow` also displays visual 'overshoot' indication when
 * the content is pulled beyond the end, and this situation can be
 * captured with the [signal@Gtk.ScrolledWindow::edge-overshot] signal.
 *
 * If no mouse device is present, the scrollbars will overlaid as
 * narrow, auto-hiding indicators over the content. If traditional
 * scrollbars are desired although no mouse is present, this behaviour
 * can be turned off with the [property@Gtk.ScrolledWindow:overlay-scrolling]
 * property.
 *
 * # Shortcuts and Gestures
 *
 * The following signals have default keybindings:
 *
 * - [signal@Gtk.ScrolledWindow::scroll-child]
 *
 * # CSS nodes
 *
 * `GtkScrolledWindow` has a main CSS node with name scrolledwindow.
 * It gets a .frame style class added when [property@Gtk.ScrolledWindow:has-frame]
 * is %TRUE.
 *
 * It uses subnodes with names overshoot and undershoot to draw the overflow
 * and underflow indications. These nodes get the .left, .right, .top or .bottom
 * style class added depending on where the indication is drawn.
 *
 * `GtkScrolledWindow` also sets the positional style classes (.left, .right,
 * .top, .bottom) and style classes related to overlay scrolling
 * (.overlay-indicator, .dragging, .hovering) on its scrollbars.
 *
 * If both scrollbars are visible, the area where they meet is drawn
 * with a subnode named junction.
 *
 * # Accessibility
 *
 * Until GTK 4.10, `GtkScrolledWindow` used the [enum@Gtk.AccessibleRole.group] role.
 *
 * Starting from GTK 4.12, `GtkScrolledWindow` uses the [enum@Gtk.AccessibleRole.generic]
 * role.
 */


/* scrolled window policy and size requisition handling:
 *
 * gtk size requisition works as follows:
 *   a widget upon size-request reports the width and height that it finds
 *   to be best suited to display its contents, including children.
 *   the width and/or height reported from a widget upon size requisition
 *   may be overridden by the user by specifying a width and/or height
 *   other than 0 through gtk_widget_set_size_request().
 *
 * a scrolled window needs (for implementing all three policy types) to
 * request its width and height based on two different rationales.
 * 1)   the user wants the scrolled window to just fit into the space
 *      that it gets allocated for a specific dimension.
 * 1.1) this does not apply if the user specified a concrete value
 *      value for that specific dimension by either specifying usize for the
 *      scrolled window or for its child.
 * 2)   the user wants the scrolled window to take as much space up as
 *      is desired by the child for a specific dimension (i.e. POLICY_NEVER).
 *
 * also, kinda obvious:
 * 3)   a user would certainly not have chosen a scrolled window as a container
 *      for the child, if the resulting allocation takes up more space than the
 *      child would have allocated without the scrolled window.
 *
 * conclusions:
 * A) from 1) follows: the scrolled window shouldnt request more space for a
 *    specific dimension than is required at minimum.
 * B) from 1.1) follows: the requisition may be overridden by usize of the scrolled
 *    window (done automatically) or by usize of the child (needs to be checked).
 * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
 *    childs dimension.
 * D) from 3) follows: the scrolled window childs minimum width and minimum height
 *    under A) at least correspond to the space taken up by its scrollbars.
 */


/* Kinetic scrolling */
#define MAX_OVERSHOOT_DISTANCE 100
#define DECELERATION_FRICTION 4
#define OVERSHOOT_FRICTION 20
#define VELOCITY_ACCUMULATION_FLOOR 0.33
#define VELOCITY_ACCUMULATION_CEIL 1.0
#define VELOCITY_ACCUMULATION_MAX 6.0

/* Animated scrolling */
#define ANIMATION_DURATION 200

/* Overlay scrollbars */
#define INDICATOR_FADE_OUT_DELAY 2000
#define INDICATOR_FADE_OUT_DURATION 1000
#define INDICATOR_FADE_OUT_TIME 500
#define INDICATOR_CLOSE_DISTANCE 5
#define INDICATOR_FAR_DISTANCE 10

/* Scrolled off indication */
#define UNDERSHOOT_SIZE 40

#define MAGIC_SCROLL_FACTOR 2.5

typedef struct _GtkScrolledWindowClass         GtkScrolledWindowClass;

struct _GtkScrolledWindow
{
  GtkWidget parent_instance;
};

struct _GtkScrolledWindowClass
{
  GtkWidgetClass parent_class;

  /* Unfortunately, GtkScrollType is deficient in that there is
   * no horizontal/vertical variants for GTK_SCROLL_START/END,
   * so we have to add an additional boolean flag.
   */

  gboolean (*scroll_child) (GtkScrolledWindow *scrolled_window,
                            GtkScrollType      scroll,
                            gboolean           horizontal);

  void (* move_focus_out) (GtkScrolledWindow *scrolled_window,
                           GtkDirectionType   direction);
};

typedef struct
{
  GtkWidget *scrollbar;
  gboolean   over; /* either mouse over, or while dragging */
  gint64     last_scroll_time;
  guint      conceil_timer;

  double     current_pos;
  double     source_pos;
  double     target_pos;
  GtkProgressTracker tracker;
  guint      tick_id;
  guint      over_timeout_id;
} Indicator;

typedef struct
{
  GtkWidget *child;

  GtkWidget     *hscrollbar;
  GtkWidget     *vscrollbar;

  GtkCssNode    *overshoot_node[4];
  GtkCssNode    *undershoot_node[4];
  GtkCssNode    *junction_node;

  Indicator hindicator;
  Indicator vindicator;

  GtkCornerType  window_placement;
  guint    has_frame                : 1;
  guint    hscrollbar_policy        : 2;
  guint    vscrollbar_policy        : 2;
  guint    hscrollbar_visible       : 1;
  guint    vscrollbar_visible       : 1;
  guint    focus_out                : 1/* used by ::move-focus-out implementation */
  guint    overlay_scrolling        : 1;
  guint    use_indicators           : 1;
  guint    auto_added_viewport      : 1;
  guint    propagate_natural_width  : 1;
  guint    propagate_natural_height : 1;
  guint    smooth_scroll            : 1;
  guint    scrolling                : 1;

  int      min_content_width;
  int      min_content_height;
  int      max_content_width;
  int      max_content_height;

  guint scroll_events_overshoot_id;

  /* Kinetic scrolling */
  GtkGesture *long_press_gesture;
  GtkGesture *swipe_gesture;
  GtkKineticScrolling *hscrolling;
  GtkKineticScrolling *vscrolling;
  gint64 last_deceleration_time;

  /* These two gestures are mutually exclusive */
  GtkGesture *drag_gesture;
  GtkGesture *pan_gesture;

  double drag_start_x;
  double drag_start_y;
  double prev_x;
  double prev_y;

  guint                  kinetic_scrolling         : 1;

  guint                  deceleration_id;
  GdkModifierType        scrolling_modifiers;

  double                 x_velocity;
  double                 y_velocity;

  double                 unclamped_hadj_value;
  double                 unclamped_vadj_value;
} GtkScrolledWindowPrivate;

enum {
  PROP_0,
  PROP_HADJUSTMENT,
  PROP_VADJUSTMENT,
  PROP_HSCROLLBAR_POLICY,
  PROP_VSCROLLBAR_POLICY,
  PROP_WINDOW_PLACEMENT,
  PROP_HAS_FRAME,
  PROP_MIN_CONTENT_WIDTH,
  PROP_MIN_CONTENT_HEIGHT,
  PROP_KINETIC_SCROLLING,
  PROP_OVERLAY_SCROLLING,
  PROP_MAX_CONTENT_WIDTH,
  PROP_MAX_CONTENT_HEIGHT,
  PROP_PROPAGATE_NATURAL_WIDTH,
  PROP_PROPAGATE_NATURAL_HEIGHT,
  PROP_CHILD,
  NUM_PROPERTIES
};

/* Signals */
enum
{
  SCROLL_CHILD,
  MOVE_FOCUS_OUT,
  EDGE_OVERSHOT,
  EDGE_REACHED,
  LAST_SIGNAL
};

static void     gtk_scrolled_window_set_property       (GObject           *object,
                                                        guint              prop_id,
                                                        const GValue      *value,
                                                        GParamSpec        *pspec);
static void     gtk_scrolled_window_get_property       (GObject           *object,
                                                        guint              prop_id,
                                                        GValue            *value,
                                                        GParamSpec        *pspec);
static void     gtk_scrolled_window_dispose            (GObject           *object);

static void     gtk_scrolled_window_snapshot           (GtkWidget         *widget,
                                                        GtkSnapshot       *snapshot);
static void     gtk_scrolled_window_size_allocate      (GtkWidget         *widget,
                                                        int                width,
                                                        int                height,
                                                        int                baseline);
static gboolean gtk_scrolled_window_focus              (GtkWidget         *widget,
                                                        GtkDirectionType   direction);
static gboolean gtk_scrolled_window_scroll_child       (GtkScrolledWindow *scrolled_window,
                                                        GtkScrollType      scroll,
                                                        gboolean           horizontal);
static void     gtk_scrolled_window_move_focus_out     (GtkScrolledWindow *scrolled_window,
                                                        GtkDirectionType   direction_type);

static void     gtk_scrolled_window_relative_allocation(GtkScrolledWindow *scrolled_window,
                                                        GtkAllocation     *allocation);
static void     gtk_scrolled_window_inner_allocation   (GtkScrolledWindow *scrolled_window,
                                                        GtkAllocation     *rect);
static void     gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
                                                        GtkWidget         *scrollbar,
                                                        GtkAllocation     *allocation);
static void     gtk_scrolled_window_allocate_child     (GtkScrolledWindow   *swindow,
                                                        int                  width,
                                                        int                  height);
static void     gtk_scrolled_window_adjustment_changed (GtkAdjustment     *adjustment,
                                                        gpointer           data);
static void     gtk_scrolled_window_adjustment_value_changed (GtkAdjustment     *adjustment,
                                                              gpointer           data);
static gboolean gtk_widget_should_animate              (GtkWidget           *widget);
static void     gtk_scrolled_window_measure (GtkWidget      *widget,
                                             GtkOrientation  orientation,
                                             int             for_size,
                                             int            *minimum_size,
                                             int            *natural_size,
                                             int            *minimum_baseline,
                                             int            *natural_baseline);
static void  gtk_scrolled_window_map                   (GtkWidget           *widget);
static void  gtk_scrolled_window_unmap                 (GtkWidget           *widget);
static void  gtk_scrolled_window_realize               (GtkWidget           *widget);
static void  gtk_scrolled_window_unrealize             (GtkWidget           *widget);
static void _gtk_scrolled_window_set_adjustment_value  (GtkScrolledWindow *scrolled_window,
                                                        GtkAdjustment     *adjustment,
                                                        double             value);

static void gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window);

static gboolean _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
                                                    int               *overshoot_x,
                                                    int               *overshoot_y);

static void     gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);

static void     gtk_scrolled_window_update_use_indicators (GtkScrolledWindow *scrolled_window);
static void     remove_indicator     (GtkScrolledWindow *sw,
                                      Indicator         *indicator);
static gboolean maybe_hide_indicator (gpointer data);

static void     indicator_start_fade (Indicator *indicator,
                                      double     pos);
static void     indicator_set_over   (Indicator *indicator,
                                      gboolean   over);

static gboolean scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
                                        double                    delta_x,
                                        double                    delta_y,
                                        GtkEventControllerScroll *scroll);

static guint signals[LAST_SIGNAL] = {0};
static GParamSpec *properties[NUM_PROPERTIES];

static void gtk_scrolled_window_buildable_init (GtkBuildableIface *iface);

G_DEFINE_TYPE_WITH_CODE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_WIDGET,
                         G_ADD_PRIVATE (GtkScrolledWindow)
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_scrolled_window_buildable_init))

static GtkBuildableIface *parent_buildable_iface;

static void
gtk_scrolled_window_buildable_add_child (GtkBuildable *buildable,
                                         GtkBuilder   *builder,
                                         GObject      *child,
                                         const char   *type)
{
  if (GTK_IS_WIDGET (child))
    {
      gtk_buildable_child_deprecation_warning (buildable, builder, NULL, "child");
      gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (buildable), GTK_WIDGET (child));
    }
  else
    {
      parent_buildable_iface->add_child (buildable, builder, child, type);
    }
}

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

  iface->add_child = gtk_scrolled_window_buildable_add_child;
}

static void
add_scroll_binding (GtkWidgetClass *widget_class,
                    guint           keyval,
                    GdkModifierType mask,
                    GtkScrollType   scroll,
                    gboolean        horizontal)
{
  gtk_widget_class_add_binding_signal (widget_class,
                                       keyval, mask,
                                       "scroll-child",
                                       "(ib)", scroll, horizontal);
}

static void
add_tab_bindings (GtkWidgetClass   *widget_class,
                  GdkModifierType   modifiers,
                  GtkDirectionType  direction)
{
  gtk_widget_class_add_binding_signal (widget_class,
                                       GDK_KEY_Tab, modifiers,
                                       "move-focus-out",
                                       "(i)", direction);
  gtk_widget_class_add_binding_signal (widget_class,
                                       GDK_KEY_KP_Tab, modifiers,
                                       "move-focus-out",
                                       "(i)", direction);
}

static void
motion_controller_leave (GtkEventController   *controller,
                         GtkScrolledWindow    *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (priv->use_indicators)
    {
      indicator_set_over (&priv->hindicator, FALSE);
      indicator_set_over (&priv->vindicator, FALSE);
    }
}

static void
update_scrollbar_positions (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  gboolean is_rtl;

  if (priv->hscrollbar != NULL)
    {
      if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
          priv->window_placement == GTK_CORNER_TOP_RIGHT)
        {
          gtk_widget_add_css_class (priv->hscrollbar, "bottom");
          gtk_widget_remove_css_class (priv->hscrollbar, "top");
        }
      else
        {
          gtk_widget_add_css_class (priv->hscrollbar, "top");
          gtk_widget_remove_css_class (priv->hscrollbar, "bottom");
        }
    }

  if (priv->vscrollbar != NULL)
    {
      is_rtl = _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;
      if ((is_rtl &&
          (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
           priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
         (!is_rtl &&
          (priv->window_placement == GTK_CORNER_TOP_LEFT ||
           priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
        {
          gtk_widget_add_css_class (priv->vscrollbar, "right");
          gtk_widget_remove_css_class (priv->vscrollbar, "left");
        }
      else
        {
          gtk_widget_add_css_class (priv->vscrollbar, "left");
          gtk_widget_remove_css_class (priv->vscrollbar, "right");
        }
    }
}

static void
gtk_scrolled_window_direction_changed (GtkWidget        *widget,
                                       GtkTextDirection  previous_dir)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);

  update_scrollbar_positions (scrolled_window);

  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->direction_changed (widget, previous_dir);
}

static void
gtk_scrolled_window_compute_expand (GtkWidget *widget,
                                    gboolean  *hexpand,
                                    gboolean  *vexpand)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (priv->child)
    {
      *hexpand = gtk_widget_compute_expand (priv->child, GTK_ORIENTATION_HORIZONTAL);
      *vexpand = gtk_widget_compute_expand (priv->child, GTK_ORIENTATION_VERTICAL);
    }
  else
    {
      *hexpand = FALSE;
      *vexpand = FALSE;
    }
}

static GtkSizeRequestMode
gtk_scrolled_window_get_request_mode (GtkWidget *widget)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (!priv->child || !gtk_widget_get_visible (priv->child))
    return GTK_SIZE_REQUEST_CONSTANT_SIZE;

  /* In many cases, we can actually get away with reporting constant-size,
   * so try to do that unless our reported size actually depends on the
   * child's size *and* the for_size we pass to the child depends on the
   * for_size passed to us.
   */

  if ((priv->hscrollbar_policy == GTK_POLICY_NEVER &&
       priv->vscrollbar_policy == GTK_POLICY_NEVER) ||
      (priv->hscrollbar_policy == GTK_POLICY_NEVER &&
       priv->propagate_natural_height) ||
      (priv->vscrollbar_policy == GTK_POLICY_NEVER &&
       priv->propagate_natural_width))
    return gtk_widget_get_request_mode (priv->child);

  return GTK_SIZE_REQUEST_CONSTANT_SIZE;
}

static void
gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);

  gobject_class->set_property = gtk_scrolled_window_set_property;
  gobject_class->get_property = gtk_scrolled_window_get_property;
  gobject_class->dispose = gtk_scrolled_window_dispose;

  widget_class->snapshot = gtk_scrolled_window_snapshot;
  widget_class->size_allocate = gtk_scrolled_window_size_allocate;
  widget_class->measure = gtk_scrolled_window_measure;
  widget_class->focus = gtk_scrolled_window_focus;
  widget_class->map = gtk_scrolled_window_map;
  widget_class->unmap = gtk_scrolled_window_unmap;
  widget_class->realize = gtk_scrolled_window_realize;
  widget_class->unrealize = gtk_scrolled_window_unrealize;
  widget_class->direction_changed = gtk_scrolled_window_direction_changed;
  widget_class->compute_expand = gtk_scrolled_window_compute_expand;
  widget_class->get_request_mode = gtk_scrolled_window_get_request_mode;

  class->scroll_child = gtk_scrolled_window_scroll_child;
  class->move_focus_out = gtk_scrolled_window_move_focus_out;

  /**
   * GtkScrolledWindow:hadjustment:
   *
   * The `GtkAdjustment` for the horizontal position.
   */

  properties[PROP_HADJUSTMENT] =
      g_param_spec_object ("hadjustment", NULL, NULL,
                           GTK_TYPE_ADJUSTMENT,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:vadjustment:
   *
   * The `GtkAdjustment` for the vertical position.
   */

  properties[PROP_VADJUSTMENT] =
      g_param_spec_object ("vadjustment", NULL, NULL,
                           GTK_TYPE_ADJUSTMENT,
                           G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:hscrollbar-policy:
   *
   * When the horizontal scrollbar is displayed.
   *
   * Use [method@Gtk.ScrolledWindow.set_policy] to set
   * this property.
   */

  properties[PROP_HSCROLLBAR_POLICY] =
      g_param_spec_enum ("hscrollbar-policy", NULL, NULL,
                         GTK_TYPE_POLICY_TYPE,
                         GTK_POLICY_AUTOMATIC,
                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:vscrollbar-policy:
   *
   * When the vertical scrollbar is displayed.
   *
   * Use [method@Gtk.ScrolledWindow.set_policy] to set
   * this property.
   */

  properties[PROP_VSCROLLBAR_POLICY] =
      g_param_spec_enum ("vscrollbar-policy", NULL, NULL,
                        GTK_TYPE_POLICY_TYPE,
                        GTK_POLICY_AUTOMATIC,
                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:window-placement: (getter get_placement) (setter set_placement)
   *
   * Where the contents are located with respect to the scrollbars.
   */

  properties[PROP_WINDOW_PLACEMENT] =
      g_param_spec_enum ("window-placement", NULL, NULL,
                        GTK_TYPE_CORNER_TYPE,
                        GTK_CORNER_TOP_LEFT,
                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:has-frame:
   *
   * Whether to draw a frame around the contents.
   */

  properties[PROP_HAS_FRAME] =
      g_param_spec_boolean ("has-frame", NULL, NULL,
                            FALSE,
                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:min-content-width:
   *
   * The minimum content width of @scrolled_window.
   */

  properties[PROP_MIN_CONTENT_WIDTH] =
      g_param_spec_int ("min-content-width", NULL, NULL,
                        -1, G_MAXINT, -1,
                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:min-content-height:
   *
   * The minimum content height of @scrolled_window.
   */

  properties[PROP_MIN_CONTENT_HEIGHT] =
      g_param_spec_int ("min-content-height", NULL, NULL,
                        -1, G_MAXINT, -1,
                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:kinetic-scrolling:
   *
   * Whether kinetic scrolling is enabled or not.
   *
   * Kinetic scrolling only applies to devices with source %GDK_SOURCE_TOUCHSCREEN.
   */

  properties[PROP_KINETIC_SCROLLING] =
      g_param_spec_boolean ("kinetic-scrolling", NULL, NULL,
                            TRUE,
                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:overlay-scrolling:
   *
   * Whether overlay scrolling is enabled or not.
   *
   * If it is, the scrollbars are only added as traditional widgets
   * when a mouse is present. Otherwise, they are overlaid on top of
   * the content, as narrow indicators.
   *
   * Note that overlay scrolling can also be globally disabled, with
   * the [property@Gtk.Settings:gtk-overlay-scrolling] setting.
   */

  properties[PROP_OVERLAY_SCROLLING] =
      g_param_spec_boolean ("overlay-scrolling", NULL, NULL,
                            TRUE,
                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:max-content-width:
   *
   * The maximum content width of @scrolled_window.
   */

  properties[PROP_MAX_CONTENT_WIDTH] =
      g_param_spec_int ("max-content-width", NULL, NULL,
                        -1, G_MAXINT, -1,
                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:max-content-height:
   *
   * The maximum content height of @scrolled_window.
   */

  properties[PROP_MAX_CONTENT_HEIGHT] =
      g_param_spec_int ("max-content-height", NULL, NULL,
                        -1, G_MAXINT, -1,
                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:propagate-natural-width:
   *
   * Whether the natural width of the child should be calculated and propagated
   * through the scrolled windows requested natural width.
   *
   * This is useful in cases where an attempt should be made to allocate exactly
   * enough space for the natural size of the child.
   */

  properties[PROP_PROPAGATE_NATURAL_WIDTH] =
      g_param_spec_boolean ("propagate-natural-width", NULL, NULL,
                            FALSE,
                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:propagate-natural-height:
   *
   * Whether the natural height of the child should be calculated and propagated
   * through the scrolled windows requested natural height.
   *
   * This is useful in cases where an attempt should be made to allocate exactly
   * enough space for the natural size of the child.
   */

  properties[PROP_PROPAGATE_NATURAL_HEIGHT] =
      g_param_spec_boolean ("propagate-natural-height", NULL, NULL,
                            FALSE,
                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkScrolledWindow:child:
   *
   * The child widget.
   *
   * When setting this property, if the child widget does not implement
   * [iface@Gtk.Scrollable], the scrolled window will add the child to
   * a [class@Gtk.Viewport] and then set the viewport as the child.
   */

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

  g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);

  /**
   * GtkScrolledWindow::scroll-child:
   * @scrolled_window: a `GtkScrolledWindow`
   * @scroll: a `GtkScrollType` describing how much to scroll
   * @horizontal: whether the keybinding scrolls the child
   *   horizontally or not
   *
   * Emitted when a keybinding that scrolls is pressed.
   *
   * This is a [keybinding signal](class.SignalAction.html).
   *
   * The horizontal or vertical adjustment is updated which triggers a
   * signal that the scrolled windows child may listen to and scroll itself.
   *
   * Returns: whether the scroll happened
   */

  signals[SCROLL_CHILD] =
    g_signal_new (I_("scroll-child"),
                  G_TYPE_FROM_CLASS (gobject_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
                  NULL, NULL,
                  _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
                  G_TYPE_BOOLEAN, 2,
                  GTK_TYPE_SCROLL_TYPE,
                  G_TYPE_BOOLEAN);
  g_signal_set_va_marshaller (signals[SCROLL_CHILD],
                              G_TYPE_FROM_CLASS (gobject_class),
                              _gtk_marshal_BOOLEAN__ENUM_BOOLEANv);

  /**
   * GtkScrolledWindow::move-focus-out:
   * @scrolled_window: a `GtkScrolledWindow`
   * @direction_type: either %GTK_DIR_TAB_FORWARD or
   *   %GTK_DIR_TAB_BACKWARD
   *
   * Emitted when focus is moved away from the scrolled window by a
   * keybinding.
   *
   * This is a [keybinding signal](class.SignalAction.html).
   *
   * The default bindings for this signal are
   * <kbd>Ctrl</kbd>+<kbd>Tab</kbd> to move forward and
   * <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Tab</kbd>` to move backward.
   */

  signals[MOVE_FOCUS_OUT] =
    g_signal_new (I_("move-focus-out"),
                  G_TYPE_FROM_CLASS (gobject_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
                  NULL, NULL,
                  NULL,
                  G_TYPE_NONE, 1,
                  GTK_TYPE_DIRECTION_TYPE);

  /**
   * GtkScrolledWindow::edge-overshot:
   * @scrolled_window: a `GtkScrolledWindow`
   * @pos: edge side that was hit
   *
   * Emitted whenever user initiated scrolling makes the scrolled
   * window firmly surpass the limits defined by the adjustment
   * in that orientation.
   *
   * A similar behavior without edge resistance is provided by the
   * [signal@Gtk.ScrolledWindow::edge-reached] signal.
   *
   * Note: The @pos argument is LTR/RTL aware, so callers should be
   * aware too if intending to provide behavior on horizontal edges.
   */

  signals[EDGE_OVERSHOT] =
    g_signal_new (I_("edge-overshot"),
                  G_TYPE_FROM_CLASS (gobject_class),
                  G_SIGNAL_RUN_LAST, 0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE);

  /**
   * GtkScrolledWindow::edge-reached:
   * @scrolled_window: a `GtkScrolledWindow`
   * @pos: edge side that was reached
   *
   * Emitted whenever user-initiated scrolling makes the scrolled
   * window exactly reach the lower or upper limits defined by the
   * adjustment in that orientation.
   *
   * A similar behavior with edge resistance is provided by the
   * [signal@Gtk.ScrolledWindow::edge-overshot] signal.
   *
   * Note: The @pos argument is LTR/RTL aware, so callers should be
   * aware too if intending to provide behavior on horizontal edges.
   */

  signals[EDGE_REACHED] =
    g_signal_new (I_("edge-reached"),
                  G_TYPE_FROM_CLASS (gobject_class),
                  G_SIGNAL_RUN_LAST, 0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE);

  add_scroll_binding (widget_class, GDK_KEY_Left,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
  add_scroll_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  TRUE);
  add_scroll_binding (widget_class, GDK_KEY_Up,    GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
  add_scroll_binding (widget_class, GDK_KEY_Down,  GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD,  FALSE);

  add_scroll_binding (widget_class, GDK_KEY_Page_Up,   GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
  add_scroll_binding (widget_class, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD,  TRUE);
  add_scroll_binding (widget_class, GDK_KEY_Page_Up,   0,                GTK_SCROLL_PAGE_BACKWARD, FALSE);
  add_scroll_binding (widget_class, GDK_KEY_Page_Down, 0,                GTK_SCROLL_PAGE_FORWARD,  FALSE);

  add_scroll_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE);
  add_scroll_binding (widget_class, GDK_KEY_End,  GDK_CONTROL_MASK, GTK_SCROLL_END,   TRUE);
  add_scroll_binding (widget_class, GDK_KEY_Home, 0,                GTK_SCROLL_START, FALSE);
  add_scroll_binding (widget_class, GDK_KEY_End,  0,                GTK_SCROLL_END,   FALSE);

  add_tab_bindings (widget_class, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
  add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);

  gtk_widget_class_set_css_name (widget_class, I_("scrolledwindow"));
  gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
}

static gboolean
may_hscroll (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  return priv->hscrollbar_visible || priv->hscrollbar_policy == GTK_POLICY_EXTERNAL;
}

static gboolean
may_vscroll (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  return priv->vscrollbar_visible || priv->vscrollbar_policy == GTK_POLICY_EXTERNAL;
}

static inline gboolean
policy_may_be_visible (GtkPolicyType policy)
{
  return policy == GTK_POLICY_ALWAYS || policy == GTK_POLICY_AUTOMATIC;
}

static void
scrolled_window_drag_begin_cb (GtkScrolledWindow *scrolled_window,
                               double             start_x,
                               double             start_y,
                               GtkGesture        *gesture)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GdkEventSequence *sequence;
  GtkWidget *event_widget;

  priv->drag_start_x = priv->unclamped_hadj_value;
  priv->drag_start_y = priv->unclamped_vadj_value;
  gtk_scrolled_window_cancel_deceleration (scrolled_window);
  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
  event_widget = gtk_gesture_get_last_target (gesture, sequence);

  if (event_widget == priv->vscrollbar || event_widget == priv->hscrollbar ||
      (!may_hscroll (scrolled_window) && !may_vscroll (scrolled_window)))
    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
}

static void
gtk_scrolled_window_invalidate_overshoot (GtkScrolledWindow *scrolled_window)
{
  GtkAllocation child_allocation;
  int overshoot_x, overshoot_y;

  if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
    return;

  gtk_scrolled_window_relative_allocation (scrolled_window,
                                           &child_allocation);
  if (overshoot_x != 0)
    gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));

  if (overshoot_y != 0)
    gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
}

static void
scrolled_window_drag_update_cb (GtkScrolledWindow *scrolled_window,
                                double             offset_x,
                                double             offset_y,
                                GtkGesture        *gesture)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GdkEventSequence *sequence;
  GtkAdjustment *hadjustment;
  GtkAdjustment *vadjustment;
  double dx, dy;

  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));

  if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_CLAIMED &&
      !gtk_drag_check_threshold_double (GTK_WIDGET (scrolled_window),
                                        00, offset_x, offset_y))
    return;

  gtk_scrolled_window_invalidate_overshoot (scrolled_window);
  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);

  hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
  if (hadjustment && may_hscroll (scrolled_window))
    {
      dx = priv->drag_start_x - offset_x;
      _gtk_scrolled_window_set_adjustment_value (scrolled_window,
                                                 hadjustment, dx);
    }

  vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
  if (vadjustment && may_vscroll (scrolled_window))
    {
      dy = priv->drag_start_y - offset_y;
      _gtk_scrolled_window_set_adjustment_value (scrolled_window,
                                                 vadjustment, dy);
    }

  gtk_scrolled_window_invalidate_overshoot (scrolled_window);
}

static void
gtk_scrolled_window_decelerate (GtkScrolledWindow *scrolled_window,
                                double             x_velocity,
                                double             y_velocity)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  gboolean overshoot;

  overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
  priv->x_velocity = x_velocity;
  priv->y_velocity = y_velocity;

  /* Zero out vector components for which we don't scroll */
  if (!may_hscroll (scrolled_window))
    priv->x_velocity = 0;
  if (!may_vscroll (scrolled_window))
    priv->y_velocity = 0;

  if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
    {
      if (priv->deceleration_id == 0)
        gtk_scrolled_window_start_deceleration (scrolled_window);
      priv->x_velocity = priv->y_velocity = 0;
    }
  else
    {
      g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
      g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
    }
}

static void
scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
                          double             x_velocity,
                          double             y_velocity)
{
  gtk_scrolled_window_decelerate (scrolled_window, -x_velocity, -y_velocity);
}

static void
scrolled_window_long_press_cb (GtkScrolledWindow *scrolled_window,
                               double             x,
                               double             y,
                               GtkGesture        *gesture)
{
  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
}

static void
scrolled_window_long_press_cancelled_cb (GtkScrolledWindow *scrolled_window,
                                         GtkGesture        *gesture)
{
  GdkEventSequence *sequence;
  GdkEvent *event;
  GdkEventType event_type;

  sequence = gtk_gesture_get_last_updated_sequence (gesture);
  event = gtk_gesture_get_last_event (gesture, sequence);
  event_type = gdk_event_get_event_type (event);

  if (event_type == GDK_TOUCH_BEGIN ||
      event_type == GDK_BUTTON_PRESS)
    gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
}

static void
gtk_scrolled_window_check_attach_pan_gesture (GtkScrolledWindow *sw)
{
  GtkPropagationPhase phase = GTK_PHASE_NONE;
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);

  if (priv->kinetic_scrolling &&
      ((may_hscroll (sw) && !may_vscroll (sw)) ||
       (!may_hscroll (sw) && may_vscroll (sw))))
    {
      GtkOrientation orientation;

      if (may_hscroll (sw))
        orientation = GTK_ORIENTATION_HORIZONTAL;
      else
        orientation = GTK_ORIENTATION_VERTICAL;

      gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
                                       orientation);
      phase = GTK_PHASE_CAPTURE;
    }

  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);
}

static void
indicator_set_over (Indicator *indicator,
                    gboolean   over)
{
  g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);

  if (indicator->over == over)
    return;

  indicator->over = over;

  if (indicator->over)
    gtk_widget_add_css_class (indicator->scrollbar, "hovering");
  else
    gtk_widget_remove_css_class (indicator->scrollbar, "hovering");

  gtk_widget_queue_resize (indicator->scrollbar);
}

static gboolean
coords_close_to_indicator (GtkScrolledWindow *sw,
                           Indicator         *indicator,
                           double             x,
                           double             y)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
  graphene_rect_t indicator_bounds;
  int distance;

  if (!gtk_widget_compute_bounds (indicator->scrollbar, GTK_WIDGET (sw), &indicator_bounds))
    return FALSE;

  if (indicator->over)
    distance = INDICATOR_FAR_DISTANCE;
  else
    distance = INDICATOR_CLOSE_DISTANCE;

  graphene_rect_inset (&indicator_bounds, - distance, - distance);

  if (indicator == &priv->hindicator)
    {
      if (y >= indicator_bounds.origin.y &&
          y < indicator_bounds.origin.y + indicator_bounds.size.height)
         return TRUE;
    }
  else if (indicator == &priv->vindicator)
    {
      if (x >= indicator_bounds.origin.x &&
          x < indicator_bounds.origin.x + indicator_bounds.size.width)
        return TRUE;
    }

  return FALSE;
}

static gboolean
enable_over_timeout_cb (gpointer user_data)
{
  Indicator *indicator = user_data;

  indicator_set_over (indicator, TRUE);
  return G_SOURCE_REMOVE;
}

static gboolean
check_update_scrollbar_proximity (GtkScrolledWindow *sw,
                                  Indicator         *indicator,
                                  GtkWidget         *target,
                                  double             x,
                                  double             y)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
  gboolean indicator_close, on_scrollbar, on_other_scrollbar;

  indicator_close = coords_close_to_indicator (sw, indicator, x, y);
  on_scrollbar = (target == indicator->scrollbar ||
                  gtk_widget_is_ancestor (target, indicator->scrollbar));
  on_other_scrollbar = (!on_scrollbar &&
                        (target == priv->hindicator.scrollbar ||
                         target == priv->vindicator.scrollbar ||
                         gtk_widget_is_ancestor (target, priv->hindicator.scrollbar) ||
                         gtk_widget_is_ancestor (target, priv->vindicator.scrollbar)));


  g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);

  if (on_scrollbar)
    indicator_set_over (indicator, TRUE);
  else if (indicator_close && !on_other_scrollbar)
    {
      indicator->over_timeout_id = g_timeout_add (30, enable_over_timeout_cb, indicator);
      gdk_source_set_static_name_by_id (indicator->over_timeout_id, "[gtk] enable_over_timeout_cb");
    }
  else
    indicator_set_over (indicator, FALSE);

  return indicator_close;
}

static double
get_wheel_detent_scroll_step (GtkScrolledWindow *sw,
                              GtkOrientation     orientation)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
  GtkScrollbar *scrollbar;
  GtkAdjustment *adj;
  double page_size;
  double scroll_step;

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    scrollbar = GTK_SCROLLBAR (priv->hscrollbar);
  else
    scrollbar = GTK_SCROLLBAR (priv->vscrollbar);

  if (!scrollbar)
    return 0;

  adj = gtk_scrollbar_get_adjustment (scrollbar);
  page_size = gtk_adjustment_get_page_size (adj);
  scroll_step = pow (page_size, 2.0 / 3.0);

  return scroll_step;
}

static gboolean
captured_scroll_cb (GtkEventControllerScroll *scroll,
                    double                    delta_x,
                    double                    delta_y,
                    GtkScrolledWindow        *scrolled_window)
{
  GtkScrolledWindowPrivate *priv =
    gtk_scrolled_window_get_instance_private (scrolled_window);
  int overshoot_x, overshoot_y;

  gtk_scrolled_window_cancel_deceleration (scrolled_window);

  if (!may_hscroll (scrolled_window) &&
      !may_vscroll (scrolled_window))
    priv->scrolling = FALSE;

  if (priv->scrolling &&
      priv->scrolling_modifiers !=
      gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll)))
    priv->scrolling = FALSE;

  if (!priv->scrolling)
    return GDK_EVENT_PROPAGATE;

  scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);

  _gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y);

  if (ABS (overshoot_x) == MAX_OVERSHOOT_DISTANCE ||
      ABS (overshoot_y) == MAX_OVERSHOOT_DISTANCE)
    {
      priv->scrolling = FALSE;
      return GDK_EVENT_PROPAGATE;
    }

  return GDK_EVENT_STOP;
}

static void
captured_motion (GtkEventController *controller,
                 double              x,
                 double              y,
                 GtkScrolledWindow  *sw)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
  GdkDevice *source_device;
  GdkInputSource input_source;
  GdkModifierType state;
  GdkEvent *event;
  GtkWidget *target;

  if (priv->prev_x != x || priv->prev_y != y)
    {
      gtk_scrolled_window_decelerate (sw, 00);
      priv->prev_x = x;
      priv->prev_y = y;
    }

  if (!priv->use_indicators)
    return;

  if (!priv->child)
    return;

  target = gtk_event_controller_get_target (controller);
  state = gtk_event_controller_get_current_event_state (controller);
  event = gtk_event_controller_get_current_event (controller);

  source_device = gdk_event_get_device (event);
  input_source = gdk_device_get_source (source_device);

  if (priv->hscrollbar_visible)
    indicator_start_fade (&priv->hindicator, 1.0);
  if (priv->vscrollbar_visible)
    indicator_start_fade (&priv->vindicator, 1.0);

  if ((target == priv->child ||
       gtk_widget_is_ancestor (target, priv->child)) &&
      (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) != 0)
    {
      indicator_set_over (&priv->hindicator, FALSE);
      indicator_set_over (&priv->vindicator, FALSE);
    }
  else if (input_source == GDK_SOURCE_PEN ||
           input_source == GDK_SOURCE_TRACKPOINT)
    {
      indicator_set_over (&priv->hindicator, TRUE);
      indicator_set_over (&priv->vindicator, TRUE);
    }
  else
    {
      if (!check_update_scrollbar_proximity (sw, &priv->vindicator, target, x, y))
        check_update_scrollbar_proximity (sw, &priv->hindicator, target, x, y);
      else
        indicator_set_over (&priv->hindicator, FALSE);
    }
}

static gboolean
start_scroll_deceleration_cb (gpointer user_data)
{
  GtkScrolledWindow *scrolled_window = user_data;
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  priv->scroll_events_overshoot_id = 0;

  if (!priv->deceleration_id)
    gtk_scrolled_window_start_deceleration (scrolled_window);

  return G_SOURCE_REMOVE;
}

static void
scroll_controller_scroll_begin (GtkEventControllerScroll *scroll,
                                GtkScrolledWindow        *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  priv->smooth_scroll = TRUE;
}

static void
stop_kinetic_scrolling_cb (GtkEventControllerScroll *scroll,
                           GtkScrolledWindow        *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (priv->hscrolling)
    gtk_kinetic_scrolling_stop (priv->hscrolling);

  if (priv->vscrolling)
    gtk_kinetic_scrolling_stop (priv->vscrolling);
}

static gboolean
scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
                        double                    delta_x,
                        double                    delta_y,
                        GtkEventControllerScroll *scroll)
{
  GtkScrolledWindowPrivate *priv =
    gtk_scrolled_window_get_instance_private (scrolled_window);
  gboolean shifted;
  GdkModifierType state;
  gboolean changed = FALSE;

  state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));
  shifted = (state & GDK_SHIFT_MASK) != 0;

  gtk_scrolled_window_invalidate_overshoot (scrolled_window);

  if (shifted)
    {
      double delta;

      delta = delta_x;
      delta_x = delta_y;
      delta_y = delta;
    }

  if (delta_x != 0.0 &&
      may_hscroll (scrolled_window))
    {
      GtkAdjustment *adj;
      double new_value;
      GdkScrollUnit scroll_unit;

      adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
      scroll_unit = gtk_event_controller_scroll_get_unit (scroll);

      if (scroll_unit == GDK_SCROLL_UNIT_WHEEL)
        {
          delta_x *= get_wheel_detent_scroll_step (scrolled_window,
                                                   GTK_ORIENTATION_HORIZONTAL);
        }
      else if (scroll_unit == GDK_SCROLL_UNIT_SURFACE)
        delta_x *= MAGIC_SCROLL_FACTOR;

      new_value = priv->unclamped_hadj_value + delta_x;
      _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj,
                                                 new_value);
      changed |= TRUE;
    }

  if (delta_y != 0.0 &&
      may_vscroll (scrolled_window))
    {
      GtkAdjustment *adj;
      double new_value;
      GdkScrollUnit scroll_unit;

      adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
      scroll_unit = gtk_event_controller_scroll_get_unit (scroll);

      if (scroll_unit == GDK_SCROLL_UNIT_WHEEL)
        {
          delta_y *= get_wheel_detent_scroll_step (scrolled_window,
                                                   GTK_ORIENTATION_VERTICAL);
        }
      else if (scroll_unit == GDK_SCROLL_UNIT_SURFACE)
        delta_y *= MAGIC_SCROLL_FACTOR;

      new_value = priv->unclamped_vadj_value + delta_y;
      _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj,
                                                 new_value);
      changed |= TRUE;
    }

  g_clear_handle_id (&priv->scroll_events_overshoot_id, g_source_remove);

  if (!priv->smooth_scroll &&
      _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
    {
      priv->scroll_events_overshoot_id =
        g_timeout_add (50, start_scroll_deceleration_cb, scrolled_window);
      gdk_source_set_static_name_by_id (priv->scroll_events_overshoot_id,
                                      "[gtk] start_scroll_deceleration_cb");
    }

  return changed;
}

static gboolean
scroll_controller_scroll (GtkEventControllerScroll *scroll,
                          double                    delta_x,
                          double                    delta_y,
                          GtkScrolledWindow        *scrolled_window)
{
  GtkScrolledWindowPrivate *priv =
    gtk_scrolled_window_get_instance_private (scrolled_window);
  gboolean scrolled_in_non_scrollable_dir = FALSE;

  if (!may_hscroll (scrolled_window) &&
      !may_vscroll (scrolled_window))
    return GDK_EVENT_PROPAGATE;

  if (!priv->scrolling)
    {
      if (scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll))
        {
          if (priv->smooth_scroll)
            {
              priv->scrolling = TRUE;
              priv->scrolling_modifiers =
                gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));
            }
        }
      else
        {
          scrolled_in_non_scrollable_dir =
            ((delta_x != 0 && !may_hscroll (scrolled_window)) ||
             (delta_y != 0 && !may_vscroll (scrolled_window)));
          if (scrolled_in_non_scrollable_dir)
            gtk_scrolled_window_decelerate (scrolled_window, 00);
        }
    }

  return (scrolled_in_non_scrollable_dir ||
          _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL)) ?
    GDK_EVENT_PROPAGATE : GDK_EVENT_STOP;
}

static void
scroll_controller_scroll_end (GtkEventControllerScroll *scroll,
                              GtkScrolledWindow        *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  priv->smooth_scroll = FALSE;
  priv->scrolling = FALSE;
}

static void
scroll_controller_decelerate (GtkEventControllerScroll *scroll,
                              double                    initial_vel_x,
                              double                    initial_vel_y,
                              GtkScrolledWindow        *scrolled_window)
{
  GtkScrolledWindowPrivate *priv =
    gtk_scrolled_window_get_instance_private (scrolled_window);
  GdkScrollUnit scroll_unit;
  gboolean shifted;
  GdkModifierType state;

  if (!priv->scrolling)
    return;

  scroll_unit = gtk_event_controller_scroll_get_unit (scroll);
  state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));

  shifted = (state & GDK_SHIFT_MASK) != 0;

  if (shifted)
    {
      double tmp;

      tmp = initial_vel_x;
      initial_vel_x = initial_vel_y;
      initial_vel_y = tmp;
    }

  if (scroll_unit == GDK_SCROLL_UNIT_WHEEL)
    {
      initial_vel_x *= get_wheel_detent_scroll_step (scrolled_window,
                                                     GTK_ORIENTATION_HORIZONTAL);

      initial_vel_y *= get_wheel_detent_scroll_step (scrolled_window,
                                                     GTK_ORIENTATION_VERTICAL);
    }
  else
    {
      initial_vel_x *= MAGIC_SCROLL_FACTOR;
      initial_vel_y *= MAGIC_SCROLL_FACTOR;
    }

  gtk_scrolled_window_decelerate (scrolled_window,
                                  initial_vel_x,
                                  initial_vel_y);
}

static void
gtk_scrolled_window_update_scrollbar_visibility_flags (GtkScrolledWindow *scrolled_window,
                                                       GtkWidget         *scrollbar)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkAdjustment *adjustment;

  if (scrollbar == NULL)
    return;

  adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (scrollbar));

  if (scrollbar == priv->hscrollbar)
    {
      if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
        {
          priv->hscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
                                      gtk_adjustment_get_page_size (adjustment));
        }
    }
  else if (scrollbar == priv->vscrollbar)
    {
      if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
        {
          priv->vscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
                                      gtk_adjustment_get_page_size (adjustment));
        }
    }
}

static void
gtk_scrolled_window_size_allocate (GtkWidget *widget,
                                   int        width,
                                   int        height,
                                   int        baseline)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkAllocation child_allocation;
  int sb_width;
  int sb_height;

  /* Get possible scrollbar dimensions */
  gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                      &sb_width, NULL, NULL, NULL);
  gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                      &sb_height, NULL, NULL, NULL);

  if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
    priv->hscrollbar_visible = TRUE;
  else if (priv->hscrollbar_policy == GTK_POLICY_NEVER ||
           priv->hscrollbar_policy == GTK_POLICY_EXTERNAL)
    priv->hscrollbar_visible = FALSE;

  if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
    priv->vscrollbar_visible = TRUE;
  else if (priv->vscrollbar_policy == GTK_POLICY_NEVER ||
           priv->vscrollbar_policy == GTK_POLICY_EXTERNAL)
    priv->vscrollbar_visible = FALSE;

  if (priv->child && gtk_widget_get_visible (priv->child))
    {
      int child_scroll_width;
      int child_scroll_height;
      gboolean previous_hvis;
      gboolean previous_vvis;
      guint count = 0;
      GtkScrollable *scrollable_child = GTK_SCROLLABLE (priv->child);
      GtkScrollablePolicy hscroll_policy = gtk_scrollable_get_hscroll_policy (scrollable_child);
      GtkScrollablePolicy vscroll_policy = gtk_scrollable_get_vscroll_policy (scrollable_child);

      /* Determine scrollbar visibility first via hfw apis */
      if (gtk_widget_get_request_mode (priv->child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
        {
          if (hscroll_policy == GTK_SCROLL_MINIMUM)
            gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL, -1,
                                &child_scroll_width, NULL, NULL, NULL);
          else
            gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL, -1,
                                NULL, &child_scroll_width, NULL, NULL);

          if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
            {
              /* First try without a vertical scrollbar if the content will fit the height
               * given the extra width of the scrollbar */

              if (vscroll_policy == GTK_SCROLL_MINIMUM)
                gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL,
                                    MAX (width, child_scroll_width),
                                    &child_scroll_height, NULL, NULL, NULL);
              else
                gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL,
                                    MAX (width, child_scroll_width),
                                    NULL, &child_scroll_height, NULL, NULL);

              if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
                {
                  /* Does the content height fit the allocation height ? */
                  priv->vscrollbar_visible = child_scroll_height > height;

                  /* Does the content width fit the allocation with minus a possible scrollbar ? */
                  priv->hscrollbar_visible = child_scroll_width > width -
                    (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);

                  /* Now that we've guessed the hscrollbar, does the content height fit
                   * the possible new allocation height ?
                   */

                  priv->vscrollbar_visible = child_scroll_height > height -
                    (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);

                  /* Now that we've guessed the vscrollbar, does the content width fit
                   * the possible new allocation width ?
                   */

                  priv->hscrollbar_visible = child_scroll_width > width -
                    (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);
                }
              else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
                {
                  priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
                  priv->vscrollbar_visible = child_scroll_height > height -
                    (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
                }
            }
          else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
            {
              priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);

              if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
                priv->hscrollbar_visible = child_scroll_width > width -
                  (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width);
              else
                priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
            }
        }
      else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
        {
          if (vscroll_policy == GTK_SCROLL_MINIMUM)
            gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL, -1,
                                &child_scroll_height, NULL, NULL, NULL);
          else
            gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL, -1,
                                NULL, &child_scroll_height, NULL, NULL);

          if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
            {
              /* First try without a horizontal scrollbar if the content will fit the width
               * given the extra height of the scrollbar */

              if (hscroll_policy == GTK_SCROLL_MINIMUM)
                gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL,
                                    MAX (height, child_scroll_height),
                                    &child_scroll_width, NULL, NULL, NULL);
              else
                gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL,
                                    MAX (height, child_scroll_height),
                                    NULL, &child_scroll_width, NULL, NULL);

              if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
                {
                  /* Does the content width fit the allocation width ? */
                  priv->hscrollbar_visible = child_scroll_width > width;

                  /* Does the content height fit the allocation with minus a possible scrollbar ? */
                  priv->vscrollbar_visible = child_scroll_height > height -
                    (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);

                  /* Now that we've guessed the vscrollbar, does the content width fit
                   * the possible new allocation width ?
                   */

                  priv->hscrollbar_visible = child_scroll_width > width -
                    (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);

                  /* Now that we've guessed the hscrollbar, does the content height fit
                   * the possible new allocation height ?
                   */

                  priv->vscrollbar_visible = child_scroll_height > height -
                    (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
                }
              else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
                {
                  priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
                  priv->hscrollbar_visible = child_scroll_width > width -
                    (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);
                }
            }
          else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
            {
              priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);

              if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
                priv->vscrollbar_visible = child_scroll_height > height -
                  (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
              else
                priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
            }
        }

      /* Now after guessing scrollbar visibility; fall back on the allocation loop which
       * observes the adjustments to detect scrollbar visibility and also avoids
       * infinite recursion
       */

      do
        {
          previous_hvis = priv->hscrollbar_visible;
          previous_vvis = priv->vscrollbar_visible;

          gtk_scrolled_window_allocate_child (scrolled_window, width, height);

          /* Explicitly force scrollbar visibility checks.
           *
           * Since we make a guess above, the child might not decide to update the adjustments
           * if they logically did not change since the last configuration
           *
           * These will update priv->hscrollbar_visible and priv->vscrollbar_visible.
           */

          gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window,
                                                                 priv->hscrollbar);

          gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window,
                                                                 priv->vscrollbar);

          /* If, after the first iteration, the hscrollbar and the
           * vscrollbar flip visibility... or if one of the scrollbars flip
           * on each iteration indefinitely/infinitely, then we just need both
           * at this size.
           */

          if ((count &&
               previous_hvis != priv->hscrollbar_visible &&
               previous_vvis != priv->vscrollbar_visible) || count > 3)
            {
              priv->hscrollbar_visible = TRUE;
              priv->vscrollbar_visible = TRUE;

              gtk_scrolled_window_allocate_child (scrolled_window, width, height);

              break;
            }

          count++;
        }
      while (previous_hvis != priv->hscrollbar_visible ||
             previous_vvis != priv->vscrollbar_visible);
    }
  else
    {
      priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
      priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
    }

  gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible);
  if (priv->hscrollbar_visible)
    {
      gtk_scrolled_window_allocate_scrollbar (scrolled_window,
                                              priv->hscrollbar,
                                              &child_allocation);
      gtk_widget_size_allocate (priv->hscrollbar, &child_allocation, -1);
    }

  gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible);
  if (priv->vscrollbar_visible)
    {
      gtk_scrolled_window_allocate_scrollbar (scrolled_window,
                                              priv->vscrollbar,
                                              &child_allocation);
      gtk_widget_size_allocate (priv->vscrollbar, &child_allocation, -1);
    }

  gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
}

static void
gtk_scrolled_window_measure (GtkWidget      *widget,
                             GtkOrientation  orientation,
                             int             for_size,
                             int            *minimum_size,
                             int            *natural_size,
                             int            *minimum_baseline,
                             int            *natural_baseline)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  int minimum_req = 0, natural_req = 0;
  GtkBorder sborder = { 0 };
  gboolean need_child_size = FALSE;

  if (priv->child)
    gtk_scrollable_get_border (GTK_SCROLLABLE (priv->child), &sborder);

  /*
   * First collect the child requisition, if we want to.
   */

  if (orientation == GTK_ORIENTATION_VERTICAL)
    need_child_size = priv->propagate_natural_height || priv->vscrollbar_policy == GTK_POLICY_NEVER;
  else
    need_child_size = priv->propagate_natural_width || priv->hscrollbar_policy == GTK_POLICY_NEVER;

  if (priv->child && gtk_widget_get_visible (priv->child) && need_child_size)
    {
      int min_child_size, nat_child_size;
      int child_for_size = -1;

      /* We can pass on the requested size if we have a scrollbar policy that prevents scrolling in that direction */
      if (for_size != -1 &&
          ((orientation == GTK_ORIENTATION_VERTICAL && priv->hscrollbar_policy == GTK_POLICY_NEVER)
           || (orientation == GTK_ORIENTATION_HORIZONTAL && priv->vscrollbar_policy == GTK_POLICY_NEVER)))
        {
          child_for_size = for_size;

          /* If the other scrollbar is always visible and not an overlay scrollbar we must subtract it from the measure */
          if (orientation == GTK_ORIENTATION_VERTICAL && !priv->use_indicators && priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
            {
              int min_scrollbar_width;

              gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                                  &min_scrollbar_width, NULL,
                                  NULL, NULL);

              child_for_size = MAX (0, child_for_size - min_scrollbar_width);
            }
          if (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->use_indicators && priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
            {
              int min_scrollbar_height;

              gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                              &min_scrollbar_height, NULL,
                              NULL, NULL);

              child_for_size = MAX (0, child_for_size - min_scrollbar_height);
            }
        }

      gtk_widget_measure (priv->child, orientation, child_for_size,
                          &min_child_size, &nat_child_size,
                          NULL, NULL);

      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        {
          if (priv->propagate_natural_width)
            natural_req += nat_child_size;

          if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
            {
              minimum_req += min_child_size;
            }
          else
            {
              int min = priv->min_content_width >= 0 ? priv->min_content_width : 0;
              int max = priv->max_content_width >= 0 ? priv->max_content_width : G_MAXINT;

              minimum_req = CLAMP (minimum_req, min, max);
              natural_req = CLAMP (natural_req, min, max);
            }
        }
      else /* GTK_ORIENTATION_VERTICAL */
        {
          if (priv->propagate_natural_height)
            natural_req += nat_child_size;

          if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
            {
              minimum_req += min_child_size;
            }
          else
            {
              int min = priv->min_content_height >= 0 ? priv->min_content_height : 0;
              int max = priv->max_content_height >= 0 ? priv->max_content_height : G_MAXINT;

              minimum_req = CLAMP (minimum_req, min, max);
              natural_req = CLAMP (natural_req, min, max);
            }
        }
    }
  else
    {
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        minimum_req = MAX (0, priv->min_content_width);
      else
        minimum_req = MAX (0, priv->min_content_height);
    }

  /* Ensure we make requests with natural size >= minimum size */
  natural_req = MAX (minimum_req, natural_req);

  /*
   * Now add to the requisition any additional space for surrounding scrollbars
   * and the special scrollable border.
   */

  if (orientation == GTK_ORIENTATION_HORIZONTAL && policy_may_be_visible (priv->hscrollbar_policy))
    {
      int min_scrollbar_width, nat_scrollbar_width;

      gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                          &min_scrollbar_width, &nat_scrollbar_width,
                          NULL, NULL);
      minimum_req = MAX (minimum_req, min_scrollbar_width + sborder.left + sborder.right);
      natural_req = MAX (natural_req, nat_scrollbar_width + sborder.left + sborder.right);
    }
  else if (orientation == GTK_ORIENTATION_VERTICAL && policy_may_be_visible (priv->vscrollbar_policy))
    {
      int min_scrollbar_height, nat_scrollbar_height;

      gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                          &min_scrollbar_height, &nat_scrollbar_height,
                          NULL, NULL);
      minimum_req = MAX (minimum_req, min_scrollbar_height + sborder.top + sborder.bottom);
      natural_req = MAX (natural_req, nat_scrollbar_height + sborder.top + sborder.bottom);
    }

  if (!priv->use_indicators)
    {
      if (orientation == GTK_ORIENTATION_VERTICAL && priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
        {
          int min_scrollbar_height, nat_scrollbar_height;

          gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                              &min_scrollbar_height, &nat_scrollbar_height,
                              NULL, NULL);

          minimum_req += min_scrollbar_height;
          natural_req += nat_scrollbar_height;
        }
      else if (orientation == GTK_ORIENTATION_HORIZONTAL && priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
        {
          int min_scrollbar_width, nat_scrollbar_width;

          gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                              &min_scrollbar_width, &nat_scrollbar_width,
                              NULL, NULL);
          minimum_req += min_scrollbar_width;
          natural_req += nat_scrollbar_width;
        }
    }

  *minimum_size = minimum_req;
  *natural_size = natural_req;
}

static void
gtk_scrolled_window_snapshot_scrollbars_junction (GtkScrolledWindow *scrolled_window,
                                                  GtkSnapshot       *snapshot)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  graphene_rect_t hscr_bounds, vscr_bounds;
  GtkCssStyle *style;
  GtkCssBoxes boxes;

  if (!gtk_widget_compute_bounds (GTK_WIDGET (priv->hscrollbar), GTK_WIDGET (scrolled_window), &hscr_bounds))
    return;

  if (!gtk_widget_compute_bounds (GTK_WIDGET (priv->vscrollbar), GTK_WIDGET (scrolled_window), &vscr_bounds))
    return;

  style = gtk_css_node_get_style (priv->junction_node);

  gtk_css_boxes_init_border_box (&boxes, style,
                                 vscr_bounds.origin.x, hscr_bounds.origin.y,
                                 vscr_bounds.size.width, hscr_bounds.size.height);

  gtk_css_style_snapshot_background (&boxes, snapshot);
  gtk_css_style_snapshot_border (&boxes, snapshot);
}

static void
gtk_scrolled_window_snapshot_overshoot (GtkScrolledWindow *scrolled_window,
                                        GtkSnapshot       *snapshot)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  int overshoot_x, overshoot_y;
  GtkCssStyle *style;
  GdkRectangle rect;
  GtkCssBoxes boxes;

  if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
    return;

  gtk_scrolled_window_inner_allocation (scrolled_window, &rect);

  overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
  overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);

  if (overshoot_x > 0)
    {
      style = gtk_css_node_get_style (priv->overshoot_node[GTK_POS_RIGHT]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }
  else if (overshoot_x < 0)
    {
      style = gtk_css_node_get_style (priv->overshoot_node[GTK_POS_LEFT]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x, rect.y, -overshoot_x, rect.height);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }

  if (overshoot_y > 0)
    {
      style = gtk_css_node_get_style (priv->overshoot_node[GTK_POS_BOTTOM]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }
  else if (overshoot_y < 0)
    {
      style = gtk_css_node_get_style (priv->overshoot_node[GTK_POS_TOP]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x, rect.y, rect.width, -overshoot_y);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }
}

static void
gtk_scrolled_window_snapshot_undershoot (GtkScrolledWindow *scrolled_window,
                                         GtkSnapshot       *snapshot)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkCssStyle *style;
  GdkRectangle rect;
  GtkAdjustment *adj;
  GtkCssBoxes boxes;

  gtk_scrolled_window_inner_allocation (scrolled_window, &rect);

  adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
  if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
    {
      style = gtk_css_node_get_style (priv->undershoot_node[GTK_POS_RIGHT]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }
  if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
    {
      style = gtk_css_node_get_style (priv->undershoot_node[GTK_POS_LEFT]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }

  adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
  if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
    {
      style = gtk_css_node_get_style (priv->undershoot_node[GTK_POS_BOTTOM]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }
  if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
    {
      style = gtk_css_node_get_style (priv->undershoot_node[GTK_POS_TOP]);
      gtk_css_boxes_init_border_box (&boxes, style,
                                     rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
      gtk_css_style_snapshot_background (&boxes, snapshot);
      gtk_css_style_snapshot_border (&boxes, snapshot);
    }
}

static void
gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
{
  GtkWidget *widget = GTK_WIDGET (scrolled_window);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkEventController *controller;
  GtkCssNode *widget_node;
  GQuark classes[4] = {
    g_quark_from_static_string ("left"),
    g_quark_from_static_string ("right"),
    g_quark_from_static_string ("top"),
    g_quark_from_static_string ("bottom"),
  };
  int i;

  gtk_widget_set_focusable (widget, TRUE);

  /* Instantiated by gtk_scrolled_window_set_[hv]adjustment
   * which are both construct properties
   */

  priv->hscrollbar = NULL;
  priv->vscrollbar = NULL;
  priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
  priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
  priv->hscrollbar_visible = FALSE;
  priv->vscrollbar_visible = FALSE;
  priv->focus_out = FALSE;
  priv->auto_added_viewport = FALSE;
  priv->window_placement = GTK_CORNER_TOP_LEFT;
  priv->min_content_width = -1;
  priv->min_content_height = -1;
  priv->max_content_width = -1;
  priv->max_content_height = -1;

  priv->overlay_scrolling = TRUE;

  priv->drag_gesture = gtk_gesture_drag_new ();
  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
  g_signal_connect_swapped (priv->drag_gesture, "drag-begin",
                            G_CALLBACK (scrolled_window_drag_begin_cb),
                            scrolled_window);
  g_signal_connect_swapped (priv->drag_gesture, "drag-update",
                            G_CALLBACK (scrolled_window_drag_update_cb),
                            scrolled_window);
  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->drag_gesture));

  priv->pan_gesture = gtk_gesture_pan_new (GTK_ORIENTATION_VERTICAL);
  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE);
  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->pan_gesture));
  gtk_gesture_group (priv->pan_gesture, priv->drag_gesture);

  priv->swipe_gesture = gtk_gesture_swipe_new ();
  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE);
  g_signal_connect_swapped (priv->swipe_gesture, "swipe",
                            G_CALLBACK (scrolled_window_swipe_cb),
                            scrolled_window);
  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->swipe_gesture));
  gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture);

  priv->long_press_gesture = gtk_gesture_long_press_new ();
  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
  g_signal_connect_swapped (priv->long_press_gesture, "pressed",
                            G_CALLBACK (scrolled_window_long_press_cb),
                            scrolled_window);
  g_signal_connect_swapped (priv->long_press_gesture, "cancelled",
                            G_CALLBACK (scrolled_window_long_press_cancelled_cb),
                            scrolled_window);
  gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->long_press_gesture));
  gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture);

  gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);

  controller = gtk_event_controller_motion_new ();
  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
  g_signal_connect (controller, "motion",
                    G_CALLBACK (captured_motion), scrolled_window);
  gtk_widget_add_controller (widget, controller);

  widget_node = gtk_widget_get_css_node (widget);
  for (i = 0; i < 4; i++)
    {
      priv->overshoot_node[i] = gtk_css_node_new ();
      gtk_css_node_set_name (priv->overshoot_node[i], g_quark_from_static_string ("overshoot"));
      gtk_css_node_add_class (priv->overshoot_node[i], classes[i]);
      gtk_css_node_set_parent (priv->overshoot_node[i], widget_node);
      gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node));
      g_object_unref (priv->overshoot_node[i]);

      priv->undershoot_node[i] = gtk_css_node_new ();
      gtk_css_node_set_name (priv->undershoot_node[i], g_quark_from_static_string ("undershoot"));
      gtk_css_node_add_class (priv->undershoot_node[i], classes[i]);
      gtk_css_node_set_parent (priv->undershoot_node[i], widget_node);
      gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node));
      g_object_unref (priv->undershoot_node[i]);
    }

  gtk_scrolled_window_update_use_indicators (scrolled_window);

  priv->junction_node = gtk_css_node_new ();
  gtk_css_node_set_name (priv->junction_node, g_quark_from_static_string ("junction"));
  gtk_css_node_set_parent (priv->junction_node, widget_node);
  gtk_css_node_set_state (priv->junction_node, gtk_css_node_get_state (widget_node));
  g_object_unref (priv->junction_node);

  controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES |
                                                GTK_EVENT_CONTROLLER_SCROLL_KINETIC);
  g_signal_connect (controller, "scroll-begin",
                    G_CALLBACK (scroll_controller_scroll_begin), scrolled_window);
  g_signal_connect (controller, "scroll",
                    G_CALLBACK (scroll_controller_scroll), scrolled_window);
  g_signal_connect (controller, "scroll-end",
                    G_CALLBACK (scroll_controller_scroll_end), scrolled_window);
  gtk_widget_add_controller (widget, controller);

  controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES |
                                                GTK_EVENT_CONTROLLER_SCROLL_KINETIC);
  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
  g_signal_connect (controller, "scroll-begin",
                    G_CALLBACK (stop_kinetic_scrolling_cb), scrolled_window);
  g_signal_connect (controller, "scroll",
                    G_CALLBACK (captured_scroll_cb), scrolled_window);
  g_signal_connect (controller, "decelerate",
                    G_CALLBACK (scroll_controller_decelerate), scrolled_window);
  gtk_widget_add_controller (widget, controller);

  controller = gtk_event_controller_motion_new ();
  g_signal_connect (controller, "leave",
                    G_CALLBACK (motion_controller_leave), scrolled_window);
  gtk_widget_add_controller (widget, controller);
}

/**
 * gtk_scrolled_window_new:
 *
 * Creates a new scrolled window.
 *
 * Returns: a new scrolled window
 */

GtkWidget *
gtk_scrolled_window_new (void)
{
  return g_object_new (GTK_TYPE_SCROLLED_WINDOW, NULL);
}

/**
 * gtk_scrolled_window_set_hadjustment:
 * @scrolled_window: a `GtkScrolledWindow`
 * @hadjustment: (nullable): the `GtkAdjustment` to use, or %NULL to create a new one
 *
 * Sets the `GtkAdjustment` for the horizontal scrollbar.
 */

void
gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
                                     GtkAdjustment     *hadjustment)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (hadjustment)
    g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
  else
    hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);

  if (!priv->hscrollbar)
    {
      priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);

      gtk_widget_insert_before (priv->hscrollbar, GTK_WIDGET (scrolled_window), priv->vscrollbar);
      update_scrollbar_positions (scrolled_window);
    }
  else
    {
      GtkAdjustment *old_adjustment;

      old_adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
      if (old_adjustment == hadjustment)
        return;

      g_signal_handlers_disconnect_by_func (old_adjustment,
                                            gtk_scrolled_window_adjustment_changed,
                                            scrolled_window);
      g_signal_handlers_disconnect_by_func (old_adjustment,
                                            gtk_scrolled_window_adjustment_value_changed,
                                            scrolled_window);

      gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
      gtk_scrollbar_set_adjustment (GTK_SCROLLBAR (priv->hscrollbar), hadjustment);
    }

  hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));

  g_signal_connect (hadjustment,
                    "changed",
                    G_CALLBACK (gtk_scrolled_window_adjustment_changed),
                    scrolled_window);
  g_signal_connect (hadjustment,
                    "value-changed",
                    G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
                    scrolled_window);

  gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
  gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);

  if (priv->child)
    gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (priv->child), hadjustment);

  if (gtk_widget_should_animate (GTK_WIDGET (scrolled_window)))
    gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);

  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]);
}

/**
 * gtk_scrolled_window_set_vadjustment:
 * @scrolled_window: a `GtkScrolledWindow`
 * @vadjustment: (nullable): the `GtkAdjustment` to use, or %NULL to create a new one
 *
 * Sets the `GtkAdjustment` for the vertical scrollbar.
 */

void
gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
                                     GtkAdjustment     *vadjustment)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (vadjustment)
    g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
  else
    vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);

  if (!priv->vscrollbar)
    {
      priv->vscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadjustment);

      gtk_widget_insert_after (priv->vscrollbar, GTK_WIDGET (scrolled_window), priv->hscrollbar);
      update_scrollbar_positions (scrolled_window);
    }
  else
    {
      GtkAdjustment *old_adjustment;

      old_adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
      if (old_adjustment == vadjustment)
        return;

      g_signal_handlers_disconnect_by_func (old_adjustment,
                                            gtk_scrolled_window_adjustment_changed,
                                            scrolled_window);
      g_signal_handlers_disconnect_by_func (old_adjustment,
                                            gtk_scrolled_window_adjustment_value_changed,
                                            scrolled_window);

      gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
      gtk_scrollbar_set_adjustment (GTK_SCROLLBAR (priv->vscrollbar), vadjustment);
    }

  vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));

  g_signal_connect (vadjustment,
                    "changed",
                    G_CALLBACK (gtk_scrolled_window_adjustment_changed),
                    scrolled_window);
  g_signal_connect (vadjustment,
                    "value-changed",
                    G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
                    scrolled_window);

  gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
  gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);

  if (priv->child)
    gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (priv->child), vadjustment);

  if (gtk_widget_should_animate (GTK_WIDGET (scrolled_window)))
    gtk_adjustment_enable_animation (vadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);

  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_VADJUSTMENT]);
}

/**
 * gtk_scrolled_window_get_hadjustment:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the horizontal scrollbars adjustment.
 *
 * This is the adjustment used to connect the horizontal scrollbar
 * to the child widgets horizontal scroll functionality.
 *
 * Returns: (transfer none): the horizontal `GtkAdjustment`
 */

GtkAdjustment*
gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);

  return gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
}

/**
 * gtk_scrolled_window_get_vadjustment:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the vertical scrollbars adjustment.
 *
 * This is the adjustment used to connect the vertical
 * scrollbar to the child widgets vertical scroll functionality.
 *
 * Returns: (transfer none): the vertical `GtkAdjustment`
 */

GtkAdjustment*
gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);

  return gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
}

/**
 * gtk_scrolled_window_get_hscrollbar:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the horizontal scrollbar of @scrolled_window.
 *
 * Returns: (transfer none): the horizontal scrollbar of the scrolled window.
 */

GtkWidget*
gtk_scrolled_window_get_hscrollbar (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);

  return priv->hscrollbar;
}

/**
 * gtk_scrolled_window_get_vscrollbar:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the vertical scrollbar of @scrolled_window.
 *
 * Returns: (transfer none): the vertical scrollbar of the scrolled window.
 */

GtkWidget*
gtk_scrolled_window_get_vscrollbar (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);

  return priv->vscrollbar;
}

/**
 * gtk_scrolled_window_set_policy:
 * @scrolled_window: a `GtkScrolledWindow`
 * @hscrollbar_policy: policy for horizontal bar
 * @vscrollbar_policy: policy for vertical bar
 *
 * Sets the scrollbar policy for the horizontal and vertical scrollbars.
 *
 * The policy determines when the scrollbar should appear; it is a value
 * from the [enum@Gtk.PolicyType] enumeration. If %GTK_POLICY_ALWAYS, the
 * scrollbar is always present; if %GTK_POLICY_NEVER, the scrollbar is
 * never present; if %GTK_POLICY_AUTOMATIC, the scrollbar is present only
 * if needed (that is, if the slider part of the bar would be smaller
 * than the trough  the display is larger than the page size).
 */

void
gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
                                GtkPolicyType      hscrollbar_policy,
                                GtkPolicyType      vscrollbar_policy)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GObject *object = G_OBJECT (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if ((priv->hscrollbar_policy != hscrollbar_policy) ||
      (priv->vscrollbar_policy != vscrollbar_policy))
    {
      priv->hscrollbar_policy = hscrollbar_policy;
      priv->vscrollbar_policy = vscrollbar_policy;

      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));

      g_object_notify_by_pspec (object, properties[PROP_HSCROLLBAR_POLICY]);
      g_object_notify_by_pspec (object, properties[PROP_VSCROLLBAR_POLICY]);
    }
}

/**
 * gtk_scrolled_window_get_policy:
 * @scrolled_window: a `GtkScrolledWindow`
 * @hscrollbar_policy: (out) (optional): location to store the policy
 *   for the horizontal scrollbar
 * @vscrollbar_policy: (out) (optional): location to store the policy
 *   for the vertical scrollbar
 *
 * Retrieves the current policy values for the horizontal and vertical
 * scrollbars.
 *
 * See [method@Gtk.ScrolledWindow.set_policy].
 */

void
gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
                                GtkPolicyType     *hscrollbar_policy,
                                GtkPolicyType     *vscrollbar_policy)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (hscrollbar_policy)
    *hscrollbar_policy = priv->hscrollbar_policy;
  if (vscrollbar_policy)
    *vscrollbar_policy = priv->vscrollbar_policy;
}

/**
 * gtk_scrolled_window_set_placement: (set-property window-placement)
 * @scrolled_window: a `GtkScrolledWindow`
 * @window_placement: position of the child window
 *
 * Sets the placement of the contents with respect to the scrollbars
 * for the scrolled window.
 *
 * The default is %GTK_CORNER_TOP_LEFT, meaning the child is
 * in the top left, with the scrollbars underneath and to the right.
 * Other values in [enum@Gtk.CornerType] are %GTK_CORNER_TOP_RIGHT,
 * %GTK_CORNER_BOTTOM_LEFT, and %GTK_CORNER_BOTTOM_RIGHT.
 *
 * See also [method@Gtk.ScrolledWindow.get_placement] and
 * [method@Gtk.ScrolledWindow.unset_placement].
 */

void
gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
                                   GtkCornerType      window_placement)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (priv->window_placement != window_placement)
    {
      priv->window_placement = window_placement;
      update_scrollbar_positions (scrolled_window);

      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));

      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_WINDOW_PLACEMENT]);
    }
}

/**
 * gtk_scrolled_window_get_placement: (get-property window-placement)
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Gets the placement of the contents with respect to the scrollbars.
 *
 * Returns: the current placement value.
 */

GtkCornerType
gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);

  return priv->window_placement;
}

/**
 * gtk_scrolled_window_unset_placement:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Unsets the placement of the contents with respect to the scrollbars.
 *
 * If no window placement is set for a scrolled window,
 * it defaults to %GTK_CORNER_TOP_LEFT.
 */

void
gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window)
{
  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  gtk_scrolled_window_set_placement (scrolled_window, GTK_CORNER_TOP_LEFT);
}

/**
 * gtk_scrolled_window_set_has_frame:
 * @scrolled_window: a `GtkScrolledWindow`
 * @has_frame: whether to draw a frame around scrolled window contents
 *
 * Changes the frame drawn around the contents of @scrolled_window.
 */

void
gtk_scrolled_window_set_has_frame (GtkScrolledWindow *scrolled_window,
                                   gboolean           has_frame)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (priv->has_frame == !!has_frame)
    return;

  priv->has_frame = has_frame;

  if (has_frame)
    gtk_widget_add_css_class (GTK_WIDGET (scrolled_window), "frame");
  else
    gtk_widget_remove_css_class (GTK_WIDGET (scrolled_window), "frame");

  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HAS_FRAME]);
}

/**
 * gtk_scrolled_window_get_has_frame:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Gets whether the scrolled window draws a frame.
 *
 * Returns: %TRUE if the @scrolled_window has a frame
 */

gboolean
gtk_scrolled_window_get_has_frame (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);

  return priv->has_frame;
}

/**
 * gtk_scrolled_window_set_kinetic_scrolling:
 * @scrolled_window: a `GtkScrolledWindow`
 * @kinetic_scrolling: %TRUE to enable kinetic scrolling
 *
 * Turns kinetic scrolling on or off.
 *
 * Kinetic scrolling only applies to devices with source
 * %GDK_SOURCE_TOUCHSCREEN.
 **/

void
gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
                                           gboolean           kinetic_scrolling)
{
  GtkPropagationPhase phase = GTK_PHASE_NONE;
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (priv->kinetic_scrolling == kinetic_scrolling)
    return;

  priv->kinetic_scrolling = kinetic_scrolling;
  gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);

  if (priv->kinetic_scrolling)
    phase = GTK_PHASE_CAPTURE;
  else
    gtk_scrolled_window_cancel_deceleration (scrolled_window);

  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture), phase);
  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->swipe_gesture), phase);
  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->long_press_gesture), phase);
  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);

  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_KINETIC_SCROLLING]);
}

/**
 * gtk_scrolled_window_get_kinetic_scrolling:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the specified kinetic scrolling behavior.
 *
 * Returns: the scrolling behavior flags.
 */

gboolean
gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);

  return priv->kinetic_scrolling;
}

static void
gtk_scrolled_window_dispose (GObject *object)
{
  GtkScrolledWindow *self = GTK_SCROLLED_WINDOW (object);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (self);

  g_clear_pointer (&priv->child, gtk_widget_unparent);

  remove_indicator (self, &priv->hindicator);
  remove_indicator (self, &priv->vindicator);

  if (priv->hscrollbar)
    {
      GtkAdjustment *hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));

      g_signal_handlers_disconnect_by_data (hadjustment, self);
      g_signal_handlers_disconnect_by_data (hadjustment, &priv->hindicator);

      gtk_widget_unparent (priv->hscrollbar);
      priv->hscrollbar = NULL;
    }

  if (priv->vscrollbar)
    {
      GtkAdjustment *vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));

      g_signal_handlers_disconnect_by_data (vadjustment, self);
      g_signal_handlers_disconnect_by_data (vadjustment, &priv->vindicator);

      gtk_widget_unparent (priv->vscrollbar);
      priv->vscrollbar = NULL;
    }

  if (priv->deceleration_id)
    {
      gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->deceleration_id);
      priv->deceleration_id = 0;
    }

  g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
  g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
  g_clear_handle_id (&priv->scroll_events_overshoot_id, g_source_remove);

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

static void
gtk_scrolled_window_set_property (GObject      *object,
                                  guint         prop_id,
                                  const GValue *value,
                                  GParamSpec   *pspec)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  switch (prop_id)
    {
    case PROP_HADJUSTMENT:
      gtk_scrolled_window_set_hadjustment (scrolled_window,
                                           g_value_get_object (value));
      break;
    case PROP_VADJUSTMENT:
      gtk_scrolled_window_set_vadjustment (scrolled_window,
                                           g_value_get_object (value));
      break;
    case PROP_HSCROLLBAR_POLICY:
      gtk_scrolled_window_set_policy (scrolled_window,
                                      g_value_get_enum (value),
                                      priv->vscrollbar_policy);
      break;
    case PROP_VSCROLLBAR_POLICY:
      gtk_scrolled_window_set_policy (scrolled_window,
                                      priv->hscrollbar_policy,
                                      g_value_get_enum (value));
      break;
    case PROP_WINDOW_PLACEMENT:
      gtk_scrolled_window_set_placement (scrolled_window,
                                         g_value_get_enum (value));
      break;
    case PROP_HAS_FRAME:
      gtk_scrolled_window_set_has_frame (scrolled_window,
                                         g_value_get_boolean (value));
      break;
    case PROP_MIN_CONTENT_WIDTH:
      gtk_scrolled_window_set_min_content_width (scrolled_window,
                                                 g_value_get_int (value));
      break;
    case PROP_MIN_CONTENT_HEIGHT:
      gtk_scrolled_window_set_min_content_height (scrolled_window,
                                                  g_value_get_int (value));
      break;
    case PROP_KINETIC_SCROLLING:
      gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
                                                 g_value_get_boolean (value));
      break;
    case PROP_OVERLAY_SCROLLING:
      gtk_scrolled_window_set_overlay_scrolling (scrolled_window,
                                                 g_value_get_boolean (value));
      break;
    case PROP_MAX_CONTENT_WIDTH:
      gtk_scrolled_window_set_max_content_width (scrolled_window,
                                                 g_value_get_int (value));
      break;
    case PROP_MAX_CONTENT_HEIGHT:
      gtk_scrolled_window_set_max_content_height (scrolled_window,
                                                  g_value_get_int (value));
      break;
    case PROP_PROPAGATE_NATURAL_WIDTH:
      gtk_scrolled_window_set_propagate_natural_width (scrolled_window,
                                                       g_value_get_boolean (value));
      break;
    case PROP_PROPAGATE_NATURAL_HEIGHT:
      gtk_scrolled_window_set_propagate_natural_height (scrolled_window,
                                                       g_value_get_boolean (value));
      break;
    case PROP_CHILD:
      gtk_scrolled_window_set_child (scrolled_window, g_value_get_object (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_scrolled_window_get_property (GObject    *object,
                                  guint       prop_id,
                                  GValue     *value,
                                  GParamSpec *pspec)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  switch (prop_id)
    {
    case PROP_HADJUSTMENT:
      g_value_set_object (value,
                          G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
      break;
    case PROP_VADJUSTMENT:
      g_value_set_object (value,
                          G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
      break;
    case PROP_WINDOW_PLACEMENT:
      g_value_set_enum (value, priv->window_placement);
      break;
    case PROP_HAS_FRAME:
      g_value_set_boolean (value, priv->has_frame);
      break;
    case PROP_HSCROLLBAR_POLICY:
      g_value_set_enum (value, priv->hscrollbar_policy);
      break;
    case PROP_VSCROLLBAR_POLICY:
      g_value_set_enum (value, priv->vscrollbar_policy);
      break;
    case PROP_MIN_CONTENT_WIDTH:
      g_value_set_int (value, priv->min_content_width);
      break;
    case PROP_MIN_CONTENT_HEIGHT:
      g_value_set_int (value, priv->min_content_height);
      break;
    case PROP_KINETIC_SCROLLING:
      g_value_set_boolean (value, priv->kinetic_scrolling);
      break;
    case PROP_OVERLAY_SCROLLING:
      g_value_set_boolean (value, priv->overlay_scrolling);
      break;
    case PROP_MAX_CONTENT_WIDTH:
      g_value_set_int (value, priv->max_content_width);
      break;
    case PROP_MAX_CONTENT_HEIGHT:
      g_value_set_int (value, priv->max_content_height);
      break;
    case PROP_PROPAGATE_NATURAL_WIDTH:
      g_value_set_boolean (value, priv->propagate_natural_width);
      break;
    case PROP_PROPAGATE_NATURAL_HEIGHT:
      g_value_set_boolean (value, priv->propagate_natural_height);
      break;
    case PROP_CHILD:
      g_value_set_object (value, gtk_scrolled_window_get_child (scrolled_window));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_scrolled_window_inner_allocation (GtkScrolledWindow *scrolled_window,
                                      GtkAllocation     *rect)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkBorder border = { 0 };

  gtk_scrolled_window_relative_allocation (scrolled_window, rect);
  rect->x = 0;
  rect->y = 0;
  if (priv->child && gtk_scrollable_get_border (GTK_SCROLLABLE (priv->child), &border))
    {
      rect->x += border.left;
      rect->y += border.top;
      rect->width -= border.left + border.right;
      rect->height -= border.top + border.bottom;
    }
}

static void
gtk_scrolled_window_snapshot (GtkWidget   *widget,
                              GtkSnapshot *snapshot)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (priv->hscrollbar_visible &&
      priv->vscrollbar_visible &&
      !priv->use_indicators)
    gtk_scrolled_window_snapshot_scrollbars_junction (scrolled_window, snapshot);

  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->snapshot (widget, snapshot);

  gtk_scrolled_window_snapshot_undershoot (scrolled_window, snapshot);
  gtk_scrolled_window_snapshot_overshoot (scrolled_window, snapshot);
}

static gboolean
gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
                                  GtkScrollType      scroll,
                                  gboolean           horizontal)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkAdjustment *adjustment = NULL;

  switch (scroll)
    {
    case GTK_SCROLL_STEP_UP:
      scroll = GTK_SCROLL_STEP_BACKWARD;
      horizontal = FALSE;
      break;
    case GTK_SCROLL_STEP_DOWN:
      scroll = GTK_SCROLL_STEP_FORWARD;
      horizontal = FALSE;
      break;
    case GTK_SCROLL_STEP_LEFT:
      scroll = GTK_SCROLL_STEP_BACKWARD;
      horizontal = TRUE;
      break;
    case GTK_SCROLL_STEP_RIGHT:
      scroll = GTK_SCROLL_STEP_FORWARD;
      horizontal = TRUE;
      break;
    case GTK_SCROLL_PAGE_UP:
      scroll = GTK_SCROLL_PAGE_BACKWARD;
      horizontal = FALSE;
      break;
    case GTK_SCROLL_PAGE_DOWN:
      scroll = GTK_SCROLL_PAGE_FORWARD;
      horizontal = FALSE;
      break;
    case GTK_SCROLL_PAGE_LEFT:
      scroll = GTK_SCROLL_STEP_BACKWARD;
      horizontal = TRUE;
      break;
    case GTK_SCROLL_PAGE_RIGHT:
      scroll = GTK_SCROLL_STEP_FORWARD;
      horizontal = TRUE;
      break;
    case GTK_SCROLL_STEP_BACKWARD:
    case GTK_SCROLL_STEP_FORWARD:
    case GTK_SCROLL_PAGE_BACKWARD:
    case GTK_SCROLL_PAGE_FORWARD:
    case GTK_SCROLL_START:
    case GTK_SCROLL_END:
      break;
    case GTK_SCROLL_NONE:
    case GTK_SCROLL_JUMP:
    default:
      g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
      return FALSE;
    }

  if (horizontal)
    {
      if (may_hscroll (scrolled_window))
        adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
      else
        return FALSE;
    }
  else
    {
      if (may_vscroll (scrolled_window))
        adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
      else
        return FALSE;
    }

  if (adjustment)
    {
      double value = gtk_adjustment_get_value (adjustment);

      switch (scroll)
        {
        case GTK_SCROLL_STEP_FORWARD:
          value += gtk_adjustment_get_step_increment (adjustment);
          break;
        case GTK_SCROLL_STEP_BACKWARD:
          value -= gtk_adjustment_get_step_increment (adjustment);
          break;
        case GTK_SCROLL_PAGE_FORWARD:
          value += gtk_adjustment_get_page_increment (adjustment);
          break;
        case GTK_SCROLL_PAGE_BACKWARD:
          value -= gtk_adjustment_get_page_increment (adjustment);
          break;
        case GTK_SCROLL_START:
          value = gtk_adjustment_get_lower (adjustment);
          break;
        case GTK_SCROLL_END:
          value = gtk_adjustment_get_upper (adjustment);
          break;
        case GTK_SCROLL_STEP_UP:
        case GTK_SCROLL_STEP_DOWN:
        case GTK_SCROLL_STEP_LEFT:
        case GTK_SCROLL_STEP_RIGHT:
        case GTK_SCROLL_PAGE_UP:
        case GTK_SCROLL_PAGE_DOWN:
        case GTK_SCROLL_PAGE_LEFT:
        case GTK_SCROLL_PAGE_RIGHT:
        case GTK_SCROLL_NONE:
        case GTK_SCROLL_JUMP:
        default:
          g_assert_not_reached ();
          break;
        }

      gtk_adjustment_animate_to_value (adjustment, value);

      return TRUE;
    }

  return FALSE;
}

static void
gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
                                    GtkDirectionType   direction_type)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkWidget *toplevel;

  /* Focus out of the scrolled window entirely. We do this by setting
   * a flag, then propagating the focus motion to the notebook.
   */

  toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (scrolled_window)));
  if (!GTK_IS_ROOT (toplevel))
    return;

  g_object_ref (scrolled_window);

  priv->focus_out = TRUE;
  g_signal_emit_by_name (toplevel, "move-focus", direction_type);
  priv->focus_out = FALSE;

  g_object_unref (scrolled_window);
}

static void
gtk_scrolled_window_relative_allocation (GtkScrolledWindow *scrolled_window,
                                         GtkAllocation     *allocation)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  int sb_width;
  int sb_height;
  int width, height;

  g_return_if_fail (scrolled_window != NULL);
  g_return_if_fail (allocation != NULL);

  /* Get possible scrollbar dimensions */
  gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                      &sb_width, NULL, NULL, NULL);
  gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                      &sb_height, NULL, NULL, NULL);

  width = gtk_widget_get_width (GTK_WIDGET (scrolled_window));
  height = gtk_widget_get_height (GTK_WIDGET (scrolled_window));

  allocation->x = 0;
  allocation->y = 0;
  allocation->width = width;
  allocation->height = height;

  /* Subtract some things from our available allocation size */
  if (priv->vscrollbar_visible && !priv->use_indicators)
    {
      gboolean is_rtl;

      is_rtl = _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;

      if ((!is_rtl &&
           (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
            priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
          (is_rtl &&
           (priv->window_placement == GTK_CORNER_TOP_LEFT ||
            priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
        allocation->x += sb_width;

      allocation->width = MAX (1, width - sb_width);
    }

  if (priv->hscrollbar_visible && !priv->use_indicators)
    {

      if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
          priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
        allocation->y += (sb_height);

      allocation->height = MAX (1, height - sb_height);
    }
}

static gboolean
_gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
                                    int               *overshoot_x,
                                    int               *overshoot_y)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkAdjustment *vadjustment, *hadjustment;
  double lower, upper, x, y;

  /* Vertical overshoot */
  vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
  lower = gtk_adjustment_get_lower (vadjustment);
  upper = gtk_adjustment_get_upper (vadjustment) -
    gtk_adjustment_get_page_size (vadjustment);

  if (priv->unclamped_vadj_value < lower)
    y = priv->unclamped_vadj_value - lower;
  else if (priv->unclamped_vadj_value > upper)
    y = priv->unclamped_vadj_value - upper;
  else
    y = 0;

  /* Horizontal overshoot */
  hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
  lower = gtk_adjustment_get_lower (hadjustment);
  upper = gtk_adjustment_get_upper (hadjustment) -
    gtk_adjustment_get_page_size (hadjustment);

  if (priv->unclamped_hadj_value < lower)
    x = priv->unclamped_hadj_value - lower;
  else if (priv->unclamped_hadj_value > upper)
    x = priv->unclamped_hadj_value - upper;
  else
    x = 0;

  if (overshoot_x)
    *overshoot_x = x;

  if (overshoot_y)
    *overshoot_y = y;

  return (x != 0 || y != 0);
}

static void
gtk_scrolled_window_allocate_child (GtkScrolledWindow   *swindow,
                                    int                  width,
                                    int                  height)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (swindow);
  GtkWidget *widget = GTK_WIDGET (swindow);
  GtkAllocation child_allocation;
  int sb_width;
  int sb_height;

  child_allocation = (GtkAllocation) {00, width, height};

  /* Get possible scrollbar dimensions */
  gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                      &sb_width, NULL, NULL, NULL);
  gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                      &sb_height, NULL, NULL, NULL);

  /* Subtract some things from our available allocation size */
  if (priv->vscrollbar_visible && !priv->use_indicators)
    {
      gboolean is_rtl;

      is_rtl = _gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;

      if ((!is_rtl &&
           (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
            priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
          (is_rtl &&
           (priv->window_placement == GTK_CORNER_TOP_LEFT ||
            priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
        child_allocation.x += sb_width;

      child_allocation.width = MAX (1, child_allocation.width - sb_width);
    }

  if (priv->hscrollbar_visible && !priv->use_indicators)
    {

      if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
          priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
        child_allocation.y += (sb_height);

      child_allocation.height = MAX (1, child_allocation.height - sb_height);
    }

  gtk_widget_size_allocate (priv->child, &child_allocation, -1);
}

static void
gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
                                        GtkWidget         *scrollbar,
                                        GtkAllocation     *allocation)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkAllocation child_allocation, content_allocation;
  GtkWidget *widget = GTK_WIDGET (scrolled_window);
  int sb_height, sb_width;
  gboolean is_top, is_start, ltr, is_left;

  gtk_scrolled_window_inner_allocation (scrolled_window, &content_allocation);
  gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
                      &sb_width, NULL, NULL, NULL);
  gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
                      &sb_height, NULL, NULL, NULL);

  is_top = priv->window_placement == GTK_CORNER_TOP_LEFT ||
           priv->window_placement == GTK_CORNER_TOP_RIGHT;
  is_start = priv->window_placement == GTK_CORNER_TOP_LEFT ||
             priv->window_placement == GTK_CORNER_BOTTOM_LEFT;
  ltr = _gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
  is_left = ltr != is_start;

  if (scrollbar == priv->hscrollbar)
    {
      child_allocation.x = content_allocation.x;

      if (is_left &&
          priv->vscrollbar_visible &&
          !priv->use_indicators)
        {
          child_allocation.x += sb_height;
        }

      child_allocation.y = content_allocation.y;

      if (is_top)
        {
          child_allocation.y += content_allocation.height;

          if (priv->use_indicators)
            child_allocation.y -= sb_height;
        }

      child_allocation.width = content_allocation.width;
      child_allocation.height = sb_height;
    }
  else
    {
      g_assert (scrollbar == priv->vscrollbar);

      child_allocation.x = content_allocation.x;

      if (!is_left)
        {
          child_allocation.x += content_allocation.width;

          if (priv->use_indicators)
            child_allocation.x -= sb_width;
        }

      child_allocation.y = content_allocation.y;

      if (!is_top &&
          priv->hscrollbar_visible &&
          !priv->use_indicators)
        {
          child_allocation.y += sb_width;
        }

      child_allocation.width = sb_width;
      child_allocation.height = content_allocation.height;
    }

  *allocation = child_allocation;
}

static void
_gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
                                           GtkAdjustment     *adjustment,
                                           double             value)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  double lower, upper, *prev_value;
  GtkPositionType edge_pos;
  gboolean vertical;

  lower = gtk_adjustment_get_lower (adjustment) - MAX_OVERSHOOT_DISTANCE;
  upper = gtk_adjustment_get_upper (adjustment) -
    gtk_adjustment_get_page_size (adjustment) + MAX_OVERSHOOT_DISTANCE;

  if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
    vertical = FALSE;
  else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
    vertical = TRUE;
  else
    return;

  if (vertical)
    prev_value = &priv->unclamped_vadj_value;
  else
    prev_value = &priv->unclamped_hadj_value;

  value = CLAMP (value, lower, upper);

  if (*prev_value == value)
    return;

  *prev_value = value;
  gtk_adjustment_set_value (adjustment, value);

  if (value == lower)
    edge_pos = vertical ? GTK_POS_TOP : GTK_POS_LEFT;
  else if (value == upper)
    edge_pos = vertical ? GTK_POS_BOTTOM : GTK_POS_RIGHT;
  else
    return;

  /* Invert horizontal edge position on RTL */
  if (!vertical &&
      _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL)
    edge_pos = (edge_pos == GTK_POS_LEFT) ? GTK_POS_RIGHT : GTK_POS_LEFT;

  g_signal_emit (scrolled_window, signals[EDGE_OVERSHOT], 0, edge_pos);
}

static gboolean
scrolled_window_deceleration_cb (GtkWidget         *widget,
                                 GdkFrameClock     *frame_clock,
                                 gpointer           user_data)
{
  GtkScrolledWindow *scrolled_window = user_data;
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkAdjustment *hadjustment, *vadjustment;
  gint64 current_time;
  double position;
  gboolean retval = G_SOURCE_REMOVE;

  current_time = gdk_frame_clock_get_frame_time (frame_clock);
  priv->last_deceleration_time = current_time;

  hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
  vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));

  gtk_scrolled_window_invalidate_overshoot (scrolled_window);

  if (priv->hscrolling &&
      gtk_kinetic_scrolling_tick (priv->hscrolling, current_time, &position, NULL))
    {
      priv->unclamped_hadj_value = position;
      gtk_adjustment_set_value (hadjustment, position);
      retval = G_SOURCE_CONTINUE;
    }

  if (priv->vscrolling &&
      gtk_kinetic_scrolling_tick (priv->vscrolling, current_time, &position, NULL))
    {
      priv->unclamped_vadj_value = position;
      gtk_adjustment_set_value (vadjustment, position);
      retval = G_SOURCE_CONTINUE;
    }

  if (retval == G_SOURCE_REMOVE)
    gtk_scrolled_window_cancel_deceleration (scrolled_window);
  else
    gtk_scrolled_window_invalidate_overshoot (scrolled_window);

  return retval;
}

static void
gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (priv->deceleration_id)
    {
      gtk_widget_remove_tick_callback (GTK_WIDGET (scrolled_window),
                                       priv->deceleration_id);
      priv->deceleration_id = 0;
    }
}

static void
kinetic_scroll_stop_notify (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  priv->deceleration_id = 0;
}

static void
gtk_scrolled_window_accumulate_velocity (GtkKineticScrolling **scrolling,
                                         gint64                current_time,
                                         double               *velocity)
{
    if (!*scrolling)
      return;

    double last_velocity;
    gtk_kinetic_scrolling_tick (*scrolling, current_time, NULL, &last_velocity);
    if (((*velocity >= 0) == (last_velocity >= 0)) &&
        (fabs (*velocity) >= fabs (last_velocity) * VELOCITY_ACCUMULATION_FLOOR))
      {
        double min_velocity = last_velocity * VELOCITY_ACCUMULATION_FLOOR;
        double max_velocity = last_velocity * VELOCITY_ACCUMULATION_CEIL;
        double accumulation_multiplier = (*velocity - min_velocity) / (max_velocity - min_velocity);
        *velocity += last_velocity * fmin (accumulation_multiplier, VELOCITY_ACCUMULATION_MAX);
      }
    g_clear_pointer (scrolling, gtk_kinetic_scrolling_free);
}

static void
gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GdkFrameClock *frame_clock;
  gint64 current_time;
  int overshoot_x, overshoot_y;

  g_return_if_fail (priv->deceleration_id == 0);

  frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));

  current_time = gdk_frame_clock_get_frame_time (frame_clock);
  priv->last_deceleration_time = current_time;

  _gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y);

  if (may_hscroll (scrolled_window))
    {
      double lower,upper;
      GtkAdjustment *hadjustment;

      gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, current_time, &priv->x_velocity);
      g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);

      if (priv->x_velocity != 0 || overshoot_x != 0)
        {
          hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
          lower = gtk_adjustment_get_lower (hadjustment);
          upper = gtk_adjustment_get_upper (hadjustment);
          upper -= gtk_adjustment_get_page_size (hadjustment);
          priv->hscrolling =
            gtk_kinetic_scrolling_new (current_time,
                                       lower,
                                       upper,
                                       MAX_OVERSHOOT_DISTANCE,
                                       DECELERATION_FRICTION,
                                       OVERSHOOT_FRICTION,
                                       priv->unclamped_hadj_value,
                                       priv->x_velocity);
        }
    }
  else
    g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);

  if (may_vscroll (scrolled_window))
    {
      double lower,upper;
      GtkAdjustment *vadjustment;

      gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, current_time, &priv->y_velocity);
      g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);

      if (priv->y_velocity != 0 || overshoot_y != 0)
        {
          vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
          lower = gtk_adjustment_get_lower(vadjustment);
          upper = gtk_adjustment_get_upper(vadjustment);
          upper -= gtk_adjustment_get_page_size(vadjustment);
          priv->vscrolling =
            gtk_kinetic_scrolling_new (current_time,
                                       lower,
                                       upper,
                                       MAX_OVERSHOOT_DISTANCE,
                                       DECELERATION_FRICTION,
                                       OVERSHOOT_FRICTION,
                                       priv->unclamped_vadj_value,
                                       priv->y_velocity);
        }
    }
  else
    g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);

  priv->deceleration_id = gtk_widget_add_tick_callback (GTK_WIDGET (scrolled_window),
                                                        scrolled_window_deceleration_cb, scrolled_window,
                                                        (GDestroyNotify) kinetic_scroll_stop_notify);
}

static gboolean
gtk_scrolled_window_focus (GtkWidget        *widget,
                           GtkDirectionType  direction)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  gboolean had_focus_child;

  had_focus_child = gtk_widget_get_focus_child (widget) != NULL;

  if (priv->focus_out)
    {
      priv->focus_out = FALSE/* Clear this to catch the wrap-around case */
      return FALSE;
    }

  if (gtk_widget_is_focus (widget))
    return FALSE;

  /* We only put the scrolled window itself in the focus chain if it
   * isn't possible to focus any children.
   */

  if (priv->child)
    {
      if (gtk_widget_child_focus (priv->child, direction))
        return TRUE;
    }

  if (!had_focus_child && gtk_widget_get_can_focus (widget))
    {
      gtk_widget_grab_focus (widget);
      return TRUE;
    }
  else
    return FALSE;
}

static void
gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
                                        gpointer       data)
{
  GtkScrolledWindow *scrolled_window = data;
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
    {
      if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
        {
          gboolean visible;

          visible = priv->hscrollbar_visible;
          gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window, priv->hscrollbar);

          if (priv->hscrollbar_visible != visible)
            gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));

          if (priv->hscrolling)
            {
              GtkKineticScrollingChange change;
              double lower = gtk_adjustment_get_lower (adjustment);
              double upper = gtk_adjustment_get_upper (adjustment);
              upper -= gtk_adjustment_get_page_size (adjustment);

              change = gtk_kinetic_scrolling_update_size (priv->hscrolling, lower, upper);

              if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
                  (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
                {
                  g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
                  priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
                  gtk_scrolled_window_invalidate_overshoot (scrolled_window);
                }
            }
        }
    }
  else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
    {
      if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
        {
          gboolean visible;

          visible = priv->vscrollbar_visible;
          gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window, priv->vscrollbar);

          if (priv->vscrollbar_visible != visible)
            gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));

          if (priv->vscrolling)
            {
              GtkKineticScrollingChange change;
              double lower = gtk_adjustment_get_lower (adjustment);
              double upper = gtk_adjustment_get_upper (adjustment);
              upper -= gtk_adjustment_get_page_size (adjustment);

              change = gtk_kinetic_scrolling_update_size (priv->vscrolling, lower, upper);

              if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
                  (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
                {
                  g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
                  priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
                  gtk_scrolled_window_invalidate_overshoot (scrolled_window);
                }
            }
        }
    }

  if (!priv->hscrolling && !priv->vscrolling)
    gtk_scrolled_window_cancel_deceleration (scrolled_window);
}

static void
maybe_emit_edge_reached (GtkScrolledWindow *scrolled_window,
                         GtkAdjustment *adjustment)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  double value, lower, upper, page_size;
  GtkPositionType edge_pos;
  gboolean vertical;

  if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
    vertical = FALSE;
  else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
    vertical = TRUE;
  else
    return;

  value = gtk_adjustment_get_value (adjustment);
  lower = gtk_adjustment_get_lower (adjustment);
  upper = gtk_adjustment_get_upper (adjustment);
  page_size = gtk_adjustment_get_page_size (adjustment);

  if (value == lower)
    edge_pos = vertical ? GTK_POS_TOP: GTK_POS_LEFT;
  else if (value == upper - page_size)
    edge_pos = vertical ? GTK_POS_BOTTOM : GTK_POS_RIGHT;
  else
    return;

  if (!vertical &&
      _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL)
    edge_pos = (edge_pos == GTK_POS_LEFT) ? GTK_POS_RIGHT : GTK_POS_LEFT;

  g_signal_emit (scrolled_window, signals[EDGE_REACHED], 0, edge_pos);
}

static void
gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
                                              gpointer       user_data)
{
  GtkScrolledWindow *scrolled_window = user_data;
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  maybe_emit_edge_reached (scrolled_window, adjustment);

  /* Allow overshooting for kinetic scrolling operations */
  if (priv->deceleration_id)
    return;

  /* Ensure GtkAdjustment and unclamped values are in sync */
  if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
    priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
  else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
    priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
}

static gboolean
gtk_widget_should_animate (GtkWidget *widget)
{
  if (!gtk_widget_get_mapped (widget))
    return FALSE;

  return gtk_settings_get_enable_animations (gtk_widget_get_settings (widget));
}

static void
gtk_scrolled_window_update_animating (GtkScrolledWindow *sw)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
  GtkAdjustment *adjustment;
  GdkFrameClock *clock = NULL;
  guint duration = 0;

  if (gtk_widget_should_animate (GTK_WIDGET (sw)))
    {
      clock = gtk_widget_get_frame_clock (GTK_WIDGET (sw)),
      duration = ANIMATION_DURATION;
    }

  adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
  gtk_adjustment_enable_animation (adjustment, clock, duration);

  adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
  gtk_adjustment_enable_animation (adjustment, clock, duration);
}

static void
gtk_scrolled_window_map (GtkWidget *widget)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);

  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);

  gtk_scrolled_window_update_animating (scrolled_window);
  gtk_scrolled_window_update_use_indicators (scrolled_window);
}

static void
indicator_reset (Indicator *indicator)
{
  g_clear_handle_id (&indicator->conceil_timer, g_source_remove);
  g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);

  if (indicator->scrollbar && indicator->tick_id)
    {
      gtk_widget_remove_tick_callback (indicator->scrollbar,
                                       indicator->tick_id);
      indicator->tick_id = 0;
    }

  indicator->over = FALSE;
  gtk_progress_tracker_finish (&indicator->tracker);
  indicator->current_pos = indicator->source_pos = indicator->target_pos = 0;
  indicator->last_scroll_time = 0;
}

static void
gtk_scrolled_window_unmap (GtkWidget *widget)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);

  gtk_scrolled_window_update_animating (scrolled_window);

  indicator_reset (&priv->hindicator);
  indicator_reset (&priv->vindicator);
}

static void
indicator_set_fade (Indicator *indicator,
                    double     pos)
{
  gboolean visible, changed;

  changed = indicator->current_pos != pos;
  indicator->current_pos = pos;

  visible = indicator->current_pos != 0.0 || indicator->target_pos != 0.0;

  if (visible && indicator->conceil_timer == 0)
    {
      indicator->conceil_timer = g_timeout_add (INDICATOR_FADE_OUT_TIME, maybe_hide_indicator, indicator);
      gdk_source_set_static_name_by_id (indicator->conceil_timer, "[gtk] maybe_hide_indicator");
    }
  if (!visible && indicator->conceil_timer != 0)
    g_clear_handle_id (&indicator->conceil_timer, g_source_remove);

  if (changed)
    {
      gtk_widget_set_opacity (indicator->scrollbar, indicator->current_pos);
    }
}

static gboolean
indicator_fade_cb (GtkWidget     *widget,
                   GdkFrameClock *frame_clock,
                   gpointer       user_data)
{
  Indicator *indicator = user_data;
  double t;

  gtk_progress_tracker_advance_frame (&indicator->tracker,
                                      gdk_frame_clock_get_frame_time (frame_clock));
  t = gtk_progress_tracker_get_ease_out_cubic (&indicator->tracker, FALSE);

  indicator_set_fade (indicator,
                      indicator->source_pos + (t * (indicator->target_pos - indicator->source_pos)));

  if (gtk_progress_tracker_get_state (&indicator->tracker) == GTK_PROGRESS_STATE_AFTER)
    {
      indicator->tick_id = 0;
      return G_SOURCE_REMOVE;
    }

  return G_SOURCE_CONTINUE;
}

static void
indicator_start_fade (Indicator *indicator,
                      double     target)
{
  if (indicator->target_pos == target)
    return;

  indicator->target_pos = target;

  if (target != 0.0)
    indicator->last_scroll_time = g_get_monotonic_time ();

  if (gtk_widget_should_animate (indicator->scrollbar))
    {
      indicator->source_pos = indicator->current_pos;
      gtk_progress_tracker_start (&indicator->tracker, INDICATOR_FADE_OUT_DURATION * 100001.0);
      if (indicator->tick_id == 0)
        indicator->tick_id = gtk_widget_add_tick_callback (indicator->scrollbar, indicator_fade_cb, indicator, NULL);
    }
  else
    indicator_set_fade (indicator, target);
}

static gboolean
maybe_hide_indicator (gpointer data)
{
  Indicator *indicator = data;

  if (g_get_monotonic_time () - indicator->last_scroll_time >= INDICATOR_FADE_OUT_DELAY * 1000 &&
      !indicator->over)
    indicator_start_fade (indicator, 0.0);

  return G_SOURCE_CONTINUE;
}

static void
indicator_value_changed (GtkAdjustment *adjustment,
                         Indicator     *indicator)
{
  indicator->last_scroll_time = g_get_monotonic_time ();
  indicator_start_fade (indicator, 1.0);
}

static void
setup_indicator (GtkScrolledWindow *scrolled_window,
                 Indicator         *indicator,
                 GtkWidget         *scrollbar)
{
  GtkAdjustment *adjustment;

  if (scrollbar == NULL)
    return;

  adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (scrollbar));

  indicator->scrollbar = scrollbar;

  gtk_widget_add_css_class (scrollbar, "overlay-indicator");
  g_signal_connect (adjustment, "value-changed",
                    G_CALLBACK (indicator_value_changed), indicator);

  gtk_widget_set_opacity (scrollbar, 0.0);
  indicator->current_pos = 0.0;
}

static void
remove_indicator (GtkScrolledWindow *scrolled_window,
                  Indicator         *indicator)
{
  GtkWidget *scrollbar;
  GtkAdjustment *adjustment;

  if (indicator->scrollbar == NULL)
    return;

  scrollbar = indicator->scrollbar;
  indicator->scrollbar = NULL;

  gtk_widget_remove_css_class (scrollbar, "overlay-indicator");

  adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (scrollbar));
  g_signal_handlers_disconnect_by_data (adjustment, indicator);

  g_clear_handle_id (&indicator->conceil_timer, g_source_remove);

  g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);

  if (indicator->tick_id)
    {
      gtk_widget_remove_tick_callback (scrollbar, indicator->tick_id);
      indicator->tick_id = 0;
    }

  gtk_widget_set_opacity (scrollbar, 1.0);
  indicator->current_pos = 1.0;
}

static void
gtk_scrolled_window_sync_use_indicators (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  if (priv->use_indicators)
    {
      setup_indicator (scrolled_window, &priv->hindicator, priv->hscrollbar);
      setup_indicator (scrolled_window, &priv->vindicator, priv->vscrollbar);
    }
  else
    {
      remove_indicator (scrolled_window, &priv->hindicator);
      remove_indicator (scrolled_window, &priv->vindicator);
    }
}

static void
gtk_scrolled_window_update_use_indicators (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  gboolean use_indicators;
  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
  gboolean overlay_scrolling;

  g_object_get (settings, "gtk-overlay-scrolling", &overlay_scrolling, NULL);

  use_indicators = overlay_scrolling && priv->overlay_scrolling;

  if (priv->use_indicators != use_indicators)
    {
      priv->use_indicators = use_indicators;

      if (gtk_widget_get_realized (GTK_WIDGET (scrolled_window)))
        gtk_scrolled_window_sync_use_indicators (scrolled_window);

      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
    }
}

static void
gtk_scrolled_window_realize (GtkWidget *widget)
{
  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkSettings *settings;

  priv->hindicator.scrollbar = priv->hscrollbar;
  priv->vindicator.scrollbar = priv->vscrollbar;

  gtk_scrolled_window_sync_use_indicators (scrolled_window);

  settings = gtk_widget_get_settings (widget);
  g_signal_connect_swapped (settings, "notify::gtk-overlay-scrolling",
                            G_CALLBACK (gtk_scrolled_window_update_use_indicators), widget);

  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
}

static void
gtk_scrolled_window_unrealize (GtkWidget *widget)
{
  GtkSettings *settings;

  settings = gtk_widget_get_settings (widget);

  g_signal_handlers_disconnect_by_func (settings, gtk_scrolled_window_update_use_indicators, widget);

  GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
}

/**
 * gtk_scrolled_window_get_min_content_width:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Gets the minimum content width of @scrolled_window.
 *
 * Returns: the minimum content width
 */

int
gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);

  return priv->min_content_width;
}

/**
 * gtk_scrolled_window_set_min_content_width:
 * @scrolled_window: a `GtkScrolledWindow`
 * @width: the minimal content width
 *
 * Sets the minimum width that @scrolled_window should keep visible.
 *
 * Note that this can and (usually will) be smaller than the minimum
 * size of the content.
 *
 * It is a programming error to set the minimum content width to a
 * value greater than [property@Gtk.ScrolledWindow:max-content-width].
 */

void
gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
                                           int                width)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  g_return_if_fail (width == -1 || priv->max_content_width == -1 || width <= priv->max_content_width);

  if (priv->min_content_width != width)
    {
      priv->min_content_width = width;

      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));

      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_MIN_CONTENT_WIDTH]);
    }
}

/**
 * gtk_scrolled_window_get_min_content_height:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Gets the minimal content height of @scrolled_window.
 *
 * Returns: the minimal content height
 */

int
gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);

  return priv->min_content_height;
}

/**
 * gtk_scrolled_window_set_min_content_height:
 * @scrolled_window: a `GtkScrolledWindow`
 * @height: the minimal content height
 *
 * Sets the minimum height that @scrolled_window should keep visible.
 *
 * Note that this can and (usually will) be smaller than the minimum
 * size of the content.
 *
 * It is a programming error to set the minimum content height to a
 * value greater than [property@Gtk.ScrolledWindow:max-content-height].
 */

void
gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
                                            int                height)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  g_return_if_fail (height == -1 || priv->max_content_height == -1 || height <= priv->max_content_height);

  if (priv->min_content_height != height)
    {
      priv->min_content_height = height;

      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));

      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_MIN_CONTENT_HEIGHT]);
    }
}

/**
 * gtk_scrolled_window_set_overlay_scrolling:
 * @scrolled_window: a `GtkScrolledWindow`
 * @overlay_scrolling: whether to enable overlay scrolling
 *
 * Enables or disables overlay scrolling for this scrolled window.
 */

void
gtk_scrolled_window_set_overlay_scrolling (GtkScrolledWindow *scrolled_window,
                                           gboolean           overlay_scrolling)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  if (priv->overlay_scrolling != overlay_scrolling)
    {
      priv->overlay_scrolling = overlay_scrolling;

      gtk_scrolled_window_update_use_indicators (scrolled_window);

      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_OVERLAY_SCROLLING]);
    }
}

/**
 * gtk_scrolled_window_get_overlay_scrolling:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns whether overlay scrolling is enabled for this scrolled window.
 *
 * Returns: %TRUE if overlay scrolling is enabled
 */

gboolean
gtk_scrolled_window_get_overlay_scrolling (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), TRUE);

  return priv->overlay_scrolling;
}

/**
 * gtk_scrolled_window_set_max_content_width:
 * @scrolled_window: a `GtkScrolledWindow`
 * @width: the maximum content width
 *
 * Sets the maximum width that @scrolled_window should keep visible.
 *
 * The @scrolled_window will grow up to this width before it starts
 * scrolling the content.
 *
 * It is a programming error to set the maximum content width to a
 * value smaller than [property@Gtk.ScrolledWindow:min-content-width].
 */

void
gtk_scrolled_window_set_max_content_width (GtkScrolledWindow *scrolled_window,
                                           int                width)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  g_return_if_fail (width == -1 || priv->min_content_width == -1 || width >= priv->min_content_width);

  if (width != priv->max_content_width)
    {
      priv->max_content_width = width;
      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_MAX_CONTENT_WIDTH]);
      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
    }
}

/**
 * gtk_scrolled_window_get_max_content_width:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the maximum content width set.
 *
 * Returns: the maximum content width, or -1
 */

int
gtk_scrolled_window_get_max_content_width (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);

  return priv->max_content_width;
}

/**
 * gtk_scrolled_window_set_max_content_height:
 * @scrolled_window: a `GtkScrolledWindow`
 * @height: the maximum content height
 *
 * Sets the maximum height that @scrolled_window should keep visible.
 *
 * The @scrolled_window will grow up to this height before it starts
 * scrolling the content.
 *
 * It is a programming error to set the maximum content height to a value
 * smaller than [property@Gtk.ScrolledWindow:min-content-height].
 */

void
gtk_scrolled_window_set_max_content_height (GtkScrolledWindow *scrolled_window,
                                            int                height)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  g_return_if_fail (height == -1 || priv->min_content_height == -1 || height >= priv->min_content_height);

  if (height != priv->max_content_height)
    {
      priv->max_content_height = height;
      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_MAX_CONTENT_HEIGHT]);
      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
    }
}

/**
 * gtk_scrolled_window_get_max_content_height:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Returns the maximum content height set.
 *
 * Returns: the maximum content height, or -1
 */

int
gtk_scrolled_window_get_max_content_height (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);

  return priv->max_content_height;
}

/**
 * gtk_scrolled_window_set_propagate_natural_width:
 * @scrolled_window: a `GtkScrolledWindow`
 * @propagate: whether to propagate natural width
 *
 * Sets whether the natural width of the child should be calculated
 * and propagated through the scrolled windows requested natural width.
 */

void
gtk_scrolled_window_set_propagate_natural_width (GtkScrolledWindow *scrolled_window,
                                                 gboolean           propagate)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  propagate = !!propagate;

  if (priv->propagate_natural_width != propagate)
    {
      priv->propagate_natural_width = propagate;
      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_PROPAGATE_NATURAL_WIDTH]);
      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
    }
}

/**
 * gtk_scrolled_window_get_propagate_natural_width:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Reports whether the natural width of the child will be calculated
 * and propagated through the scrolled windows requested natural width.
 *
 * Returns: whether natural width propagation is enabled.
 */

gboolean
gtk_scrolled_window_get_propagate_natural_width (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);

  return priv->propagate_natural_width;
}

/**
 * gtk_scrolled_window_set_propagate_natural_height:
 * @scrolled_window: a `GtkScrolledWindow`
 * @propagate: whether to propagate natural height
 *
 * Sets whether the natural height of the child should be calculated
 * and propagated through the scrolled windows requested natural height.
 */

void
gtk_scrolled_window_set_propagate_natural_height (GtkScrolledWindow *scrolled_window,
                                                  gboolean           propagate)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));

  propagate = !!propagate;

  if (priv->propagate_natural_height != propagate)
    {
      priv->propagate_natural_height = propagate;
      g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_PROPAGATE_NATURAL_HEIGHT]);
      gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
    }
}

/**
 * gtk_scrolled_window_get_propagate_natural_height:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Reports whether the natural height of the child will be calculated
 * and propagated through the scrolled windows requested natural height.
 *
 * Returns: whether natural height propagation is enabled.
 */

gboolean
gtk_scrolled_window_get_propagate_natural_height (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);

  return priv->propagate_natural_height;
}

/**
 * gtk_scrolled_window_set_child:
 * @scrolled_window: a `GtkScrolledWindow`
 * @child: (nullable): the child widget
 *
 * Sets the child widget of @scrolled_window.
 *
 * If @child does not implement the [iface@Gtk.Scrollable] interface,
 * the scrolled window will add @child to a [class@Gtk.Viewport] instance
 * and then add the viewport as its child widget.
 */

void
gtk_scrolled_window_set_child (GtkScrolledWindow *scrolled_window,
                               GtkWidget         *child)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
  GtkWidget *scrollable_child;

  g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
  g_return_if_fail (child == NULL ||
                    priv->child == child ||
                    (priv->auto_added_viewport && gtk_viewport_get_child (GTK_VIEWPORT (priv->child)) == child) ||
                    gtk_widget_get_parent (child) == NULL);

  if (priv->child == child ||
      (priv->auto_added_viewport && gtk_viewport_get_child (GTK_VIEWPORT (priv->child)) == child))
    return;

  if (priv->child)
    {
      if (priv->auto_added_viewport)
        gtk_viewport_set_child (GTK_VIEWPORT (priv->child), NULL);

      g_object_set (priv->child,
                    "hadjustment", NULL,
                    "vadjustment", NULL,
                    NULL);

      g_clear_pointer (&priv->child, gtk_widget_unparent);
      priv->auto_added_viewport = FALSE;
    }

  if (child)
    {
      GtkAdjustment *hadj, *vadj;

      /* gtk_scrolled_window_set_[hv]adjustment have the side-effect
       * of creating the scrollbars
       */

      if (!priv->hscrollbar)
        gtk_scrolled_window_set_hadjustment (scrolled_window, NULL);

      if (!priv->vscrollbar)
        gtk_scrolled_window_set_vadjustment (scrolled_window, NULL);

      hadj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
      vadj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));

      if (GTK_IS_SCROLLABLE (child))
        {
          scrollable_child = child;
          priv->auto_added_viewport = FALSE;
        }
      else
        {
          scrollable_child = gtk_viewport_new (hadj, vadj);
          gtk_viewport_set_child (GTK_VIEWPORT (scrollable_child), child);
          priv->auto_added_viewport = TRUE;
        }

      priv->child = scrollable_child;
      gtk_widget_insert_after (scrollable_child, GTK_WIDGET (scrolled_window), NULL);

      g_object_set (scrollable_child,
                    "hadjustment", hadj,
                    "vadjustment", vadj,
                    NULL);
    }

  if (priv->child)
    {
      gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->hscrollbar),
                                      GTK_ACCESSIBLE_RELATION_CONTROLS, priv->child, NULL,
                                      -1);
      gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->vscrollbar),
                                      GTK_ACCESSIBLE_RELATION_CONTROLS, priv->child, NULL,
                                      -1);
    }
  else
    {
      gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->hscrollbar),
                                     GTK_ACCESSIBLE_RELATION_CONTROLS);
      gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->vscrollbar),
                                     GTK_ACCESSIBLE_RELATION_CONTROLS);
    }

  g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_CHILD]);
}

/**
 * gtk_scrolled_window_get_child:
 * @scrolled_window: a `GtkScrolledWindow`
 *
 * Gets the child widget of @scrolled_window.
 *
 * If the scrolled window automatically added a [class@Gtk.Viewport], this
 * function will return the viewport widget, and you can retrieve its child
 * using [method@Gtk.Viewport.get_child].
 *
 * Returns: (nullable) (transfer none): the child widget of @scrolled_window
 */

GtkWidget *
gtk_scrolled_window_get_child (GtkScrolledWindow *scrolled_window)
{
  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);

  g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);

  return priv->child;
}

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

¤ Dauer der Verarbeitung: 0.143 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.