forked from AuroraMiddleware/gtk
Merge branch 'listview-dnd' into 'master'
Listview dnd See merge request GNOME/gtk!2116
This commit is contained in:
commit
395d40485d
@ -28,6 +28,7 @@
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkdropcontrollermotion.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistview.h"
|
||||
#include "gtkmain.h"
|
||||
@ -778,6 +779,37 @@ remove_autoscroll (GtkColumnView *self)
|
||||
}
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 30
|
||||
|
||||
static void
|
||||
update_autoscroll (GtkColumnView *self,
|
||||
double x)
|
||||
{
|
||||
double width;
|
||||
double delta;
|
||||
double vx, vy;
|
||||
|
||||
/* x is in header coordinates */
|
||||
gtk_widget_translate_coordinates (self->header, GTK_WIDGET (self), x, 0, &vx, &vy);
|
||||
width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
if (vx < SCROLL_EDGE_SIZE)
|
||||
delta = - (SCROLL_EDGE_SIZE - vx)/3.0;
|
||||
else if (width - vx < SCROLL_EDGE_SIZE)
|
||||
delta = (SCROLL_EDGE_SIZE - (width - vx))/3.0;
|
||||
else
|
||||
delta = 0;
|
||||
|
||||
if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
|
||||
delta = - delta;
|
||||
|
||||
if (delta != 0)
|
||||
add_autoscroll (self, x, delta);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
}
|
||||
|
||||
|
||||
#define DRAG_WIDTH 6
|
||||
|
||||
static gboolean
|
||||
@ -968,8 +1000,6 @@ update_column_reorder (GtkColumnView *self,
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 15
|
||||
|
||||
static void
|
||||
header_drag_update (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
@ -1018,20 +1048,7 @@ header_drag_update (GtkGestureDrag *gesture,
|
||||
update_column_reorder (self, x);
|
||||
|
||||
if (self->in_column_resize || self->in_column_reorder)
|
||||
{
|
||||
double value, page_size, upper;
|
||||
|
||||
value = gtk_adjustment_get_value (self->hadjustment);
|
||||
page_size = gtk_adjustment_get_page_size (self->hadjustment);
|
||||
upper = gtk_adjustment_get_upper (self->hadjustment);
|
||||
|
||||
if (x - value < SCROLL_EDGE_SIZE && value > 0)
|
||||
add_autoscroll (self, x, - (SCROLL_EDGE_SIZE - (x - value))/3.0);
|
||||
else if (value + page_size - x < SCROLL_EDGE_SIZE && value + page_size < upper)
|
||||
add_autoscroll (self, x, (SCROLL_EDGE_SIZE - (value + page_size - x))/3.0);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
}
|
||||
update_autoscroll (self, x);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1086,6 +1103,29 @@ header_key_pressed (GtkEventControllerKey *controller,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_drag_motion (GtkDropControllerMotion *motion,
|
||||
double x,
|
||||
double y,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (motion));
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
double hx, hy;
|
||||
|
||||
gtk_widget_translate_coordinates (widget, self->header, x, 0, &hx, &hy);
|
||||
update_autoscroll (GTK_COLUMN_VIEW (widget), hx);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_drag_leave (GtkDropControllerMotion *motion,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (motion));
|
||||
|
||||
remove_autoscroll (GTK_COLUMN_VIEW (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_init (GtkColumnView *self)
|
||||
{
|
||||
@ -1114,6 +1154,11 @@ gtk_column_view_init (GtkColumnView *self)
|
||||
g_signal_connect (controller, "key-pressed", G_CALLBACK (header_key_pressed), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
controller = gtk_drop_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (gtk_column_view_drag_motion), NULL);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (gtk_column_view_drag_leave), NULL);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->sorter = gtk_column_view_sorter_new ();
|
||||
self->factory = gtk_column_list_item_factory_new (self);
|
||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkdragsource.h"
|
||||
#include "gtkdroptarget.h"
|
||||
#include "gtkeditablelabel.h"
|
||||
#include "gtkeditable.h"
|
||||
#include "gtklabel.h"
|
||||
@ -133,10 +135,54 @@ text_changed (GtkEditableLabel *self)
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_editable_label_drag_accept (GtkDropTarget *dest,
|
||||
GdkDrop *drop,
|
||||
GtkEditableLabel *self)
|
||||
{
|
||||
if (!gtk_editable_get_editable (GTK_EDITABLE (self)))
|
||||
return FALSE;
|
||||
|
||||
if ((gdk_drop_get_actions (drop) & gtk_drop_target_get_actions (dest)) == 0)
|
||||
return FALSE;
|
||||
|
||||
return gdk_content_formats_match (gtk_drop_target_get_formats (dest), gdk_drop_get_formats (drop));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_editable_label_drag_drop (GtkDropTarget *dest,
|
||||
const GValue *value,
|
||||
double x,
|
||||
double y,
|
||||
GtkEditableLabel *self)
|
||||
{
|
||||
if (!gtk_editable_get_editable (GTK_EDITABLE (self)))
|
||||
return FALSE;
|
||||
|
||||
gtk_editable_set_text (GTK_EDITABLE (self), g_value_get_string (value));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GdkContentProvider *
|
||||
gtk_editable_label_prepare_drag (GtkDragSource *source,
|
||||
double x,
|
||||
double y,
|
||||
GtkEditableLabel *self)
|
||||
{
|
||||
if (!gtk_editable_get_editable (GTK_EDITABLE (self)))
|
||||
return NULL;
|
||||
|
||||
return gdk_content_provider_new_typed (G_TYPE_STRING,
|
||||
gtk_label_get_label (GTK_LABEL (self->label)));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_editable_label_init (GtkEditableLabel *self)
|
||||
{
|
||||
GtkGesture *gesture;
|
||||
GtkDropTarget *target;
|
||||
GtkDragSource *source;
|
||||
|
||||
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
||||
|
||||
@ -151,12 +197,21 @@ gtk_editable_label_init (GtkEditableLabel *self)
|
||||
gtk_widget_set_parent (self->stack, GTK_WIDGET (self));
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
g_signal_connect_swapped (gesture, "pressed", G_CALLBACK (clicked_cb), self);
|
||||
g_signal_connect_swapped (gesture, "released", G_CALLBACK (clicked_cb), self);
|
||||
gtk_widget_add_controller (self->label, GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
g_signal_connect_swapped (self->entry, "activate", G_CALLBACK (activate_cb), self);
|
||||
g_signal_connect_swapped (self->entry, "notify::text", G_CALLBACK (text_changed), self);
|
||||
|
||||
target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY | GDK_ACTION_MOVE);
|
||||
g_signal_connect (target, "accept", G_CALLBACK (gtk_editable_label_drag_accept), self);
|
||||
g_signal_connect (target, "drop", G_CALLBACK (gtk_editable_label_drag_drop), self);
|
||||
gtk_widget_add_controller (self->label, GTK_EVENT_CONTROLLER (target));
|
||||
|
||||
source = gtk_drag_source_new ();
|
||||
g_signal_connect (source, "prepare", G_CALLBACK (gtk_editable_label_prepare_drag), self);
|
||||
gtk_widget_add_controller (self->label, GTK_EVENT_CONTROLLER (source));
|
||||
|
||||
gtk_editable_init_delegate (GTK_EDITABLE (self));
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ expand_timeout (gpointer data)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
static void
|
||||
gtk_expander_drag_enter (GtkDropControllerMotion *motion,
|
||||
double x,
|
||||
double y,
|
||||
@ -242,8 +242,6 @@ gtk_expander_drag_enter (GtkDropControllerMotion *motion,
|
||||
expander->expand_timer = g_timeout_add (TIMEOUT_EXPAND, (GSourceFunc) expand_timeout, expander);
|
||||
g_source_set_name_by_id (expander->expand_timer, "[gtk] expand_timeout");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "gtklistbaseprivate.h"
|
||||
|
||||
#include "gtkadjustment.h"
|
||||
#include "gtkdropcontrollermotion.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkgizmoprivate.h"
|
||||
#include "gtkintl.h"
|
||||
@ -1250,23 +1251,36 @@ autoscroll_cb (GtkWidget *widget,
|
||||
GtkListBase *self = data;
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value;
|
||||
double delta_x, delta_y;
|
||||
|
||||
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], value + priv->autoscroll_delta_x);
|
||||
|
||||
delta_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]) - value;
|
||||
|
||||
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_VERTICAL], value + priv->autoscroll_delta_y);
|
||||
|
||||
delta_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]) - value;
|
||||
|
||||
if (priv->rubberband)
|
||||
{
|
||||
priv->rubberband->x2 += priv->autoscroll_delta_x;
|
||||
priv->rubberband->y2 += priv->autoscroll_delta_y;
|
||||
priv->rubberband->x2 += delta_x;
|
||||
priv->rubberband->y2 += delta_y;
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
if (delta_x != 0 || delta_y != 0)
|
||||
{
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->autoscroll_id = 0;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1295,6 +1309,43 @@ remove_autoscroll (GtkListBase *self)
|
||||
}
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 30
|
||||
|
||||
static void
|
||||
update_autoscroll (GtkListBase *self,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
double width, height;
|
||||
double delta_x, delta_y;
|
||||
|
||||
width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
if (x < SCROLL_EDGE_SIZE)
|
||||
delta_x = - (SCROLL_EDGE_SIZE - x)/3.0;
|
||||
else if (width - x < SCROLL_EDGE_SIZE)
|
||||
delta_x = (SCROLL_EDGE_SIZE - (width - x))/3.0;
|
||||
else
|
||||
delta_x = 0;
|
||||
|
||||
if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
|
||||
delta_x = - delta_x;
|
||||
|
||||
height = gtk_widget_get_height (GTK_WIDGET (self));
|
||||
|
||||
if (y < SCROLL_EDGE_SIZE)
|
||||
delta_y = - (SCROLL_EDGE_SIZE - y)/3.0;
|
||||
else if (height - y < SCROLL_EDGE_SIZE)
|
||||
delta_y = (SCROLL_EDGE_SIZE - (height - y))/3.0;
|
||||
else
|
||||
delta_y = 0;
|
||||
|
||||
if (delta_x != 0 || delta_y != 0)
|
||||
add_autoscroll (self, delta_x, delta_y);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_allocate_rubberband (GtkListBase *self)
|
||||
{
|
||||
@ -1400,52 +1451,22 @@ gtk_list_base_stop_rubberband (GtkListBase *self)
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 15
|
||||
|
||||
static void
|
||||
gtk_list_base_update_rubberband (GtkListBase *self,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value_x, value_y, page_size, upper;
|
||||
double delta_x, delta_y;
|
||||
|
||||
if (!priv->rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
priv->rubberband->x2 = x + value_x;
|
||||
priv->rubberband->y2 = y + value_y;
|
||||
priv->rubberband->x2 = x + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
priv->rubberband->y2 = y + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
|
||||
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
|
||||
if (x < SCROLL_EDGE_SIZE && value_x > 0)
|
||||
delta_x = - (SCROLL_EDGE_SIZE - x)/3.0;
|
||||
else if (page_size - x < SCROLL_EDGE_SIZE && value_x + page_size < upper)
|
||||
delta_x = (SCROLL_EDGE_SIZE - (page_size - x))/3.0;
|
||||
else
|
||||
delta_x = 0;
|
||||
|
||||
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
if (y < SCROLL_EDGE_SIZE && value_y > 0)
|
||||
delta_y = - (SCROLL_EDGE_SIZE - y)/3.0;
|
||||
else if (page_size - y < SCROLL_EDGE_SIZE && value_y + page_size < upper)
|
||||
delta_y = (SCROLL_EDGE_SIZE - (page_size - y))/3.0;
|
||||
else
|
||||
delta_y = 0;
|
||||
|
||||
if (delta_x != 0 || delta_y != 0)
|
||||
add_autoscroll (self, delta_x, delta_y);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
update_autoscroll (self, x, y);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
@ -1576,11 +1597,32 @@ gtk_list_base_get_enable_rubberband (GtkListBase *self)
|
||||
return priv->enable_rubberband;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_motion (GtkDropControllerMotion *motion,
|
||||
double x,
|
||||
double y,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (motion));
|
||||
|
||||
update_autoscroll (GTK_LIST_BASE (widget), x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_leave (GtkDropControllerMotion *motion,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (motion));
|
||||
|
||||
remove_autoscroll (GTK_LIST_BASE (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_init_real (GtkListBase *self,
|
||||
GtkListBaseClass *g_class)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkEventController *controller;
|
||||
|
||||
priv->item_manager = gtk_list_item_manager_new_for_size (GTK_WIDGET (self),
|
||||
g_class->list_item_name,
|
||||
@ -1600,6 +1642,11 @@ gtk_list_base_init_real (GtkListBase *self,
|
||||
|
||||
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
|
||||
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
||||
|
||||
controller = gtk_drop_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (gtk_list_base_drag_motion), NULL);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (gtk_list_base_drag_leave), NULL);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkbuiltiniconprivate.h"
|
||||
#include "gtkdropcontrollermotion.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtktreelistmodel.h"
|
||||
@ -75,6 +76,8 @@ struct _GtkTreeExpander
|
||||
|
||||
GtkWidget *expander;
|
||||
guint notify_handler;
|
||||
|
||||
guint expand_timer;
|
||||
};
|
||||
|
||||
enum
|
||||
@ -295,6 +298,12 @@ gtk_tree_expander_dispose (GObject *object)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (object);
|
||||
|
||||
if (self->expand_timer)
|
||||
{
|
||||
g_source_remove (self->expand_timer);
|
||||
self->expand_timer = 0;
|
||||
}
|
||||
|
||||
gtk_tree_expander_clear_list_row (self);
|
||||
gtk_tree_expander_update_for_list_row (self);
|
||||
|
||||
@ -557,10 +566,60 @@ gtk_tree_expander_class_init (GtkTreeExpanderClass *klass)
|
||||
gtk_widget_class_set_css_name (widget_class, I_("treeexpander"));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tree_expander_expand_timeout (gpointer data)
|
||||
{
|
||||
GtkTreeExpander *self = GTK_TREE_EXPANDER (data);
|
||||
|
||||
if (self->list_row != NULL)
|
||||
gtk_tree_list_row_set_expanded (self->list_row, TRUE);
|
||||
|
||||
self->expand_timer = 0;
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
#define TIMEOUT_EXPAND 500
|
||||
|
||||
static void
|
||||
gtk_tree_expander_drag_enter (GtkDropControllerMotion *motion,
|
||||
double x,
|
||||
double y,
|
||||
GtkTreeExpander *self)
|
||||
{
|
||||
if (self->list_row == NULL)
|
||||
return;
|
||||
|
||||
if (!gtk_tree_list_row_get_expanded (self->list_row) &&
|
||||
!self->expand_timer)
|
||||
{
|
||||
self->expand_timer = g_timeout_add (TIMEOUT_EXPAND, (GSourceFunc) gtk_tree_expander_expand_timeout, self);
|
||||
g_source_set_name_by_id (self->expand_timer, "[gtk] gtk_tree_expander_expand_timeout");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_drag_leave (GtkDropControllerMotion *motion,
|
||||
GtkTreeExpander *self)
|
||||
{
|
||||
if (self->expand_timer)
|
||||
{
|
||||
g_source_remove (self->expand_timer);
|
||||
self->expand_timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_expander_init (GtkTreeExpander *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
|
||||
|
||||
controller = gtk_drop_controller_motion_new ();
|
||||
g_signal_connect (controller, "enter", G_CALLBACK (gtk_tree_expander_drag_enter), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (gtk_tree_expander_drag_leave), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,7 @@ gtk_tests = [
|
||||
['testlist2'],
|
||||
['testlist3'],
|
||||
['testlist4'],
|
||||
['testlistdnd'],
|
||||
['testlistview'],
|
||||
['testlistview-animating'],
|
||||
['testlevelbar'],
|
||||
|
441
tests/testlistdnd.c
Normal file
441
tests/testlistdnd.c
Normal file
@ -0,0 +1,441 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (TestObject, test_object, TEST, OBJECT, GObject)
|
||||
|
||||
struct _TestObject {
|
||||
GObject parent_instance;
|
||||
char *string;
|
||||
guint number;
|
||||
gboolean allow_children;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_STRING = 1,
|
||||
PROP_NUMBER,
|
||||
PROP_ALLOW_CHILDREN,
|
||||
PROP_NUM_PROPERTIES
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
test_object_init (TestObject *obj)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_finalize (GObject *object)
|
||||
{
|
||||
TestObject *obj = TEST_OBJECT (object);
|
||||
|
||||
g_free (obj->string);
|
||||
|
||||
G_OBJECT_CLASS (test_object_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
TestObject *obj = TEST_OBJECT (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_STRING:
|
||||
g_free (obj->string);
|
||||
obj->string = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_NUMBER:
|
||||
obj->number = g_value_get_uint (value);
|
||||
break;
|
||||
|
||||
case PROP_ALLOW_CHILDREN:
|
||||
obj->allow_children = g_value_get_boolean (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
TestObject *obj = TEST_OBJECT (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_STRING:
|
||||
g_value_set_string (value, obj->string);
|
||||
break;
|
||||
|
||||
case PROP_NUMBER:
|
||||
g_value_set_uint (value, obj->number);
|
||||
break;
|
||||
|
||||
case PROP_ALLOW_CHILDREN:
|
||||
g_value_set_boolean (value, obj->allow_children);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_class_init (TestObjectClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = test_object_finalize;
|
||||
object_class->set_property = test_object_set_property;
|
||||
object_class->get_property = test_object_get_property;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_STRING,
|
||||
g_param_spec_string ("string", "String", "String",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_NUMBER,
|
||||
g_param_spec_uint ("number", "Number", "Number",
|
||||
0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_ALLOW_CHILDREN,
|
||||
g_param_spec_boolean ("allow-children", "Allow children", "Allow children",
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static TestObject *
|
||||
test_object_new (const char *string,
|
||||
guint number,
|
||||
gboolean allow_children)
|
||||
{
|
||||
return g_object_new (TEST_TYPE_OBJECT,
|
||||
"string", string,
|
||||
"number", number,
|
||||
"allow-children", allow_children,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static const char *
|
||||
test_object_get_string (TestObject *obj)
|
||||
{
|
||||
return obj->string;
|
||||
}
|
||||
|
||||
static guint
|
||||
test_object_get_number (TestObject *obj)
|
||||
{
|
||||
return obj->number;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_object_get_allow_children (TestObject *obj)
|
||||
{
|
||||
return obj->allow_children;
|
||||
}
|
||||
|
||||
/* * * */
|
||||
|
||||
static GListModel *
|
||||
create_model (guint base,
|
||||
guint n,
|
||||
guint increment,
|
||||
gboolean allow_children)
|
||||
{
|
||||
GListStore *store;
|
||||
guint i;
|
||||
|
||||
store = g_list_store_new (TEST_TYPE_OBJECT);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
char *string;
|
||||
guint number;
|
||||
TestObject *obj;
|
||||
|
||||
number = base + i * increment;
|
||||
string = g_strdup_printf ("%u", number);
|
||||
obj = test_object_new (string, number, allow_children);
|
||||
g_list_store_append (store, obj);
|
||||
g_object_unref (obj);
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_child_model (gpointer item,
|
||||
gpointer user_data)
|
||||
{
|
||||
guint size = GPOINTER_TO_UINT (user_data);
|
||||
guint base = test_object_get_number (TEST_OBJECT (item));
|
||||
|
||||
if (test_object_get_allow_children (TEST_OBJECT (item)))
|
||||
return create_model (base, size, 1, FALSE);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_tree_model (guint n, guint m)
|
||||
{
|
||||
return G_LIST_MODEL (gtk_tree_list_model_new (FALSE,
|
||||
create_model (0, n, m, TRUE),
|
||||
FALSE,
|
||||
create_child_model,
|
||||
GUINT_TO_POINTER (m), NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_item (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *item)
|
||||
{
|
||||
GtkWidget *entry;
|
||||
|
||||
entry = gtk_editable_label_new ("");
|
||||
gtk_editable_set_width_chars (GTK_EDITABLE (entry), 3);
|
||||
gtk_list_item_set_child (item, entry);
|
||||
}
|
||||
|
||||
static void
|
||||
text_changed (GObject *object,
|
||||
GParamSpec *pspec,
|
||||
gpointer data)
|
||||
{
|
||||
const char *text;
|
||||
|
||||
text = gtk_editable_get_text (GTK_EDITABLE (object));
|
||||
g_print ("text changed to '%s'\n", text);
|
||||
g_object_set (data, "string", text, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_item (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *item)
|
||||
{
|
||||
TestObject *obj;
|
||||
GtkWidget *entry;
|
||||
|
||||
obj = gtk_list_item_get_item (item);
|
||||
entry = gtk_list_item_get_child (item);
|
||||
gtk_editable_set_text (GTK_EDITABLE (entry), test_object_get_string (obj));
|
||||
g_signal_connect (entry, "notify::text", G_CALLBACK (text_changed), obj);
|
||||
}
|
||||
|
||||
static void
|
||||
unbind_item (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *item)
|
||||
{
|
||||
TestObject *obj;
|
||||
GtkWidget *entry;
|
||||
|
||||
obj = gtk_list_item_get_item (item);
|
||||
entry = gtk_list_item_get_child (item);
|
||||
g_signal_handlers_disconnect_by_func (entry, text_changed, obj);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_tree_item (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *item)
|
||||
{
|
||||
GtkWidget *expander;
|
||||
GtkWidget *entry;
|
||||
|
||||
entry = gtk_editable_label_new ("");
|
||||
gtk_editable_set_width_chars (GTK_EDITABLE (entry), 3);
|
||||
expander = gtk_tree_expander_new ();
|
||||
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), entry);
|
||||
gtk_list_item_set_child (item, expander);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_tree_item (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *item)
|
||||
{
|
||||
GtkTreeListRow *row;
|
||||
GtkTreeExpander *expander;
|
||||
TestObject *obj;
|
||||
GtkWidget *entry;
|
||||
|
||||
row = gtk_list_item_get_item (item);
|
||||
expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item));
|
||||
gtk_tree_expander_set_list_row (expander, row);
|
||||
obj = gtk_tree_list_row_get_item (row);
|
||||
entry = gtk_tree_expander_get_child (expander);
|
||||
gtk_editable_set_text (GTK_EDITABLE (entry), test_object_get_string (obj));
|
||||
|
||||
g_signal_connect (entry, "notify::text", G_CALLBACK (text_changed), obj);
|
||||
}
|
||||
|
||||
static void
|
||||
unbind_tree_item (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *item)
|
||||
{
|
||||
GtkTreeListRow *row;
|
||||
GtkTreeExpander *expander;
|
||||
TestObject *obj;
|
||||
GtkWidget *entry;
|
||||
|
||||
row = gtk_list_item_get_item (item);
|
||||
expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item));
|
||||
obj = gtk_tree_list_row_get_item (row);
|
||||
entry = gtk_tree_expander_get_child (expander);
|
||||
g_signal_handlers_disconnect_by_func (entry, text_changed, obj);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
GtkWidget *box2;
|
||||
GtkWidget *stack;
|
||||
GtkWidget *switcher;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *list;
|
||||
GtkWidget *cv;
|
||||
GListModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
||||
|
||||
box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
||||
gtk_window_set_child (GTK_WINDOW (window), box2);
|
||||
|
||||
switcher = gtk_stack_switcher_new ();
|
||||
gtk_widget_set_halign (GTK_WIDGET (switcher), GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_top (GTK_WIDGET (switcher), 10);
|
||||
gtk_widget_set_margin_bottom (GTK_WIDGET (switcher), 10);
|
||||
gtk_box_append (GTK_BOX (box2), switcher);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
|
||||
gtk_box_append (GTK_BOX (box2), box);
|
||||
|
||||
label = gtk_editable_label_new ("Drag me");
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
stack = gtk_stack_new ();
|
||||
gtk_widget_set_vexpand (stack, TRUE);
|
||||
gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack));
|
||||
gtk_box_append (GTK_BOX (box), stack);
|
||||
|
||||
/* grid */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_stack_add_titled (GTK_STACK (stack), sw, "grid", "GtkGridView");
|
||||
|
||||
grid = gtk_grid_view_new ();
|
||||
gtk_grid_view_set_min_columns (GTK_GRID_VIEW (grid), 20);
|
||||
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (grid), 20);
|
||||
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), grid);
|
||||
|
||||
model = create_model (0, 400, 1, FALSE);
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (grid), model);
|
||||
g_object_unref (model);
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), NULL);
|
||||
|
||||
gtk_grid_view_set_factory (GTK_GRID_VIEW (grid), factory);
|
||||
g_object_unref (factory);
|
||||
|
||||
/* list */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_stack_add_titled (GTK_STACK (stack), sw, "list", "GtkListView");
|
||||
|
||||
list = gtk_list_view_new ();
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
|
||||
|
||||
model = create_model (0, 400, 1, FALSE);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (list), model);
|
||||
g_object_unref (model);
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), NULL);
|
||||
|
||||
gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory);
|
||||
g_object_unref (factory);
|
||||
|
||||
/* columnview */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_stack_add_titled (GTK_STACK (stack), sw, "column", "GtkColumnView");
|
||||
|
||||
cv = gtk_column_view_new ();
|
||||
|
||||
model = create_model (0, 400, 1, FALSE);
|
||||
gtk_column_view_set_model (GTK_COLUMN_VIEW (cv), model);
|
||||
g_object_unref (model);
|
||||
|
||||
for (guint i = 0; i < 20; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
char *title;
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), NULL);
|
||||
|
||||
title = g_strdup_printf ("Column %u", i);
|
||||
column = gtk_column_view_column_new_with_factory (title, factory);
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
|
||||
g_object_unref (column);
|
||||
g_free (title);
|
||||
}
|
||||
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), cv);
|
||||
|
||||
/* tree */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_stack_add_titled (GTK_STACK (stack), sw, "tree", "Tree");
|
||||
|
||||
list = gtk_list_view_new ();
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
|
||||
|
||||
model = create_tree_model (20, 20);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (list), model);
|
||||
g_object_unref (model);
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_tree_item), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_tree_item), NULL);
|
||||
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_tree_item), NULL);
|
||||
|
||||
gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory);
|
||||
g_object_unref (factory);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user