2565 lines
82 KiB
C
2565 lines
82 KiB
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 "gtkpaned.h"
|
||
|
||
#include "gtkaccessiblerange.h"
|
||
#include "gtkcssboxesprivate.h"
|
||
#include "gtkeventcontrollermotion.h"
|
||
#include "gtkgesturepan.h"
|
||
#include "gtkgesturesingle.h"
|
||
#include "gtkpanedhandleprivate.h"
|
||
#include "gtkmarshalers.h"
|
||
#include "gtkorientable.h"
|
||
#include "gtkprivate.h"
|
||
#include "gtktypebuiltins.h"
|
||
#include "gtkwidgetprivate.h"
|
||
#include "gtkbuildable.h"
|
||
|
||
#include <math.h>
|
||
|
||
/**
|
||
* GtkPaned:
|
||
*
|
||
* A widget with two panes, arranged either horizontally or vertically.
|
||
*
|
||
* ![An example GtkPaned](panes.png)
|
||
*
|
||
* The division between the two panes is adjustable by the user
|
||
* by dragging a handle.
|
||
*
|
||
* Child widgets are added to the panes of the widget with
|
||
* [method@Gtk.Paned.set_start_child] and [method@Gtk.Paned.set_end_child].
|
||
* The division between the two children is set by default from the size
|
||
* requests of the children, but it can be adjusted by the user.
|
||
*
|
||
* A paned widget draws a separator between the two child widgets and a
|
||
* small handle that the user can drag to adjust the division. It does not
|
||
* draw any relief around the children or around the separator. (The space
|
||
* in which the separator is called the gutter.) Often, it is useful to put
|
||
* each child inside a [class@Gtk.Frame] so that the gutter appears as a
|
||
* ridge. No separator is drawn if one of the children is missing.
|
||
*
|
||
* Each child has two options that can be set, "resize" and "shrink". If
|
||
* "resize" is true then, when the `GtkPaned` is resized, that child will
|
||
* expand or shrink along with the paned widget. If "shrink" is true, then
|
||
* that child can be made smaller than its requisition by the user.
|
||
* Setting "shrink" to false allows the application to set a minimum size.
|
||
* If "resize" is false for both children, then this is treated as if
|
||
* "resize" is true for both children.
|
||
*
|
||
* The application can set the position of the slider as if it were set
|
||
* by the user, by calling [method@Gtk.Paned.set_position].
|
||
*
|
||
* # CSS nodes
|
||
*
|
||
* ```
|
||
* paned
|
||
* ├── <child>
|
||
* ├── separator[.wide]
|
||
* ╰── <child>
|
||
* ```
|
||
*
|
||
* `GtkPaned` has a main CSS node with name paned, and a subnode for
|
||
* the separator with name separator. The subnode gets a .wide style
|
||
* class when the paned is supposed to be wide.
|
||
*
|
||
* In horizontal orientation, the nodes are arranged based on the text
|
||
* direction, so in left-to-right mode, :first-child will select the
|
||
* leftmost child, while it will select the rightmost child in
|
||
* RTL layouts.
|
||
*
|
||
* ## Creating a paned widget with minimum sizes.
|
||
*
|
||
* ```c
|
||
* GtkWidget *hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
|
||
* GtkWidget *frame1 = gtk_frame_new (NULL);
|
||
* GtkWidget *frame2 = gtk_frame_new (NULL);
|
||
*
|
||
* gtk_widget_set_size_request (hpaned, 200, -1);
|
||
*
|
||
* gtk_paned_set_start_child (GTK_PANED (hpaned), frame1);
|
||
* gtk_paned_set_resize_start_child (GTK_PANED (hpaned), TRUE);
|
||
* gtk_paned_set_shrink_start_child (GTK_PANED (hpaned), FALSE);
|
||
* gtk_widget_set_size_request (frame1, 50, -1);
|
||
*
|
||
* gtk_paned_set_end_child (GTK_PANED (hpaned), frame2);
|
||
* gtk_paned_set_resize_end_child (GTK_PANED (hpaned), FALSE);
|
||
* gtk_paned_set_shrink_end_child (GTK_PANED (hpaned), FALSE);
|
||
* gtk_widget_set_size_request (frame2, 50, -1);
|
||
* ```
|
||
*/
|
||
|
||
#define HANDLE_EXTRA_SIZE 6
|
||
|
||
typedef struct _GtkPanedClass GtkPanedClass;
|
||
|
||
struct _GtkPaned
|
||
{
|
||
GtkWidget parent_instance;
|
||
|
||
GtkPaned *first_paned;
|
||
GtkWidget *start_child;
|
||
GtkWidget *end_child;
|
||
GtkWidget *last_start_child_focus;
|
||
GtkWidget *last_end_child_focus;
|
||
GtkWidget *saved_focus;
|
||
GtkOrientation orientation;
|
||
|
||
GtkWidget *handle_widget;
|
||
|
||
GtkGesture *pan_gesture; /* Used for touch */
|
||
GtkGesture *drag_gesture; /* Used for mice */
|
||
|
||
int start_child_size;
|
||
int drag_pos;
|
||
int last_allocation;
|
||
int max_position;
|
||
int min_position;
|
||
int original_position;
|
||
|
||
guint in_recursion : 1;
|
||
guint resize_start_child : 1;
|
||
guint shrink_start_child : 1;
|
||
guint resize_end_child : 1;
|
||
guint shrink_end_child : 1;
|
||
guint position_set : 1;
|
||
guint panning : 1;
|
||
};
|
||
|
||
struct _GtkPanedClass
|
||
{
|
||
GtkWidgetClass parent_class;
|
||
|
||
gboolean (* cycle_child_focus) (GtkPaned *paned,
|
||
gboolean reverse);
|
||
gboolean (* toggle_handle_focus) (GtkPaned *paned);
|
||
gboolean (* move_handle) (GtkPaned *paned,
|
||
GtkScrollType scroll);
|
||
gboolean (* cycle_handle_focus) (GtkPaned *paned,
|
||
gboolean reverse);
|
||
gboolean (* accept_position) (GtkPaned *paned);
|
||
gboolean (* cancel_position) (GtkPaned *paned);
|
||
};
|
||
|
||
enum {
|
||
PROP_0,
|
||
PROP_POSITION,
|
||
PROP_POSITION_SET,
|
||
PROP_MIN_POSITION,
|
||
PROP_MAX_POSITION,
|
||
PROP_WIDE_HANDLE,
|
||
PROP_RESIZE_START_CHILD,
|
||
PROP_RESIZE_END_CHILD,
|
||
PROP_SHRINK_START_CHILD,
|
||
PROP_SHRINK_END_CHILD,
|
||
PROP_START_CHILD,
|
||
PROP_END_CHILD,
|
||
LAST_PROP,
|
||
|
||
/* GtkOrientable */
|
||
PROP_ORIENTATION,
|
||
};
|
||
|
||
enum {
|
||
CYCLE_CHILD_FOCUS,
|
||
TOGGLE_HANDLE_FOCUS,
|
||
MOVE_HANDLE,
|
||
CYCLE_HANDLE_FOCUS,
|
||
ACCEPT_POSITION,
|
||
CANCEL_POSITION,
|
||
LAST_SIGNAL
|
||
};
|
||
|
||
static void gtk_paned_set_property (GObject *object,
|
||
guint prop_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec);
|
||
static void gtk_paned_get_property (GObject *object,
|
||
guint prop_id,
|
||
GValue *value,
|
||
GParamSpec *pspec);
|
||
static void gtk_paned_dispose (GObject *object);
|
||
static void gtk_paned_measure (GtkWidget *widget,
|
||
GtkOrientation orientation,
|
||
int for_size,
|
||
int *minimum,
|
||
int *natural,
|
||
int *minimum_baseline,
|
||
int *natural_baseline);
|
||
static void gtk_paned_size_allocate (GtkWidget *widget,
|
||
int width,
|
||
int height,
|
||
int baseline);
|
||
static void gtk_paned_unrealize (GtkWidget *widget);
|
||
static void gtk_paned_css_changed (GtkWidget *widget,
|
||
GtkCssStyleChange *change);
|
||
static void gtk_paned_calc_position (GtkPaned *paned,
|
||
int allocation,
|
||
int start_child_req,
|
||
int end_child_req);
|
||
static void gtk_paned_set_focus_child (GtkWidget *widget,
|
||
GtkWidget *child);
|
||
static void gtk_paned_set_saved_focus (GtkPaned *paned,
|
||
GtkWidget *widget);
|
||
static void gtk_paned_set_first_paned (GtkPaned *paned,
|
||
GtkPaned *first_paned);
|
||
static void gtk_paned_set_last_start_child_focus (GtkPaned *paned,
|
||
GtkWidget *widget);
|
||
static void gtk_paned_set_last_end_child_focus (GtkPaned *paned,
|
||
GtkWidget *widget);
|
||
static gboolean gtk_paned_cycle_child_focus (GtkPaned *paned,
|
||
gboolean reverse);
|
||
static gboolean gtk_paned_cycle_handle_focus (GtkPaned *paned,
|
||
gboolean reverse);
|
||
static gboolean gtk_paned_move_handle (GtkPaned *paned,
|
||
GtkScrollType scroll);
|
||
static gboolean gtk_paned_accept_position (GtkPaned *paned);
|
||
static gboolean gtk_paned_cancel_position (GtkPaned *paned);
|
||
static gboolean gtk_paned_toggle_handle_focus (GtkPaned *paned);
|
||
|
||
static void update_drag (GtkPaned *paned,
|
||
int xpos,
|
||
int ypos);
|
||
|
||
static void gtk_paned_buildable_iface_init (GtkBuildableIface *iface);
|
||
|
||
static void gtk_paned_accessible_range_init (GtkAccessibleRangeInterface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_WIDGET,
|
||
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
|
||
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE_RANGE,
|
||
gtk_paned_accessible_range_init)
|
||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
|
||
gtk_paned_buildable_iface_init))
|
||
|
||
static guint signals[LAST_SIGNAL] = { 0 };
|
||
static GParamSpec *paned_props[LAST_PROP] = { NULL, };
|
||
|
||
static void
|
||
add_tab_bindings (GtkWidgetClass *widget_class,
|
||
GdkModifierType modifiers)
|
||
{
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_Tab, modifiers,
|
||
"toggle-handle-focus",
|
||
NULL);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_KP_Tab, modifiers,
|
||
"toggle-handle-focus",
|
||
NULL);
|
||
}
|
||
|
||
static void
|
||
add_move_binding (GtkWidgetClass *widget_class,
|
||
guint keyval,
|
||
GdkModifierType mask,
|
||
GtkScrollType scroll)
|
||
{
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
keyval, mask,
|
||
"move-handle",
|
||
"(i)", scroll);
|
||
}
|
||
|
||
static void
|
||
get_handle_area (GtkPaned *paned,
|
||
graphene_rect_t *area)
|
||
{
|
||
int extra = 0;
|
||
|
||
if (!gtk_widget_compute_bounds (paned->handle_widget, GTK_WIDGET (paned), area))
|
||
return;
|
||
|
||
if (!gtk_paned_get_wide_handle (paned))
|
||
extra = HANDLE_EXTRA_SIZE;
|
||
|
||
graphene_rect_inset (area, - extra, - extra);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_compute_expand (GtkWidget *widget,
|
||
gboolean *hexpand,
|
||
gboolean *vexpand)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
gboolean h = FALSE;
|
||
gboolean v = FALSE;
|
||
|
||
if (paned->start_child)
|
||
{
|
||
h = h || gtk_widget_compute_expand (paned->start_child, GTK_ORIENTATION_HORIZONTAL);
|
||
v = v || gtk_widget_compute_expand (paned->start_child, GTK_ORIENTATION_VERTICAL);
|
||
}
|
||
|
||
if (paned->end_child)
|
||
{
|
||
h = h || gtk_widget_compute_expand (paned->end_child, GTK_ORIENTATION_HORIZONTAL);
|
||
v = v || gtk_widget_compute_expand (paned->end_child, GTK_ORIENTATION_VERTICAL);
|
||
}
|
||
|
||
*hexpand = h;
|
||
*vexpand = v;
|
||
}
|
||
|
||
static GtkSizeRequestMode
|
||
gtk_paned_get_request_mode (GtkWidget *widget)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
int wfh = 0, hfw = 0;
|
||
|
||
if (paned->start_child)
|
||
{
|
||
switch (gtk_widget_get_request_mode (paned->start_child))
|
||
{
|
||
case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
|
||
hfw++;
|
||
break;
|
||
case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
|
||
wfh++;
|
||
break;
|
||
case GTK_SIZE_REQUEST_CONSTANT_SIZE:
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
if (paned->end_child)
|
||
{
|
||
switch (gtk_widget_get_request_mode (paned->end_child))
|
||
{
|
||
case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
|
||
hfw++;
|
||
break;
|
||
case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
|
||
wfh++;
|
||
break;
|
||
case GTK_SIZE_REQUEST_CONSTANT_SIZE:
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (hfw == 0 && wfh == 0)
|
||
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
|
||
else
|
||
return wfh > hfw ?
|
||
GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
|
||
GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_orientation (GtkPaned *self,
|
||
GtkOrientation orientation)
|
||
{
|
||
if (self->orientation != orientation)
|
||
{
|
||
static const char *cursor_name[2] = {
|
||
"col-resize",
|
||
"row-resize",
|
||
};
|
||
|
||
self->orientation = orientation;
|
||
|
||
gtk_widget_update_orientation (GTK_WIDGET (self), self->orientation);
|
||
gtk_widget_set_cursor_from_name (self->handle_widget,
|
||
cursor_name[orientation]);
|
||
gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (self->pan_gesture),
|
||
orientation);
|
||
|
||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||
g_object_notify (G_OBJECT (self), "orientation");
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_paned_class_init (GtkPanedClass *class)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||
|
||
object_class->set_property = gtk_paned_set_property;
|
||
object_class->get_property = gtk_paned_get_property;
|
||
object_class->dispose = gtk_paned_dispose;
|
||
|
||
widget_class->measure = gtk_paned_measure;
|
||
widget_class->size_allocate = gtk_paned_size_allocate;
|
||
widget_class->unrealize = gtk_paned_unrealize;
|
||
widget_class->focus = gtk_widget_focus_child;
|
||
widget_class->set_focus_child = gtk_paned_set_focus_child;
|
||
widget_class->css_changed = gtk_paned_css_changed;
|
||
widget_class->get_request_mode = gtk_paned_get_request_mode;
|
||
widget_class->compute_expand = gtk_paned_compute_expand;
|
||
|
||
class->cycle_child_focus = gtk_paned_cycle_child_focus;
|
||
class->toggle_handle_focus = gtk_paned_toggle_handle_focus;
|
||
class->move_handle = gtk_paned_move_handle;
|
||
class->cycle_handle_focus = gtk_paned_cycle_handle_focus;
|
||
class->accept_position = gtk_paned_accept_position;
|
||
class->cancel_position = gtk_paned_cancel_position;
|
||
|
||
/**
|
||
* GtkPaned:position: (attributes org.gtk.Property.get=gtk_paned_get_position org.gtk.Property.set=gtk_paned_set_position)
|
||
*
|
||
* Position of the separator in pixels, from the left/top.
|
||
*/
|
||
paned_props[PROP_POSITION] =
|
||
g_param_spec_int ("position", NULL, NULL,
|
||
0, G_MAXINT, 0,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:position-set:
|
||
*
|
||
* Whether the [property@Gtk.Paned:position] property has been set.
|
||
*/
|
||
paned_props[PROP_POSITION_SET] =
|
||
g_param_spec_boolean ("position-set", NULL, NULL,
|
||
FALSE,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:min-position:
|
||
*
|
||
* The smallest possible value for the [property@Gtk.Paned:position]
|
||
* property.
|
||
*
|
||
* This property is derived from the size and shrinkability
|
||
* of the widget's children.
|
||
*/
|
||
paned_props[PROP_MIN_POSITION] =
|
||
g_param_spec_int ("min-position", NULL, NULL,
|
||
0, G_MAXINT, 0,
|
||
GTK_PARAM_READABLE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:max-position:
|
||
*
|
||
* The largest possible value for the [property@Gtk.Paned:position]
|
||
* property.
|
||
*
|
||
* This property is derived from the size and shrinkability
|
||
* of the widget's children.
|
||
*/
|
||
paned_props[PROP_MAX_POSITION] =
|
||
g_param_spec_int ("max-position", NULL, NULL,
|
||
0, G_MAXINT, G_MAXINT,
|
||
GTK_PARAM_READABLE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:wide-handle: (attributes org.gtk.Property.get=gtk_paned_get_wide_handle org.gtk.Property.set=gtk_paned_set_wide_handle)
|
||
*
|
||
* Whether the `GtkPaned` should provide a stronger visual separation.
|
||
*
|
||
* For example, this could be set when a paned contains two
|
||
* [class@Gtk.Notebook]s, whose tab rows would otherwise merge visually.
|
||
*/
|
||
paned_props[PROP_WIDE_HANDLE] =
|
||
g_param_spec_boolean ("wide-handle", NULL, NULL,
|
||
FALSE,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:resize-start-child: (attributes org.gtk.Property.get=gtk_paned_get_resize_start_child org.gtk.Property.set=gtk_paned_set_resize_start_child)
|
||
*
|
||
* Determines whether the first child expands and shrinks
|
||
* along with the paned widget.
|
||
*/
|
||
paned_props[PROP_RESIZE_START_CHILD] =
|
||
g_param_spec_boolean ("resize-start-child", NULL, NULL,
|
||
TRUE,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:resize-end-child: (attributes org.gtk.Property.get=gtk_paned_get_resize_end_child org.gtk.Property.set=gtk_paned_set_resize_end_child)
|
||
*
|
||
* Determines whether the second child expands and shrinks
|
||
* along with the paned widget.
|
||
*/
|
||
paned_props[PROP_RESIZE_END_CHILD] =
|
||
g_param_spec_boolean ("resize-end-child", NULL, NULL,
|
||
TRUE,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:shrink-start-child: (attributes org.gtk.Property.get=gtk_paned_get_shrink_start_child org.gtk.Property.set=gtk_paned_set_shrink_start_child)
|
||
*
|
||
* Determines whether the first child can be made smaller
|
||
* than its requisition.
|
||
*/
|
||
paned_props[PROP_SHRINK_START_CHILD] =
|
||
g_param_spec_boolean ("shrink-start-child", NULL, NULL,
|
||
TRUE,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:shrink-end-child: (attributes org.gtk.Property.get=gtk_paned_get_shrink_end_child org.gtk.Property.set=gtk_paned_set_shrink_end_child)
|
||
*
|
||
* Determines whether the second child can be made smaller
|
||
* than its requisition.
|
||
*/
|
||
paned_props[PROP_SHRINK_END_CHILD] =
|
||
g_param_spec_boolean ("shrink-end-child", NULL, NULL,
|
||
TRUE,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:start-child: (attributes org.gtk.Property.get=gtk_paned_get_start_child org.gtk.Property.set=gtk_paned_set_start_child)
|
||
*
|
||
* The first child.
|
||
*/
|
||
paned_props[PROP_START_CHILD] =
|
||
g_param_spec_object ("start-child", NULL, NULL,
|
||
GTK_TYPE_WIDGET,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
/**
|
||
* GtkPaned:end-child: (attributes org.gtk.Property.get=gtk_paned_get_end_child org.gtk.Property.set=gtk_paned_set_end_child)
|
||
*
|
||
* The second child.
|
||
*/
|
||
paned_props[PROP_END_CHILD] =
|
||
g_param_spec_object ("end-child", NULL, NULL,
|
||
GTK_TYPE_WIDGET,
|
||
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||
|
||
g_object_class_install_properties (object_class, LAST_PROP, paned_props);
|
||
g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
|
||
|
||
/**
|
||
* GtkPaned::cycle-child-focus:
|
||
* @widget: the object that received the signal
|
||
* @reversed: whether cycling backward or forward
|
||
*
|
||
* Emitted to cycle the focus between the children of the paned.
|
||
*
|
||
* This is a [keybinding signal](class.SignalAction.html).
|
||
*
|
||
* The default binding is <kbd>F6</kbd>.
|
||
*/
|
||
signals [CYCLE_CHILD_FOCUS] =
|
||
g_signal_new (I_("cycle-child-focus"),
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||
G_STRUCT_OFFSET (GtkPanedClass, cycle_child_focus),
|
||
NULL, NULL,
|
||
_gtk_marshal_BOOLEAN__BOOLEAN,
|
||
G_TYPE_BOOLEAN, 1,
|
||
G_TYPE_BOOLEAN);
|
||
g_signal_set_va_marshaller (signals[CYCLE_CHILD_FOCUS],
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
_gtk_marshal_BOOLEAN__BOOLEANv);
|
||
|
||
/**
|
||
* GtkPaned::toggle-handle-focus:
|
||
* @widget: the object that received the signal
|
||
*
|
||
* Emitted to accept the current position of the handle and then
|
||
* move focus to the next widget in the focus chain.
|
||
*
|
||
* This is a [keybinding signal](class.SignalAction.html).
|
||
*
|
||
* The default binding is <kbd>Tab</kbd>.
|
||
*/
|
||
signals [TOGGLE_HANDLE_FOCUS] =
|
||
g_signal_new (I_("toggle-handle-focus"),
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||
G_STRUCT_OFFSET (GtkPanedClass, toggle_handle_focus),
|
||
NULL, NULL,
|
||
_gtk_marshal_BOOLEAN__VOID,
|
||
G_TYPE_BOOLEAN, 0);
|
||
g_signal_set_va_marshaller (signals[TOGGLE_HANDLE_FOCUS],
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
_gtk_marshal_BOOLEAN__VOIDv);
|
||
|
||
/**
|
||
* GtkPaned::move-handle:
|
||
* @widget: the object that received the signal
|
||
* @scroll_type: a `GtkScrollType`
|
||
*
|
||
* Emitted to move the handle with key bindings.
|
||
*
|
||
* This is a [keybinding signal](class.SignalAction.html).
|
||
*/
|
||
signals[MOVE_HANDLE] =
|
||
g_signal_new (I_("move-handle"),
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||
G_STRUCT_OFFSET (GtkPanedClass, move_handle),
|
||
NULL, NULL,
|
||
_gtk_marshal_BOOLEAN__ENUM,
|
||
G_TYPE_BOOLEAN, 1,
|
||
GTK_TYPE_SCROLL_TYPE);
|
||
g_signal_set_va_marshaller (signals[MOVE_HANDLE],
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
_gtk_marshal_BOOLEAN__ENUMv);
|
||
|
||
/**
|
||
* GtkPaned::cycle-handle-focus:
|
||
* @widget: the object that received the signal
|
||
* @reversed: whether cycling backward or forward
|
||
*
|
||
* Emitted to cycle whether the paned should grab focus to allow
|
||
* the user to change position of the handle by using key bindings.
|
||
*
|
||
* This is a [keybinding signal](class.SignalAction.html).
|
||
*
|
||
* The default binding for this signal is <kbd>F8</kbd>.
|
||
*/
|
||
signals [CYCLE_HANDLE_FOCUS] =
|
||
g_signal_new (I_("cycle-handle-focus"),
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||
G_STRUCT_OFFSET (GtkPanedClass, cycle_handle_focus),
|
||
NULL, NULL,
|
||
_gtk_marshal_BOOLEAN__BOOLEAN,
|
||
G_TYPE_BOOLEAN, 1,
|
||
G_TYPE_BOOLEAN);
|
||
g_signal_set_va_marshaller (signals[CYCLE_HANDLE_FOCUS],
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
_gtk_marshal_BOOLEAN__BOOLEANv);
|
||
|
||
/**
|
||
* GtkPaned::accept-position:
|
||
* @widget: the object that received the signal
|
||
*
|
||
* Emitted to accept the current position of the handle when
|
||
* moving it using key bindings.
|
||
*
|
||
* This is a [keybinding signal](class.SignalAction.html).
|
||
*
|
||
* The default binding for this signal is <kbd>Return</kbd> or
|
||
* <kbd>Space</kbd>.
|
||
*/
|
||
signals [ACCEPT_POSITION] =
|
||
g_signal_new (I_("accept-position"),
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||
G_STRUCT_OFFSET (GtkPanedClass, accept_position),
|
||
NULL, NULL,
|
||
_gtk_marshal_BOOLEAN__VOID,
|
||
G_TYPE_BOOLEAN, 0);
|
||
g_signal_set_va_marshaller (signals[ACCEPT_POSITION],
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
_gtk_marshal_BOOLEAN__VOIDv);
|
||
|
||
/**
|
||
* GtkPaned::cancel-position:
|
||
* @widget: the object that received the signal
|
||
*
|
||
* Emitted to cancel moving the position of the handle using key
|
||
* bindings.
|
||
*
|
||
* The position of the handle will be reset to the value prior to
|
||
* moving it.
|
||
*
|
||
* This is a [keybinding signal](class.SignalAction.html).
|
||
*
|
||
* The default binding for this signal is <kbd>Escape</kbd>.
|
||
*/
|
||
signals [CANCEL_POSITION] =
|
||
g_signal_new (I_("cancel-position"),
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||
G_STRUCT_OFFSET (GtkPanedClass, cancel_position),
|
||
NULL, NULL,
|
||
_gtk_marshal_BOOLEAN__VOID,
|
||
G_TYPE_BOOLEAN, 0);
|
||
g_signal_set_va_marshaller (signals[CANCEL_POSITION],
|
||
G_TYPE_FROM_CLASS (object_class),
|
||
_gtk_marshal_BOOLEAN__VOIDv);
|
||
|
||
/* F6 and friends */
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_F6, 0,
|
||
"cycle-child-focus",
|
||
"(b)", FALSE);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_F6, GDK_SHIFT_MASK,
|
||
"cycle-child-focus",
|
||
"(b)", TRUE);
|
||
|
||
/* F8 and friends */
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_F8, 0,
|
||
"cycle-handle-focus",
|
||
"(b)", FALSE);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_F8, GDK_SHIFT_MASK,
|
||
"cycle-handle-focus",
|
||
"(b)", TRUE);
|
||
|
||
add_tab_bindings (widget_class, 0);
|
||
add_tab_bindings (widget_class, GDK_CONTROL_MASK);
|
||
add_tab_bindings (widget_class, GDK_SHIFT_MASK);
|
||
add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
|
||
|
||
/* accept and cancel positions */
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_Escape, 0,
|
||
"cancel-position",
|
||
NULL);
|
||
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_Return, 0,
|
||
"accept-position",
|
||
NULL);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_ISO_Enter, 0,
|
||
"accept-position",
|
||
NULL);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_KP_Enter, 0,
|
||
"accept-position",
|
||
NULL);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_space, 0,
|
||
"accept-position",
|
||
NULL);
|
||
gtk_widget_class_add_binding_signal (widget_class,
|
||
GDK_KEY_KP_Space, 0,
|
||
"accept-position",
|
||
NULL);
|
||
|
||
/* move handle */
|
||
add_move_binding (widget_class, GDK_KEY_Left, 0, GTK_SCROLL_STEP_LEFT);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Left, 0, GTK_SCROLL_STEP_LEFT);
|
||
add_move_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_LEFT);
|
||
|
||
add_move_binding (widget_class, GDK_KEY_Right, 0, GTK_SCROLL_STEP_RIGHT);
|
||
add_move_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Right, 0, GTK_SCROLL_STEP_RIGHT);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Right, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_RIGHT);
|
||
|
||
add_move_binding (widget_class, GDK_KEY_Up, 0, GTK_SCROLL_STEP_UP);
|
||
add_move_binding (widget_class, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Up, 0, GTK_SCROLL_STEP_UP);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_UP);
|
||
add_move_binding (widget_class, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_UP);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, GTK_SCROLL_PAGE_UP);
|
||
|
||
add_move_binding (widget_class, GDK_KEY_Down, 0, GTK_SCROLL_STEP_DOWN);
|
||
add_move_binding (widget_class, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Down, 0, GTK_SCROLL_STEP_DOWN);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_DOWN);
|
||
add_move_binding (widget_class, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, GTK_SCROLL_PAGE_RIGHT);
|
||
|
||
add_move_binding (widget_class, GDK_KEY_Home, 0, GTK_SCROLL_START);
|
||
add_move_binding (widget_class, GDK_KEY_KP_Home, 0, GTK_SCROLL_START);
|
||
add_move_binding (widget_class, GDK_KEY_End, 0, GTK_SCROLL_END);
|
||
add_move_binding (widget_class, GDK_KEY_KP_End, 0, GTK_SCROLL_END);
|
||
|
||
gtk_widget_class_set_css_name (widget_class, I_("paned"));
|
||
}
|
||
|
||
static GtkBuildableIface *parent_buildable_iface;
|
||
|
||
static void
|
||
gtk_paned_buildable_add_child (GtkBuildable *buildable,
|
||
GtkBuilder *builder,
|
||
GObject *child,
|
||
const char *type)
|
||
{
|
||
GtkPaned *self = GTK_PANED (buildable);
|
||
|
||
if (g_strcmp0 (type, "start") == 0)
|
||
{
|
||
gtk_paned_set_start_child (self, GTK_WIDGET (child));
|
||
gtk_paned_set_resize_start_child (self, FALSE);
|
||
gtk_paned_set_shrink_start_child (self, TRUE);
|
||
}
|
||
else if (g_strcmp0 (type, "end") == 0)
|
||
{
|
||
gtk_paned_set_end_child (self, GTK_WIDGET (child));
|
||
gtk_paned_set_resize_end_child (self, TRUE);
|
||
gtk_paned_set_shrink_end_child (self, TRUE);
|
||
}
|
||
else if (type == NULL && GTK_IS_WIDGET (child))
|
||
{
|
||
if (self->start_child == NULL)
|
||
{
|
||
gtk_paned_set_start_child (self, GTK_WIDGET (child));
|
||
gtk_paned_set_resize_start_child (self, FALSE);
|
||
gtk_paned_set_shrink_start_child (self, TRUE);
|
||
}
|
||
else if (self->end_child == NULL)
|
||
{
|
||
gtk_paned_set_end_child (self, GTK_WIDGET (child));
|
||
gtk_paned_set_resize_end_child (self, TRUE);
|
||
gtk_paned_set_shrink_end_child (self, TRUE);
|
||
}
|
||
else
|
||
g_warning ("GtkPaned only accepts two widgets as children");
|
||
}
|
||
else
|
||
parent_buildable_iface->add_child (buildable, builder, child, type);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_buildable_iface_init (GtkBuildableIface *iface)
|
||
{
|
||
parent_buildable_iface = g_type_interface_peek_parent (iface);
|
||
iface->add_child = gtk_paned_buildable_add_child;
|
||
}
|
||
|
||
static gboolean
|
||
accessible_range_set_current_value (GtkAccessibleRange *accessible_range,
|
||
double value)
|
||
{
|
||
gtk_paned_set_position (GTK_PANED (accessible_range), (int) value + 0.5);
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_accessible_range_init (GtkAccessibleRangeInterface *iface)
|
||
{
|
||
iface->set_current_value = accessible_range_set_current_value;
|
||
}
|
||
|
||
static gboolean
|
||
initiates_touch_drag (GtkPaned *paned,
|
||
double start_x,
|
||
double start_y)
|
||
{
|
||
int handle_size, handle_pos, drag_pos;
|
||
graphene_rect_t handle_area;
|
||
|
||
#define TOUCH_EXTRA_AREA_WIDTH 50
|
||
get_handle_area (paned, &handle_area);
|
||
|
||
if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||
{
|
||
handle_pos = handle_area.origin.x;
|
||
drag_pos = start_x;
|
||
handle_size = handle_area.size.width;
|
||
}
|
||
else
|
||
{
|
||
handle_pos = handle_area.origin.y;
|
||
drag_pos = start_y;
|
||
handle_size = handle_area.size.height;
|
||
}
|
||
|
||
if (drag_pos < handle_pos - TOUCH_EXTRA_AREA_WIDTH ||
|
||
drag_pos > handle_pos + handle_size + TOUCH_EXTRA_AREA_WIDTH)
|
||
return FALSE;
|
||
|
||
#undef TOUCH_EXTRA_AREA_WIDTH
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gesture_drag_begin_cb (GtkGestureDrag *gesture,
|
||
double start_x,
|
||
double start_y,
|
||
GtkPaned *paned)
|
||
{
|
||
GdkEventSequence *sequence;
|
||
graphene_rect_t handle_area;
|
||
GdkEvent *event;
|
||
GdkDevice *device;
|
||
gboolean is_touch;
|
||
|
||
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
|
||
event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
|
||
device = gdk_event_get_device (event);
|
||
paned->panning = FALSE;
|
||
|
||
is_touch = (gdk_event_get_event_type (event) == GDK_TOUCH_BEGIN ||
|
||
gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN);
|
||
|
||
get_handle_area (paned, &handle_area);
|
||
|
||
if ((is_touch && GTK_GESTURE (gesture) == paned->drag_gesture) ||
|
||
(!is_touch && GTK_GESTURE (gesture) == paned->pan_gesture))
|
||
{
|
||
gtk_gesture_set_state (GTK_GESTURE (gesture),
|
||
GTK_EVENT_SEQUENCE_DENIED);
|
||
return;
|
||
}
|
||
|
||
if (graphene_rect_contains_point (&handle_area, &(graphene_point_t){start_x, start_y}) ||
|
||
(is_touch && initiates_touch_drag (paned, start_x, start_y)))
|
||
{
|
||
if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||
paned->drag_pos = start_x - handle_area.origin.x;
|
||
else
|
||
paned->drag_pos = start_y - handle_area.origin.y;
|
||
|
||
paned->panning = TRUE;
|
||
|
||
gtk_gesture_set_state (GTK_GESTURE (gesture),
|
||
GTK_EVENT_SEQUENCE_CLAIMED);
|
||
}
|
||
else
|
||
{
|
||
gtk_gesture_set_state (GTK_GESTURE (gesture),
|
||
GTK_EVENT_SEQUENCE_DENIED);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gesture_drag_update_cb (GtkGestureDrag *gesture,
|
||
double offset_x,
|
||
double offset_y,
|
||
GtkPaned *paned)
|
||
{
|
||
double start_x, start_y;
|
||
|
||
gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture),
|
||
&start_x, &start_y);
|
||
update_drag (paned, start_x + offset_x, start_y + offset_y);
|
||
}
|
||
|
||
static void
|
||
gesture_drag_end_cb (GtkGestureDrag *gesture,
|
||
double offset_x,
|
||
double offset_y,
|
||
GtkPaned *paned)
|
||
{
|
||
if (!paned->panning)
|
||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||
|
||
paned->panning = FALSE;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_property (GObject *object,
|
||
guint prop_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case PROP_ORIENTATION:
|
||
gtk_paned_set_orientation (paned, g_value_get_enum (value));
|
||
break;
|
||
case PROP_POSITION:
|
||
gtk_paned_set_position (paned, g_value_get_int (value));
|
||
break;
|
||
case PROP_POSITION_SET:
|
||
if (paned->position_set != g_value_get_boolean (value))
|
||
{
|
||
paned->position_set = g_value_get_boolean (value);
|
||
gtk_widget_queue_resize (GTK_WIDGET (paned));
|
||
g_object_notify_by_pspec (object, pspec);
|
||
}
|
||
break;
|
||
case PROP_WIDE_HANDLE:
|
||
gtk_paned_set_wide_handle (paned, g_value_get_boolean (value));
|
||
break;
|
||
case PROP_RESIZE_START_CHILD:
|
||
gtk_paned_set_resize_start_child (paned, g_value_get_boolean (value));
|
||
break;
|
||
case PROP_RESIZE_END_CHILD:
|
||
gtk_paned_set_resize_end_child (paned, g_value_get_boolean (value));
|
||
break;
|
||
case PROP_SHRINK_START_CHILD:
|
||
gtk_paned_set_shrink_start_child (paned, g_value_get_boolean (value));
|
||
break;
|
||
case PROP_SHRINK_END_CHILD:
|
||
gtk_paned_set_shrink_end_child (paned, g_value_get_boolean (value));
|
||
break;
|
||
case PROP_START_CHILD:
|
||
gtk_paned_set_start_child (paned, g_value_get_object (value));
|
||
break;
|
||
case PROP_END_CHILD:
|
||
gtk_paned_set_end_child (paned, g_value_get_object (value));
|
||
break;
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_paned_get_property (GObject *object,
|
||
guint prop_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case PROP_ORIENTATION:
|
||
g_value_set_enum (value, paned->orientation);
|
||
break;
|
||
case PROP_POSITION:
|
||
g_value_set_int (value, paned->start_child_size);
|
||
break;
|
||
case PROP_POSITION_SET:
|
||
g_value_set_boolean (value, paned->position_set);
|
||
break;
|
||
case PROP_MIN_POSITION:
|
||
g_value_set_int (value, paned->min_position);
|
||
break;
|
||
case PROP_MAX_POSITION:
|
||
g_value_set_int (value, paned->max_position);
|
||
break;
|
||
case PROP_WIDE_HANDLE:
|
||
g_value_set_boolean (value, gtk_paned_get_wide_handle (paned));
|
||
break;
|
||
case PROP_RESIZE_START_CHILD:
|
||
g_value_set_boolean (value, paned->resize_start_child);
|
||
break;
|
||
case PROP_RESIZE_END_CHILD:
|
||
g_value_set_boolean (value, paned->resize_end_child);
|
||
break;
|
||
case PROP_SHRINK_START_CHILD:
|
||
g_value_set_boolean (value, paned->shrink_start_child);
|
||
break;
|
||
case PROP_SHRINK_END_CHILD:
|
||
g_value_set_boolean (value, paned->shrink_end_child);
|
||
break;
|
||
case PROP_START_CHILD:
|
||
g_value_set_object (value, gtk_paned_get_start_child (paned));
|
||
break;
|
||
case PROP_END_CHILD:
|
||
g_value_set_object (value, gtk_paned_get_end_child (paned));
|
||
break;
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_paned_dispose (GObject *object)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (object);
|
||
|
||
gtk_paned_set_saved_focus (paned, NULL);
|
||
gtk_paned_set_first_paned (paned, NULL);
|
||
|
||
g_clear_pointer (&paned->start_child, gtk_widget_unparent);
|
||
g_clear_pointer (&paned->end_child, gtk_widget_unparent);
|
||
g_clear_pointer (&paned->handle_widget, gtk_widget_unparent);
|
||
|
||
G_OBJECT_CLASS (gtk_paned_parent_class)->dispose (object);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_compute_position (GtkPaned *paned,
|
||
int allocation,
|
||
int start_child_req,
|
||
int end_child_req,
|
||
int *min_pos,
|
||
int *max_pos,
|
||
int *out_pos)
|
||
{
|
||
int min, max, pos;
|
||
|
||
min = paned->shrink_start_child ? 0 : start_child_req;
|
||
|
||
max = allocation;
|
||
if (!paned->shrink_end_child)
|
||
max = MAX (1, max - end_child_req);
|
||
max = MAX (min, max);
|
||
|
||
if (!paned->position_set)
|
||
{
|
||
if (paned->resize_start_child && !paned->resize_end_child)
|
||
pos = MAX (0, allocation - end_child_req);
|
||
else if (!paned->resize_start_child && paned->resize_end_child)
|
||
pos = start_child_req;
|
||
else if (start_child_req + end_child_req != 0)
|
||
pos = allocation * ((double)start_child_req / (start_child_req + end_child_req)) + 0.5;
|
||
else
|
||
pos = allocation * 0.5 + 0.5;
|
||
}
|
||
else
|
||
{
|
||
/* If the position was set before the initial allocation.
|
||
* (paned->last_allocation <= 0) just clamp it and leave it.
|
||
*/
|
||
if (paned->last_allocation > 0)
|
||
{
|
||
if (paned->resize_start_child && !paned->resize_end_child)
|
||
pos = paned->start_child_size + allocation - paned->last_allocation;
|
||
else if (!(!paned->resize_start_child && paned->resize_end_child))
|
||
pos = allocation * ((double) paned->start_child_size / (paned->last_allocation)) + 0.5;
|
||
else
|
||
pos = paned->start_child_size;
|
||
}
|
||
else
|
||
pos = paned->start_child_size;
|
||
}
|
||
|
||
pos = CLAMP (pos, min, max);
|
||
|
||
if (min_pos)
|
||
*min_pos = min;
|
||
if (max_pos)
|
||
*max_pos = max;
|
||
if (out_pos)
|
||
*out_pos = pos;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_get_preferred_size_for_orientation (GtkWidget *widget,
|
||
int size,
|
||
int *minimum,
|
||
int *natural)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
int child_min, child_nat;
|
||
|
||
*minimum = *natural = 0;
|
||
|
||
if (paned->start_child && gtk_widget_get_visible (paned->start_child))
|
||
{
|
||
gtk_widget_measure (paned->start_child, paned->orientation, size, &child_min, &child_nat, NULL, NULL);
|
||
if (paned->shrink_start_child)
|
||
*minimum = 0;
|
||
else
|
||
*minimum = child_min;
|
||
*natural = child_nat;
|
||
}
|
||
|
||
if (paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
gtk_widget_measure (paned->end_child, paned->orientation, size, &child_min, &child_nat, NULL, NULL);
|
||
|
||
if (!paned->shrink_end_child)
|
||
*minimum += child_min;
|
||
*natural += child_nat;
|
||
}
|
||
|
||
if (paned->start_child && gtk_widget_get_visible (paned->start_child) &&
|
||
paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
int handle_size;
|
||
|
||
gtk_widget_measure (paned->handle_widget,
|
||
paned->orientation,
|
||
-1,
|
||
NULL, &handle_size,
|
||
NULL, NULL);
|
||
|
||
*minimum += handle_size;
|
||
*natural += handle_size;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_paned_get_preferred_size_for_opposite_orientation (GtkWidget *widget,
|
||
int size,
|
||
int *minimum,
|
||
int *natural)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
int for_start_child, for_end_child, for_handle;
|
||
int child_min, child_nat;
|
||
|
||
if (size > -1 &&
|
||
paned->start_child && gtk_widget_get_visible (paned->start_child) &&
|
||
paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
int start_child_req, end_child_req;
|
||
|
||
gtk_widget_measure (paned->handle_widget,
|
||
paned->orientation,
|
||
-1,
|
||
NULL, &for_handle,
|
||
NULL, NULL);
|
||
|
||
gtk_widget_measure (paned->start_child, paned->orientation, -1, &start_child_req, NULL, NULL, NULL);
|
||
gtk_widget_measure (paned->end_child, paned->orientation, -1, &end_child_req, NULL, NULL, NULL);
|
||
|
||
gtk_paned_compute_position (paned,
|
||
size - for_handle, start_child_req, end_child_req,
|
||
NULL, NULL, &for_start_child);
|
||
|
||
for_end_child = size - for_start_child - for_handle;
|
||
|
||
if (paned->shrink_start_child)
|
||
for_start_child = MAX (start_child_req, for_start_child);
|
||
if (paned->shrink_end_child)
|
||
for_end_child = MAX (end_child_req, for_end_child);
|
||
}
|
||
else
|
||
{
|
||
for_start_child = size;
|
||
for_end_child = size;
|
||
for_handle = -1;
|
||
}
|
||
|
||
*minimum = *natural = 0;
|
||
|
||
if (paned->start_child && gtk_widget_get_visible (paned->start_child))
|
||
{
|
||
gtk_widget_measure (paned->start_child,
|
||
OPPOSITE_ORIENTATION (paned->orientation),
|
||
for_start_child,
|
||
&child_min, &child_nat,
|
||
NULL, NULL);
|
||
|
||
*minimum = child_min;
|
||
*natural = child_nat;
|
||
}
|
||
|
||
if (paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
gtk_widget_measure (paned->end_child,
|
||
OPPOSITE_ORIENTATION (paned->orientation),
|
||
for_end_child,
|
||
&child_min, &child_nat,
|
||
NULL, NULL);
|
||
|
||
*minimum = MAX (*minimum, child_min);
|
||
*natural = MAX (*natural, child_nat);
|
||
}
|
||
|
||
if (paned->start_child && gtk_widget_get_visible (paned->start_child) &&
|
||
paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
gtk_widget_measure (paned->handle_widget,
|
||
OPPOSITE_ORIENTATION (paned->orientation),
|
||
for_handle,
|
||
&child_min, &child_nat,
|
||
NULL, NULL);
|
||
|
||
*minimum = MAX (*minimum, child_min);
|
||
*natural = MAX (*natural, child_nat);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_paned_measure (GtkWidget *widget,
|
||
GtkOrientation orientation,
|
||
int for_size,
|
||
int *minimum,
|
||
int *natural,
|
||
int *minimum_baseline,
|
||
int *natural_baseline)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
|
||
if (orientation == paned->orientation)
|
||
gtk_paned_get_preferred_size_for_orientation (widget, for_size, minimum, natural);
|
||
else
|
||
gtk_paned_get_preferred_size_for_opposite_orientation (widget, for_size, minimum, natural);
|
||
}
|
||
|
||
static void
|
||
flip_child (int width,
|
||
GtkAllocation *child_pos)
|
||
{
|
||
child_pos->x = width - child_pos->x - child_pos->width;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_size_allocate (GtkWidget *widget,
|
||
int width,
|
||
int height,
|
||
int baseline)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
|
||
if (paned->start_child && gtk_widget_get_visible (paned->start_child) &&
|
||
paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
GtkAllocation start_child_allocation;
|
||
GtkAllocation end_child_allocation;
|
||
GtkAllocation handle_allocation;
|
||
int handle_size;
|
||
|
||
gtk_widget_measure (paned->handle_widget,
|
||
paned->orientation,
|
||
-1,
|
||
NULL, &handle_size,
|
||
NULL, NULL);
|
||
|
||
if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||
{
|
||
int start_child_width, end_child_width;
|
||
|
||
gtk_widget_measure (paned->start_child, GTK_ORIENTATION_HORIZONTAL,
|
||
height,
|
||
&start_child_width, NULL, NULL, NULL);
|
||
gtk_widget_measure (paned->end_child, GTK_ORIENTATION_HORIZONTAL,
|
||
height,
|
||
&end_child_width, NULL, NULL, NULL);
|
||
|
||
gtk_paned_calc_position (paned,
|
||
MAX (1, width - handle_size),
|
||
start_child_width,
|
||
end_child_width);
|
||
|
||
handle_allocation = (GdkRectangle){
|
||
paned->start_child_size,
|
||
0,
|
||
handle_size,
|
||
height
|
||
};
|
||
|
||
start_child_allocation.height = end_child_allocation.height = height;
|
||
start_child_allocation.width = MAX (1, paned->start_child_size);
|
||
start_child_allocation.x = 0;
|
||
start_child_allocation.y = end_child_allocation.y = 0;
|
||
|
||
end_child_allocation.x = start_child_allocation.x + paned->start_child_size + handle_size;
|
||
end_child_allocation.width = MAX (1, width - paned->start_child_size - handle_size);
|
||
|
||
if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL)
|
||
{
|
||
flip_child (width, &(end_child_allocation));
|
||
flip_child (width, &(start_child_allocation));
|
||
flip_child (width, &(handle_allocation));
|
||
}
|
||
|
||
if (start_child_width > start_child_allocation.width)
|
||
{
|
||
if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_LTR)
|
||
start_child_allocation.x -= start_child_width - start_child_allocation.width;
|
||
start_child_allocation.width = start_child_width;
|
||
}
|
||
|
||
if (end_child_width > end_child_allocation.width)
|
||
{
|
||
if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL)
|
||
end_child_allocation.x -= end_child_width - end_child_allocation.width;
|
||
end_child_allocation.width = end_child_width;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int start_child_height, end_child_height;
|
||
|
||
gtk_widget_measure (paned->start_child, GTK_ORIENTATION_VERTICAL,
|
||
width,
|
||
&start_child_height, NULL, NULL, NULL);
|
||
gtk_widget_measure (paned->end_child, GTK_ORIENTATION_VERTICAL,
|
||
width,
|
||
&end_child_height, NULL, NULL, NULL);
|
||
|
||
gtk_paned_calc_position (paned,
|
||
MAX (1, height - handle_size),
|
||
start_child_height,
|
||
end_child_height);
|
||
|
||
handle_allocation = (GdkRectangle){
|
||
0,
|
||
paned->start_child_size,
|
||
width,
|
||
handle_size,
|
||
};
|
||
|
||
start_child_allocation.width = end_child_allocation.width = width;
|
||
start_child_allocation.height = MAX (1, paned->start_child_size);
|
||
start_child_allocation.x = end_child_allocation.x = 0;
|
||
start_child_allocation.y = 0;
|
||
|
||
end_child_allocation.y = start_child_allocation.y + paned->start_child_size + handle_size;
|
||
end_child_allocation.height = MAX (1, height - end_child_allocation.y);
|
||
|
||
if (start_child_height > start_child_allocation.height)
|
||
{
|
||
start_child_allocation.y -= start_child_height - start_child_allocation.height;
|
||
start_child_allocation.height = start_child_height;
|
||
}
|
||
|
||
if (end_child_height > end_child_allocation.height)
|
||
end_child_allocation.height = end_child_height;
|
||
}
|
||
|
||
gtk_widget_set_child_visible (paned->handle_widget, TRUE);
|
||
|
||
gtk_widget_size_allocate (paned->handle_widget, &handle_allocation, -1);
|
||
gtk_widget_size_allocate (paned->start_child, &start_child_allocation, -1);
|
||
gtk_widget_size_allocate (paned->end_child, &end_child_allocation, -1);
|
||
}
|
||
else
|
||
{
|
||
if (paned->start_child && gtk_widget_get_visible (paned->start_child))
|
||
{
|
||
gtk_widget_set_child_visible (paned->start_child, TRUE);
|
||
|
||
gtk_widget_size_allocate (paned->start_child,
|
||
&(GtkAllocation) {0, 0, width, height}, -1);
|
||
}
|
||
else if (paned->end_child && gtk_widget_get_visible (paned->end_child))
|
||
{
|
||
gtk_widget_set_child_visible (paned->end_child, TRUE);
|
||
|
||
gtk_widget_size_allocate (paned->end_child,
|
||
&(GtkAllocation) {0, 0, width, height}, -1);
|
||
|
||
}
|
||
|
||
gtk_widget_set_child_visible (paned->handle_widget, FALSE);
|
||
}
|
||
|
||
gtk_accessible_update_property (GTK_ACCESSIBLE (paned),
|
||
GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, 0.0,
|
||
GTK_ACCESSIBLE_PROPERTY_VALUE_MAX,
|
||
(double) (paned->orientation == GTK_ORIENTATION_HORIZONTAL ? width : height),
|
||
GTK_ACCESSIBLE_PROPERTY_VALUE_NOW,
|
||
(double) paned->start_child_size,
|
||
-1);
|
||
}
|
||
|
||
|
||
static void
|
||
gtk_paned_unrealize (GtkWidget *widget)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
|
||
gtk_paned_set_last_start_child_focus (paned, NULL);
|
||
gtk_paned_set_last_end_child_focus (paned, NULL);
|
||
gtk_paned_set_saved_focus (paned, NULL);
|
||
gtk_paned_set_first_paned (paned, NULL);
|
||
|
||
GTK_WIDGET_CLASS (gtk_paned_parent_class)->unrealize (widget);
|
||
}
|
||
|
||
static void
|
||
connect_drag_gesture_signals (GtkPaned *paned,
|
||
GtkGesture *gesture)
|
||
{
|
||
g_signal_connect (gesture, "drag-begin",
|
||
G_CALLBACK (gesture_drag_begin_cb), paned);
|
||
g_signal_connect (gesture, "drag-update",
|
||
G_CALLBACK (gesture_drag_update_cb), paned);
|
||
g_signal_connect (gesture, "drag-end",
|
||
G_CALLBACK (gesture_drag_end_cb), paned);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_init (GtkPaned *paned)
|
||
{
|
||
GtkGesture *gesture;
|
||
|
||
gtk_widget_set_focusable (GTK_WIDGET (paned), TRUE);
|
||
gtk_widget_set_overflow (GTK_WIDGET (paned), GTK_OVERFLOW_HIDDEN);
|
||
|
||
paned->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||
|
||
paned->start_child = NULL;
|
||
paned->end_child = NULL;
|
||
|
||
paned->position_set = FALSE;
|
||
paned->last_allocation = -1;
|
||
|
||
paned->last_start_child_focus = NULL;
|
||
paned->last_end_child_focus = NULL;
|
||
paned->in_recursion = FALSE;
|
||
paned->original_position = -1;
|
||
paned->max_position = G_MAXINT;
|
||
paned->resize_start_child = TRUE;
|
||
paned->resize_end_child = TRUE;
|
||
paned->shrink_start_child = TRUE;
|
||
paned->shrink_end_child = TRUE;
|
||
|
||
gtk_widget_update_orientation (GTK_WIDGET (paned), paned->orientation);
|
||
|
||
/* Touch gesture */
|
||
gesture = gtk_gesture_pan_new (GTK_ORIENTATION_HORIZONTAL);
|
||
connect_drag_gesture_signals (paned, gesture);
|
||
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), TRUE);
|
||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
|
||
GTK_PHASE_CAPTURE);
|
||
gtk_widget_add_controller (GTK_WIDGET (paned), GTK_EVENT_CONTROLLER (gesture));
|
||
paned->pan_gesture = gesture;
|
||
|
||
/* Pointer gesture */
|
||
gesture = gtk_gesture_drag_new ();
|
||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
|
||
GTK_PHASE_CAPTURE);
|
||
connect_drag_gesture_signals (paned, gesture);
|
||
gtk_widget_add_controller (GTK_WIDGET (paned), GTK_EVENT_CONTROLLER (gesture));
|
||
paned->drag_gesture = gesture;
|
||
|
||
paned->handle_widget = gtk_paned_handle_new ();
|
||
gtk_widget_set_parent (paned->handle_widget, GTK_WIDGET (paned));
|
||
gtk_widget_set_cursor_from_name (paned->handle_widget, "col-resize");
|
||
}
|
||
|
||
static gboolean
|
||
is_rtl (GtkPaned *paned)
|
||
{
|
||
return paned->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||
gtk_widget_get_direction (GTK_WIDGET (paned)) == GTK_TEXT_DIR_RTL;
|
||
}
|
||
|
||
static void
|
||
update_drag (GtkPaned *paned,
|
||
int xpos,
|
||
int ypos)
|
||
{
|
||
int pos;
|
||
int handle_size;
|
||
int size;
|
||
|
||
if (paned->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||
pos = xpos;
|
||
else
|
||
pos = ypos;
|
||
|
||
pos -= paned->drag_pos;
|
||
|
||
if (is_rtl (paned))
|
||
{
|
||
gtk_widget_measure (paned->handle_widget,
|
||
GTK_ORIENTATION_HORIZONTAL,
|
||
-1,
|
||
NULL, &handle_size,
|
||
NULL, NULL);
|
||
|
||
size = gtk_widget_get_width (GTK_WIDGET (paned)) - pos - handle_size;
|
||
}
|
||
else
|
||
{
|
||
size = pos;
|
||
}
|
||
|
||
size = CLAMP (size, paned->min_position, paned->max_position);
|
||
|
||
if (size != paned->start_child_size)
|
||
gtk_paned_set_position (paned, size);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_css_changed (GtkWidget *widget,
|
||
GtkCssStyleChange *change)
|
||
{
|
||
GTK_WIDGET_CLASS (gtk_paned_parent_class)->css_changed (widget, change);
|
||
|
||
if (change == NULL ||
|
||
gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_ICON_SIZE))
|
||
{
|
||
gtk_widget_queue_resize (widget);
|
||
}
|
||
else if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_ICON_TEXTURE |
|
||
GTK_CSS_AFFECTS_ICON_REDRAW))
|
||
{
|
||
gtk_widget_queue_draw (widget);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_new:
|
||
* @orientation: the paned’s orientation.
|
||
*
|
||
* Creates a new `GtkPaned` widget.
|
||
*
|
||
* Returns: the newly created paned widget
|
||
*/
|
||
GtkWidget *
|
||
gtk_paned_new (GtkOrientation orientation)
|
||
{
|
||
return g_object_new (GTK_TYPE_PANED,
|
||
"orientation", orientation,
|
||
NULL);
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_start_child: (attributes org.gtk.Method.set_property=start-child)
|
||
* @paned: a `GtkPaned`
|
||
* @child: (nullable): the widget to add
|
||
*
|
||
* Sets the start child of @paned to @child.
|
||
*
|
||
* If @child is `NULL`, the existing child will be removed.
|
||
*/
|
||
void
|
||
gtk_paned_set_start_child (GtkPaned *paned,
|
||
GtkWidget *child)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
g_return_if_fail (child == NULL || paned->start_child == child || gtk_widget_get_parent (child) == NULL);
|
||
|
||
if (paned->start_child == child)
|
||
return;
|
||
|
||
g_clear_pointer (&paned->start_child, gtk_widget_unparent);
|
||
|
||
if (child)
|
||
{
|
||
paned->start_child = child;
|
||
gtk_widget_insert_before (child, GTK_WIDGET (paned), paned->handle_widget);
|
||
}
|
||
|
||
g_object_notify (G_OBJECT (paned), "start-child");
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_start_child: (attributes org.gtk.Method.get_property=start-child)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Retrieves the start child of the given `GtkPaned`.
|
||
*
|
||
* Returns: (transfer none) (nullable): the start child widget
|
||
*/
|
||
GtkWidget *
|
||
gtk_paned_get_start_child (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), NULL);
|
||
|
||
return paned->start_child;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_resize_start_child: (attributes org.gtk.Method.set_property=resize-start-child)
|
||
* @paned: a `GtkPaned`
|
||
* @resize: true to let the start child be resized
|
||
*
|
||
* Sets whether the [property@Gtk.Paned:start-child] can be resized.
|
||
*/
|
||
void
|
||
gtk_paned_set_resize_start_child (GtkPaned *paned,
|
||
gboolean resize)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
|
||
if (paned->resize_start_child == resize)
|
||
return;
|
||
|
||
paned->resize_start_child = resize;
|
||
|
||
g_object_notify (G_OBJECT (paned), "resize-start-child");
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_resize_start_child: (attributes org.gtk.Method.get_property=resize-start-child)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Returns whether the [property@Gtk.Paned:start-child] can be resized.
|
||
*
|
||
* Returns: true if the start child is resizable
|
||
*/
|
||
gboolean
|
||
gtk_paned_get_resize_start_child (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), FALSE);
|
||
|
||
return paned->resize_start_child;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_shrink_start_child: (attributes org.gtk.Method.set_property=shrink-start-child)
|
||
* @paned: a `GtkPaned`
|
||
* @resize: true to let the start child be shrunk
|
||
*
|
||
* Sets whether the [property@Gtk.Paned:start-child] can shrink.
|
||
*/
|
||
void
|
||
gtk_paned_set_shrink_start_child (GtkPaned *paned,
|
||
gboolean shrink)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
|
||
if (paned->shrink_start_child == shrink)
|
||
return;
|
||
|
||
paned->shrink_start_child = shrink;
|
||
|
||
g_object_notify (G_OBJECT (paned), "shrink-start-child");
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_shrink_start_child: (attributes org.gtk.Method.get_property=shrink-start-child)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Returns whether the [property@Gtk.Paned:start-child] can shrink.
|
||
*
|
||
* Returns: true if the start child is shrinkable
|
||
*/
|
||
gboolean
|
||
gtk_paned_get_shrink_start_child (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), FALSE);
|
||
|
||
return paned->shrink_start_child;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_end_child: (attributes org.gtk.Method.set_property=end-child)
|
||
* @paned: a `GtkPaned`
|
||
* @child: (nullable): the widget to add
|
||
*
|
||
* Sets the end child of @paned to @child.
|
||
*
|
||
* If @child is `NULL`, the existing child will be removed.
|
||
*/
|
||
void
|
||
gtk_paned_set_end_child (GtkPaned *paned,
|
||
GtkWidget *child)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
g_return_if_fail (child == NULL || paned->end_child == child || gtk_widget_get_parent (child) == NULL);
|
||
|
||
if (paned->end_child == child)
|
||
return;
|
||
|
||
g_clear_pointer (&paned->end_child, gtk_widget_unparent);
|
||
|
||
if (child)
|
||
{
|
||
paned->end_child = child;
|
||
gtk_widget_insert_after (child, GTK_WIDGET (paned), paned->handle_widget);
|
||
}
|
||
|
||
g_object_notify (G_OBJECT (paned), "end-child");
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_end_child: (attributes org.gtk.Method.get_property=end-child)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Retrieves the end child of the given `GtkPaned`.
|
||
*
|
||
* Returns: (transfer none) (nullable): the end child widget
|
||
*/
|
||
GtkWidget *
|
||
gtk_paned_get_end_child (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), NULL);
|
||
|
||
return paned->end_child;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_resize_end_child: (attributes org.gtk.Method.set_property=resize-end-child)
|
||
* @paned: a `GtkPaned`
|
||
* @resize: true to let the end child be resized
|
||
*
|
||
* Sets whether the [property@Gtk.Paned:end-child] can be resized.
|
||
*/
|
||
void
|
||
gtk_paned_set_resize_end_child (GtkPaned *paned,
|
||
gboolean resize)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
|
||
if (paned->resize_end_child == resize)
|
||
return;
|
||
|
||
paned->resize_end_child = resize;
|
||
|
||
g_object_notify (G_OBJECT (paned), "resize-end-child");
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_resize_end_child: (attributes org.gtk.Method.get_property=resize-end-child)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Returns whether the [property@Gtk.Paned:end-child] can be resized.
|
||
*
|
||
* Returns: true if the end child is resizable
|
||
*/
|
||
gboolean
|
||
gtk_paned_get_resize_end_child (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), FALSE);
|
||
|
||
return paned->resize_end_child;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_shrink_end_child: (attributes org.gtk.Method.set_property=shrink-end-child)
|
||
* @paned: a `GtkPaned`
|
||
* @resize: true to let the end child be shrunk
|
||
*
|
||
* Sets whether the [property@Gtk.Paned:end-child] can shrink.
|
||
*/
|
||
void
|
||
gtk_paned_set_shrink_end_child (GtkPaned *paned,
|
||
gboolean shrink)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
|
||
if (paned->shrink_end_child == shrink)
|
||
return;
|
||
|
||
paned->shrink_end_child = shrink;
|
||
|
||
g_object_notify (G_OBJECT (paned), "shrink-end-child");
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_shrink_end_child: (attributes org.gtk.Method.get_property=shrink-end-child)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Returns whether the [property@Gtk.Paned:end-child] can shrink.
|
||
*
|
||
* Returns: true if the end child is shrinkable
|
||
*/
|
||
gboolean
|
||
gtk_paned_get_shrink_end_child (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), FALSE);
|
||
|
||
return paned->shrink_end_child;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_position: (attributes org.gtk.Method.get_property=position)
|
||
* @paned: a `GtkPaned` widget
|
||
*
|
||
* Obtains the position of the divider between the two panes.
|
||
*
|
||
* Returns: the position of the divider, in pixels
|
||
**/
|
||
int
|
||
gtk_paned_get_position (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), 0);
|
||
|
||
return paned->start_child_size;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_position: (attributes org.gtk.Method.set_property=position)
|
||
* @paned: a `GtkPaned` widget
|
||
* @position: pixel position of divider, a negative value means that the position
|
||
* is unset
|
||
*
|
||
* Sets the position of the divider between the two panes.
|
||
*/
|
||
void
|
||
gtk_paned_set_position (GtkPaned *paned,
|
||
int position)
|
||
{
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
|
||
g_object_freeze_notify (G_OBJECT (paned));
|
||
|
||
if (position >= 0)
|
||
{
|
||
/* We don't clamp here - the assumption is that
|
||
* if the total allocation changes at the same time
|
||
* as the position, the position set is with reference
|
||
* to the new total size. If only the position changes,
|
||
* then clamping will occur in gtk_paned_calc_position()
|
||
*/
|
||
|
||
if (!paned->position_set)
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_POSITION_SET]);
|
||
|
||
if (paned->start_child_size != position)
|
||
{
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_POSITION]);
|
||
gtk_widget_queue_allocate (GTK_WIDGET (paned));
|
||
}
|
||
|
||
paned->start_child_size = position;
|
||
paned->position_set = TRUE;
|
||
}
|
||
else
|
||
{
|
||
if (paned->position_set)
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_POSITION_SET]);
|
||
|
||
paned->position_set = FALSE;
|
||
}
|
||
|
||
g_object_thaw_notify (G_OBJECT (paned));
|
||
|
||
#ifdef G_OS_WIN32
|
||
/* Hacky work-around for bug #144269 */
|
||
if (paned->end_child != NULL)
|
||
{
|
||
gtk_widget_queue_draw (paned->end_child);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
gtk_paned_calc_position (GtkPaned *paned,
|
||
int allocation,
|
||
int start_child_req,
|
||
int end_child_req)
|
||
{
|
||
int old_position;
|
||
int old_min_position;
|
||
int old_max_position;
|
||
|
||
old_position = paned->start_child_size;
|
||
old_min_position = paned->min_position;
|
||
old_max_position = paned->max_position;
|
||
|
||
gtk_paned_compute_position (paned,
|
||
allocation, start_child_req, end_child_req,
|
||
&paned->min_position, &paned->max_position,
|
||
&paned->start_child_size);
|
||
|
||
gtk_widget_set_child_visible (paned->start_child, paned->start_child_size != 0);
|
||
gtk_widget_set_child_visible (paned->end_child, paned->start_child_size != allocation);
|
||
|
||
g_object_freeze_notify (G_OBJECT (paned));
|
||
if (paned->start_child_size != old_position)
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_POSITION]);
|
||
if (paned->min_position != old_min_position)
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_MIN_POSITION]);
|
||
if (paned->max_position != old_max_position)
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_MAX_POSITION]);
|
||
g_object_thaw_notify (G_OBJECT (paned));
|
||
|
||
paned->last_allocation = allocation;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_saved_focus (GtkPaned *paned, GtkWidget *widget)
|
||
{
|
||
if (paned->saved_focus)
|
||
g_object_remove_weak_pointer (G_OBJECT (paned->saved_focus),
|
||
(gpointer *)&(paned->saved_focus));
|
||
|
||
paned->saved_focus = widget;
|
||
|
||
if (paned->saved_focus)
|
||
g_object_add_weak_pointer (G_OBJECT (paned->saved_focus),
|
||
(gpointer *)&(paned->saved_focus));
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_first_paned (GtkPaned *paned, GtkPaned *first_paned)
|
||
{
|
||
if (paned->first_paned)
|
||
g_object_remove_weak_pointer (G_OBJECT (paned->first_paned),
|
||
(gpointer *)&(paned->first_paned));
|
||
|
||
paned->first_paned = first_paned;
|
||
|
||
if (paned->first_paned)
|
||
g_object_add_weak_pointer (G_OBJECT (paned->first_paned),
|
||
(gpointer *)&(paned->first_paned));
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_last_start_child_focus (GtkPaned *paned, GtkWidget *widget)
|
||
{
|
||
if (paned->last_start_child_focus)
|
||
g_object_remove_weak_pointer (G_OBJECT (paned->last_start_child_focus),
|
||
(gpointer *)&(paned->last_start_child_focus));
|
||
|
||
paned->last_start_child_focus = widget;
|
||
|
||
if (paned->last_start_child_focus)
|
||
g_object_add_weak_pointer (G_OBJECT (paned->last_start_child_focus),
|
||
(gpointer *)&(paned->last_start_child_focus));
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_last_end_child_focus (GtkPaned *paned, GtkWidget *widget)
|
||
{
|
||
if (paned->last_end_child_focus)
|
||
g_object_remove_weak_pointer (G_OBJECT (paned->last_end_child_focus),
|
||
(gpointer *)&(paned->last_end_child_focus));
|
||
|
||
paned->last_end_child_focus = widget;
|
||
|
||
if (paned->last_end_child_focus)
|
||
g_object_add_weak_pointer (G_OBJECT (paned->last_end_child_focus),
|
||
(gpointer *)&(paned->last_end_child_focus));
|
||
}
|
||
|
||
static GtkWidget *
|
||
paned_get_focus_widget (GtkPaned *paned)
|
||
{
|
||
GtkWidget *toplevel;
|
||
|
||
toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (paned)));
|
||
if (GTK_IS_WINDOW (toplevel))
|
||
return gtk_window_get_focus (GTK_WINDOW (toplevel));
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_set_focus_child (GtkWidget *widget,
|
||
GtkWidget *child)
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
GtkWidget *focus_child;
|
||
|
||
if (child == NULL)
|
||
{
|
||
GtkWidget *last_focus;
|
||
GtkWidget *w;
|
||
|
||
last_focus = paned_get_focus_widget (paned);
|
||
|
||
if (last_focus)
|
||
{
|
||
/* If there is one or more paned widgets between us and the
|
||
* focus widget, we want the topmost of those as last_focus
|
||
*/
|
||
for (w = last_focus; w && w != GTK_WIDGET (paned); w = gtk_widget_get_parent (w))
|
||
if (GTK_IS_PANED (w))
|
||
last_focus = w;
|
||
|
||
if (w == NULL)
|
||
{
|
||
g_warning ("Error finding last focus widget of GtkPaned %p, "
|
||
"gtk_paned_set_focus_child was called on widget %p "
|
||
"which is not child of %p.",
|
||
widget, child, widget);
|
||
return;
|
||
}
|
||
|
||
focus_child = gtk_widget_get_focus_child (widget);
|
||
if (focus_child == paned->start_child)
|
||
gtk_paned_set_last_start_child_focus (paned, last_focus);
|
||
else if (focus_child == paned->end_child)
|
||
gtk_paned_set_last_end_child_focus (paned, last_focus);
|
||
}
|
||
}
|
||
|
||
GTK_WIDGET_CLASS (gtk_paned_parent_class)->set_focus_child (widget, child);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_get_cycle_chain (GtkPaned *paned,
|
||
GtkDirectionType direction,
|
||
GList **widgets)
|
||
{
|
||
GtkWidget *ancestor = NULL;
|
||
GtkWidget *focus_child;
|
||
GtkWidget *parent;
|
||
GtkWidget *widget = GTK_WIDGET (paned);
|
||
GList *temp_list = NULL;
|
||
GList *list;
|
||
|
||
if (paned->in_recursion)
|
||
return;
|
||
|
||
g_assert (widgets != NULL);
|
||
|
||
if (paned->last_start_child_focus &&
|
||
!gtk_widget_is_ancestor (paned->last_start_child_focus, widget))
|
||
{
|
||
gtk_paned_set_last_start_child_focus (paned, NULL);
|
||
}
|
||
|
||
if (paned->last_end_child_focus &&
|
||
!gtk_widget_is_ancestor (paned->last_end_child_focus, widget))
|
||
{
|
||
gtk_paned_set_last_end_child_focus (paned, NULL);
|
||
}
|
||
|
||
parent = gtk_widget_get_parent (widget);
|
||
if (parent)
|
||
ancestor = gtk_widget_get_ancestor (parent, GTK_TYPE_PANED);
|
||
|
||
/* The idea here is that temp_list is a list of widgets we want to cycle
|
||
* to. The list is prioritized so that the first element is our first
|
||
* choice, the next our second, and so on.
|
||
*
|
||
* We can't just use g_list_reverse(), because we want to try
|
||
* paned->last_child?_focus before paned->child?, both when we
|
||
* are going forward and backward.
|
||
*/
|
||
focus_child = gtk_widget_get_focus_child (GTK_WIDGET (paned));
|
||
if (direction == GTK_DIR_TAB_FORWARD)
|
||
{
|
||
if (focus_child == paned->start_child)
|
||
{
|
||
temp_list = g_list_append (temp_list, paned->last_end_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->end_child);
|
||
temp_list = g_list_append (temp_list, ancestor);
|
||
}
|
||
else if (focus_child == paned->end_child)
|
||
{
|
||
temp_list = g_list_append (temp_list, ancestor);
|
||
temp_list = g_list_append (temp_list, paned->last_start_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->start_child);
|
||
}
|
||
else
|
||
{
|
||
temp_list = g_list_append (temp_list, paned->last_start_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->start_child);
|
||
temp_list = g_list_append (temp_list, paned->last_end_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->end_child);
|
||
temp_list = g_list_append (temp_list, ancestor);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (focus_child == paned->start_child)
|
||
{
|
||
temp_list = g_list_append (temp_list, ancestor);
|
||
temp_list = g_list_append (temp_list, paned->last_end_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->end_child);
|
||
}
|
||
else if (focus_child == paned->end_child)
|
||
{
|
||
temp_list = g_list_append (temp_list, paned->last_start_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->start_child);
|
||
temp_list = g_list_append (temp_list, ancestor);
|
||
}
|
||
else
|
||
{
|
||
temp_list = g_list_append (temp_list, paned->last_end_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->end_child);
|
||
temp_list = g_list_append (temp_list, paned->last_start_child_focus);
|
||
temp_list = g_list_append (temp_list, paned->start_child);
|
||
temp_list = g_list_append (temp_list, ancestor);
|
||
}
|
||
}
|
||
|
||
/* Walk the list and expand all the paned widgets. */
|
||
for (list = temp_list; list != NULL; list = list->next)
|
||
{
|
||
widget = list->data;
|
||
|
||
if (widget)
|
||
{
|
||
if (GTK_IS_PANED (widget))
|
||
{
|
||
paned->in_recursion = TRUE;
|
||
gtk_paned_get_cycle_chain (GTK_PANED (widget), direction, widgets);
|
||
paned->in_recursion = FALSE;
|
||
}
|
||
else
|
||
{
|
||
*widgets = g_list_append (*widgets, widget);
|
||
}
|
||
}
|
||
}
|
||
|
||
g_list_free (temp_list);
|
||
}
|
||
|
||
static gboolean
|
||
gtk_paned_cycle_child_focus (GtkPaned *paned,
|
||
gboolean reversed)
|
||
{
|
||
GList *cycle_chain = NULL;
|
||
GList *list;
|
||
|
||
GtkDirectionType direction = reversed? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
|
||
|
||
/* ignore f6 if the handle is focused */
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
return TRUE;
|
||
|
||
/* we can't just let the event propagate up the hierarchy,
|
||
* because the paned will want to cycle focus _unless_ an
|
||
* ancestor paned handles the event
|
||
*/
|
||
gtk_paned_get_cycle_chain (paned, direction, &cycle_chain);
|
||
|
||
for (list = cycle_chain; list != NULL; list = list->next)
|
||
if (gtk_widget_child_focus (GTK_WIDGET (list->data), direction))
|
||
break;
|
||
|
||
g_list_free (cycle_chain);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
get_child_panes (GtkWidget *widget,
|
||
GList **panes)
|
||
{
|
||
if (!widget || !gtk_widget_get_realized (widget))
|
||
return;
|
||
|
||
if (GTK_IS_PANED (widget))
|
||
{
|
||
GtkPaned *paned = GTK_PANED (widget);
|
||
|
||
get_child_panes (paned->start_child, panes);
|
||
*panes = g_list_prepend (*panes, widget);
|
||
get_child_panes (paned->end_child, panes);
|
||
}
|
||
else
|
||
{
|
||
GtkWidget *child;
|
||
|
||
for (child = gtk_widget_get_first_child (widget);
|
||
child != NULL;
|
||
child = gtk_widget_get_next_sibling (child))
|
||
get_child_panes (child, panes);
|
||
}
|
||
}
|
||
|
||
static GList *
|
||
get_all_panes (GtkPaned *paned)
|
||
{
|
||
GtkPaned *topmost = NULL;
|
||
GList *result = NULL;
|
||
GtkWidget *w;
|
||
|
||
for (w = GTK_WIDGET (paned); w != NULL; w = gtk_widget_get_parent (w))
|
||
{
|
||
if (GTK_IS_PANED (w))
|
||
topmost = GTK_PANED (w);
|
||
}
|
||
|
||
g_assert (topmost);
|
||
|
||
get_child_panes (GTK_WIDGET (topmost), &result);
|
||
|
||
return g_list_reverse (result);
|
||
}
|
||
|
||
static void
|
||
gtk_paned_find_neighbours (GtkPaned *paned,
|
||
GtkPaned **next,
|
||
GtkPaned **prev)
|
||
{
|
||
GList *all_panes;
|
||
GList *this_link;
|
||
|
||
all_panes = get_all_panes (paned);
|
||
g_assert (all_panes);
|
||
|
||
this_link = g_list_find (all_panes, paned);
|
||
|
||
g_assert (this_link);
|
||
|
||
if (this_link->next)
|
||
*next = this_link->next->data;
|
||
else
|
||
*next = all_panes->data;
|
||
|
||
if (this_link->prev)
|
||
*prev = this_link->prev->data;
|
||
else
|
||
*prev = g_list_last (all_panes)->data;
|
||
|
||
g_list_free (all_panes);
|
||
}
|
||
|
||
static gboolean
|
||
gtk_paned_move_handle (GtkPaned *paned,
|
||
GtkScrollType scroll)
|
||
{
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
{
|
||
int old_position;
|
||
int new_position;
|
||
int increment;
|
||
|
||
enum {
|
||
SINGLE_STEP_SIZE = 1,
|
||
PAGE_STEP_SIZE = 75
|
||
};
|
||
|
||
new_position = old_position = gtk_paned_get_position (paned);
|
||
increment = 0;
|
||
|
||
switch (scroll)
|
||
{
|
||
case GTK_SCROLL_STEP_LEFT:
|
||
case GTK_SCROLL_STEP_UP:
|
||
case GTK_SCROLL_STEP_BACKWARD:
|
||
increment = - SINGLE_STEP_SIZE;
|
||
break;
|
||
|
||
case GTK_SCROLL_STEP_RIGHT:
|
||
case GTK_SCROLL_STEP_DOWN:
|
||
case GTK_SCROLL_STEP_FORWARD:
|
||
increment = SINGLE_STEP_SIZE;
|
||
break;
|
||
|
||
case GTK_SCROLL_PAGE_LEFT:
|
||
case GTK_SCROLL_PAGE_UP:
|
||
case GTK_SCROLL_PAGE_BACKWARD:
|
||
increment = - PAGE_STEP_SIZE;
|
||
break;
|
||
|
||
case GTK_SCROLL_PAGE_RIGHT:
|
||
case GTK_SCROLL_PAGE_DOWN:
|
||
case GTK_SCROLL_PAGE_FORWARD:
|
||
increment = PAGE_STEP_SIZE;
|
||
break;
|
||
|
||
case GTK_SCROLL_START:
|
||
new_position = paned->min_position;
|
||
break;
|
||
|
||
case GTK_SCROLL_END:
|
||
new_position = paned->max_position;
|
||
break;
|
||
|
||
case GTK_SCROLL_NONE:
|
||
case GTK_SCROLL_JUMP:
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (increment)
|
||
{
|
||
if (is_rtl (paned))
|
||
increment = -increment;
|
||
|
||
new_position = old_position + increment;
|
||
}
|
||
|
||
new_position = CLAMP (new_position, paned->min_position, paned->max_position);
|
||
|
||
if (old_position != new_position)
|
||
gtk_paned_set_position (paned, new_position);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
gtk_paned_restore_focus (GtkPaned *paned)
|
||
{
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
{
|
||
if (paned->saved_focus &&
|
||
gtk_widget_get_sensitive (paned->saved_focus))
|
||
{
|
||
gtk_widget_grab_focus (paned->saved_focus);
|
||
}
|
||
else
|
||
{
|
||
/* the saved focus is somehow not available for focusing,
|
||
* try
|
||
* 1) tabbing into the paned widget
|
||
* if that didn't work,
|
||
* 2) unset focus for the window if there is one
|
||
*/
|
||
|
||
if (!gtk_widget_child_focus (GTK_WIDGET (paned), GTK_DIR_TAB_FORWARD))
|
||
{
|
||
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (paned));
|
||
gtk_root_set_focus (root, NULL);
|
||
}
|
||
}
|
||
|
||
gtk_paned_set_saved_focus (paned, NULL);
|
||
gtk_paned_set_first_paned (paned, NULL);
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
gtk_paned_accept_position (GtkPaned *paned)
|
||
{
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
{
|
||
paned->original_position = -1;
|
||
gtk_paned_restore_focus (paned);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
static gboolean
|
||
gtk_paned_cancel_position (GtkPaned *paned)
|
||
{
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
{
|
||
if (paned->original_position != -1)
|
||
{
|
||
gtk_paned_set_position (paned, paned->original_position);
|
||
paned->original_position = -1;
|
||
}
|
||
|
||
gtk_paned_restore_focus (paned);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
gtk_paned_cycle_handle_focus (GtkPaned *paned,
|
||
gboolean reversed)
|
||
{
|
||
GtkPaned *next, *prev;
|
||
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
{
|
||
GtkPaned *focus = NULL;
|
||
|
||
if (!paned->first_paned)
|
||
{
|
||
/* The first_pane has disappeared. As an ad-hoc solution,
|
||
* we make the currently focused paned the first_paned. To the
|
||
* user this will seem like the paned cycling has been reset.
|
||
*/
|
||
|
||
gtk_paned_set_first_paned (paned, paned);
|
||
}
|
||
|
||
gtk_paned_find_neighbours (paned, &next, &prev);
|
||
|
||
if (reversed && prev &&
|
||
prev != paned && paned != paned->first_paned)
|
||
{
|
||
focus = prev;
|
||
}
|
||
else if (!reversed && next &&
|
||
next != paned && next != paned->first_paned)
|
||
{
|
||
focus = next;
|
||
}
|
||
else
|
||
{
|
||
gtk_paned_accept_position (paned);
|
||
return TRUE;
|
||
}
|
||
|
||
g_assert (focus);
|
||
|
||
gtk_paned_set_saved_focus (focus, paned->saved_focus);
|
||
gtk_paned_set_first_paned (focus, paned->first_paned);
|
||
|
||
gtk_paned_set_saved_focus (paned, NULL);
|
||
gtk_paned_set_first_paned (paned, NULL);
|
||
|
||
gtk_widget_grab_focus (GTK_WIDGET (focus));
|
||
|
||
if (!gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
{
|
||
paned->original_position = -1;
|
||
paned->original_position = gtk_paned_get_position (focus);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GtkPaned *focus;
|
||
GtkPaned *first;
|
||
GtkWidget *focus_child;
|
||
|
||
gtk_paned_find_neighbours (paned, &next, &prev);
|
||
focus_child = gtk_widget_get_focus_child (GTK_WIDGET (paned));
|
||
|
||
if (focus_child == paned->start_child)
|
||
{
|
||
if (reversed)
|
||
{
|
||
focus = prev;
|
||
first = paned;
|
||
}
|
||
else
|
||
{
|
||
focus = paned;
|
||
first = paned;
|
||
}
|
||
}
|
||
else if (focus_child == paned->end_child)
|
||
{
|
||
if (reversed)
|
||
{
|
||
focus = paned;
|
||
first = next;
|
||
}
|
||
else
|
||
{
|
||
focus = next;
|
||
first = next;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Focus is not inside this paned, and we don't have focus.
|
||
* Presumably this happened because the application wants us
|
||
* to start keyboard navigating.
|
||
*/
|
||
focus = paned;
|
||
|
||
if (reversed)
|
||
first = paned;
|
||
else
|
||
first = next;
|
||
}
|
||
|
||
gtk_paned_set_saved_focus (focus, gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (paned))));
|
||
gtk_paned_set_first_paned (focus, first);
|
||
paned->original_position = gtk_paned_get_position (focus);
|
||
|
||
gtk_widget_grab_focus (GTK_WIDGET (focus));
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
gtk_paned_toggle_handle_focus (GtkPaned *paned)
|
||
{
|
||
/* This function/signal has the wrong name. It is called when you
|
||
* press Tab or Shift-Tab and what we do is act as if
|
||
* the user pressed Return and then Tab or Shift-Tab
|
||
*/
|
||
if (gtk_widget_is_focus (GTK_WIDGET (paned)))
|
||
gtk_paned_accept_position (paned);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_set_wide_handle: (attributes org.gtk.Method.set_property=wide-handle)
|
||
* @paned: a `GtkPaned`
|
||
* @wide: the new value for the [property@Gtk.Paned:wide-handle] property
|
||
*
|
||
* Sets whether the separator should be wide.
|
||
*/
|
||
void
|
||
gtk_paned_set_wide_handle (GtkPaned *paned,
|
||
gboolean wide)
|
||
{
|
||
gboolean old_wide;
|
||
|
||
g_return_if_fail (GTK_IS_PANED (paned));
|
||
|
||
old_wide = gtk_paned_get_wide_handle (paned);
|
||
if (old_wide != wide)
|
||
{
|
||
if (wide)
|
||
gtk_widget_add_css_class (paned->handle_widget, "wide");
|
||
else
|
||
gtk_widget_remove_css_class (paned->handle_widget, "wide");
|
||
|
||
g_object_notify_by_pspec (G_OBJECT (paned), paned_props[PROP_WIDE_HANDLE]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_paned_get_wide_handle: (attributes org.gtk.Method.get_property=wide-handle)
|
||
* @paned: a `GtkPaned`
|
||
*
|
||
* Gets whether the separator should be wide.
|
||
*
|
||
* Returns: %TRUE if the paned should have a wide handle
|
||
*/
|
||
gboolean
|
||
gtk_paned_get_wide_handle (GtkPaned *paned)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PANED (paned), FALSE);
|
||
|
||
return gtk_widget_has_css_class (paned->handle_widget, "wide");
|
||
}
|