mirror of
https://gitlab.gnome.org/GNOME/gitg
synced 2024-10-31 04:58:58 +00:00
flow-box: add basic keynav
This commit is contained in:
parent
93fea12498
commit
d9f158678d
3 changed files with 658 additions and 8 deletions
658
egg-flow-box.c
658
egg-flow-box.c
|
@ -42,6 +42,33 @@
|
|||
|
||||
#include <gtk/gtk.h>
|
||||
#include "egg-flow-box.h"
|
||||
/* This already exists in gtk as _gtk_marshal_VOID__ENUM_INT, inline it here for now
|
||||
to avoid separate marshallers file */
|
||||
static void
|
||||
_egg_marshal_VOID__ENUM_INT (GClosure * closure,
|
||||
GValue * return_value,
|
||||
guint n_param_values,
|
||||
const GValue * param_values,
|
||||
gpointer invocation_hint,
|
||||
gpointer marshal_data)
|
||||
{
|
||||
typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1, gint arg_1, gint arg_2, gpointer data2);
|
||||
register GMarshalFunc_VOID__ENUM_INT callback;
|
||||
register GCClosure * cc;
|
||||
register gpointer data1;
|
||||
register gpointer data2;
|
||||
cc = (GCClosure *) closure;
|
||||
g_return_if_fail (n_param_values == 3);
|
||||
if (G_CCLOSURE_SWAP_DATA (closure)) {
|
||||
data1 = closure->data;
|
||||
data2 = param_values->data[0].v_pointer;
|
||||
} else {
|
||||
data1 = param_values->data[0].v_pointer;
|
||||
data2 = closure->data;
|
||||
}
|
||||
callback = (GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->callback);
|
||||
callback (data1, g_value_get_enum (param_values + 1), g_value_get_int (param_values + 2), data2);
|
||||
}
|
||||
|
||||
#define P_(msgid) (msgid)
|
||||
|
||||
|
@ -50,6 +77,9 @@
|
|||
enum {
|
||||
CHILD_ACTIVATED,
|
||||
SELECTED_CHILDREN_CHANGED,
|
||||
ACTIVATE_CURSOR_CHILD,
|
||||
TOGGLE_CURSOR_CHILD,
|
||||
MOVE_CURSOR,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
|
@ -76,6 +106,7 @@ struct _EggFlowBoxPrivate {
|
|||
guint homogeneous : 1;
|
||||
guint activate_on_single_click : 1;
|
||||
GtkSelectionMode selection_mode;
|
||||
GtkAdjustment *adjustment;
|
||||
|
||||
guint row_spacing;
|
||||
guint column_spacing;
|
||||
|
@ -83,9 +114,12 @@ struct _EggFlowBoxPrivate {
|
|||
gboolean active_child_active;
|
||||
EggFlowBoxChildInfo *active_child;
|
||||
EggFlowBoxChildInfo *prelight_child;
|
||||
EggFlowBoxChildInfo *cursor_child;
|
||||
EggFlowBoxChildInfo *selected_child;
|
||||
|
||||
guint16 min_children_per_line;
|
||||
guint16 max_children_per_line;
|
||||
guint16 cur_children_per_line;
|
||||
|
||||
GSequence *children;
|
||||
GHashTable *child_hash;
|
||||
|
@ -140,6 +174,21 @@ egg_flow_box_lookup_info (EggFlowBox *flow_box, GtkWidget* child)
|
|||
return g_hash_table_lookup (priv->child_hash, child);
|
||||
}
|
||||
|
||||
void
|
||||
egg_flow_box_set_adjustment (EggFlowBox *box,
|
||||
GtkAdjustment *adjustment)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
|
||||
g_return_if_fail (box != NULL);
|
||||
|
||||
g_object_ref (adjustment);
|
||||
g_clear_object (&priv->adjustment);
|
||||
priv->adjustment = adjustment;
|
||||
gtk_container_set_focus_vadjustment (GTK_CONTAINER (box),
|
||||
adjustment);
|
||||
}
|
||||
|
||||
/**
|
||||
* egg_flow_box_get_homogeneous:
|
||||
* @box: a #EggFlowBox
|
||||
|
@ -185,6 +234,14 @@ egg_flow_box_set_homogeneous (EggFlowBox *box,
|
|||
}
|
||||
}
|
||||
|
||||
/* Children are visible if they are shown by the app (visible)
|
||||
and not filtered out (child_visible) by the box */
|
||||
static gboolean
|
||||
child_is_visible (GtkWidget *child)
|
||||
{
|
||||
return gtk_widget_get_visible (child) && gtk_widget_get_child_visible (child);
|
||||
}
|
||||
|
||||
static gint
|
||||
get_visible_children (EggFlowBox *box)
|
||||
{
|
||||
|
@ -202,7 +259,7 @@ get_visible_children (EggFlowBox *box)
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
|
@ -236,7 +293,7 @@ get_average_item_size (EggFlowBox *box,
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
continue;
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
|
@ -281,7 +338,7 @@ get_largest_size_for_opposing_orientation (EggFlowBox *box,
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
continue;
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
|
@ -335,7 +392,7 @@ get_largest_size_for_line_in_opposing_orientation (EggFlowBox *box,
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
continue;
|
||||
|
||||
/* Distribute the extra pixels to the first children in the line
|
||||
|
@ -402,7 +459,7 @@ gather_aligned_item_requests (EggFlowBox *box,
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
continue;
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
|
@ -715,6 +772,8 @@ egg_flow_box_real_size_allocate (GtkWidget *widget,
|
|||
* go on to distribute expand space if needed.
|
||||
*/
|
||||
|
||||
priv->cur_children_per_line = line_length;
|
||||
|
||||
/* FIXME: This portion needs to consider which columns
|
||||
* and rows asked for expand space and distribute those
|
||||
* accordingly for the case of ALIGNED allocation.
|
||||
|
@ -777,7 +836,7 @@ egg_flow_box_real_size_allocate (GtkWidget *widget,
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
{
|
||||
child_info->area.x = child_allocation.x;
|
||||
child_info->area.y = child_allocation.y;
|
||||
|
@ -933,7 +992,7 @@ egg_flow_box_real_remove (GtkContainer *container,
|
|||
|
||||
g_return_if_fail (child != NULL);
|
||||
|
||||
was_visible = gtk_widget_get_visible (child);
|
||||
was_visible = child_is_visible (child);
|
||||
|
||||
child_info = egg_flow_box_lookup_info (box, child);
|
||||
if (child_info == NULL)
|
||||
|
@ -948,6 +1007,8 @@ egg_flow_box_real_remove (GtkContainer *container,
|
|||
priv->prelight_child = NULL;
|
||||
if (child_info == priv->active_child)
|
||||
priv->active_child = NULL;
|
||||
if (child_info == priv->selected_child)
|
||||
priv->selected_child = NULL;
|
||||
|
||||
gtk_widget_unparent (child);
|
||||
g_hash_table_remove (priv->child_hash, child);
|
||||
|
@ -1033,7 +1094,7 @@ get_largest_aligned_line_length (EggFlowBox *box,
|
|||
child_info = g_sequence_get (iter);
|
||||
child = child_info->widget;
|
||||
|
||||
if (!gtk_widget_get_visible (child))
|
||||
if (!child_is_visible (child))
|
||||
continue;
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
|
@ -2159,6 +2220,46 @@ egg_flow_box_unselect_all_internal (EggFlowBox *box)
|
|||
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_unselect_child_info (EggFlowBox *box,
|
||||
EggFlowBoxChildInfo *child_info)
|
||||
{
|
||||
if (!child_info->selected)
|
||||
return;
|
||||
|
||||
if (box->priv->selection_mode == GTK_SELECTION_NONE)
|
||||
return;
|
||||
else if (box->priv->selection_mode != GTK_SELECTION_MULTIPLE)
|
||||
egg_flow_box_unselect_all_internal (box);
|
||||
else
|
||||
child_info->selected = TRUE;
|
||||
|
||||
g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
|
||||
|
||||
egg_flow_box_queue_draw_child (box, child_info);
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_update_cursor (EggFlowBox *box,
|
||||
EggFlowBoxChildInfo *child_info)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
|
||||
priv->cursor_child = child_info;
|
||||
gtk_widget_grab_focus (GTK_WIDGET (box));
|
||||
gtk_widget_queue_draw (GTK_WIDGET (box));
|
||||
|
||||
if (child_info != NULL && priv->adjustment != NULL)
|
||||
{
|
||||
GtkAllocation allocation;
|
||||
|
||||
gtk_widget_get_allocation (GTK_WIDGET (box), &allocation);
|
||||
gtk_adjustment_clamp_page (priv->adjustment,
|
||||
priv->cursor_child->area.y + allocation.y,
|
||||
priv->cursor_child->area.y + allocation.y + priv->cursor_child->area.height);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_select_child_info (EggFlowBox *box,
|
||||
EggFlowBoxChildInfo *child_info)
|
||||
|
@ -2172,10 +2273,13 @@ egg_flow_box_select_child_info (EggFlowBox *box,
|
|||
egg_flow_box_unselect_all_internal (box);
|
||||
|
||||
child_info->selected = TRUE;
|
||||
box->priv->selected_child = child_info;
|
||||
|
||||
g_signal_emit (box, signals[SELECTED_CHILDREN_CHANGED], 0);
|
||||
|
||||
egg_flow_box_queue_draw_child (box, child_info);
|
||||
|
||||
egg_flow_box_update_cursor (box, child_info);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2218,6 +2322,285 @@ egg_flow_box_real_button_release_event (GtkWidget *widget,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static EggFlowBoxChildInfo *
|
||||
egg_flow_box_get_first_visible (EggFlowBox *box)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
EggFlowBoxChildInfo *child_info;
|
||||
GSequenceIter *iter;
|
||||
|
||||
for (iter = g_sequence_get_begin_iter (priv->children);
|
||||
!g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
child_info = g_sequence_get (iter);
|
||||
if (child_is_visible (child_info->widget))
|
||||
return child_info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static EggFlowBoxChildInfo *
|
||||
egg_flow_box_get_last_visible (EggFlowBox *box)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
EggFlowBoxChildInfo *child_info;
|
||||
GSequenceIter *iter;
|
||||
|
||||
iter = g_sequence_get_end_iter (priv->children);
|
||||
while (!g_sequence_iter_is_begin (iter))
|
||||
{
|
||||
iter = g_sequence_iter_prev (iter);
|
||||
child_info = g_sequence_get (iter);
|
||||
if (child_is_visible (child_info->widget))
|
||||
return child_info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GSequenceIter *
|
||||
egg_flow_box_get_previous_visible (EggFlowBox *box,
|
||||
GSequenceIter *iter)
|
||||
{
|
||||
EggFlowBoxChildInfo *child_info;
|
||||
|
||||
if (g_sequence_iter_is_begin (iter))
|
||||
return NULL;
|
||||
|
||||
do
|
||||
{
|
||||
iter = g_sequence_iter_prev (iter);
|
||||
child_info = g_sequence_get (iter);
|
||||
if (child_is_visible (child_info->widget))
|
||||
return iter;
|
||||
}
|
||||
while (!g_sequence_iter_is_begin (iter));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GSequenceIter *
|
||||
egg_flow_box_get_next_visible (EggFlowBox *box,
|
||||
GSequenceIter *iter)
|
||||
{
|
||||
EggFlowBoxChildInfo *child_info;
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return iter;
|
||||
|
||||
do
|
||||
{
|
||||
iter = g_sequence_iter_next (iter);
|
||||
if (!g_sequence_iter_is_end (iter))
|
||||
{
|
||||
child_info = g_sequence_get (iter);
|
||||
if (child_is_visible (child_info->widget))
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
while (!g_sequence_iter_is_end (iter));
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
static GSequenceIter *
|
||||
egg_flow_box_get_above_visible (EggFlowBox *box,
|
||||
GSequenceIter *iter)
|
||||
{
|
||||
EggFlowBoxChildInfo *child_info;
|
||||
GSequenceIter *ret = NULL;
|
||||
gint i;
|
||||
|
||||
if (g_sequence_iter_is_begin (iter))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
iter = g_sequence_iter_prev (iter);
|
||||
child_info = g_sequence_get (iter);
|
||||
if (child_is_visible (child_info->widget))
|
||||
i++;
|
||||
}
|
||||
while (!g_sequence_iter_is_begin (iter)
|
||||
&& i < box->priv->cur_children_per_line);
|
||||
|
||||
if (i == box->priv->cur_children_per_line)
|
||||
ret = iter;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GSequenceIter *
|
||||
egg_flow_box_get_below_visible (EggFlowBox *box,
|
||||
GSequenceIter *iter)
|
||||
{
|
||||
EggFlowBoxChildInfo *child_info;
|
||||
GSequenceIter *ret = NULL;
|
||||
gint i;
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return iter;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
iter = g_sequence_iter_next (iter);
|
||||
if (!g_sequence_iter_is_end (iter))
|
||||
{
|
||||
child_info = g_sequence_get (iter);
|
||||
if (child_is_visible (child_info->widget))
|
||||
i++;
|
||||
}
|
||||
}
|
||||
while (!g_sequence_iter_is_end (iter)
|
||||
&& i < box->priv->cur_children_per_line);
|
||||
|
||||
if (i == box->priv->cur_children_per_line)
|
||||
ret = iter;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
egg_flow_box_real_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
EggFlowBox *box = EGG_FLOW_BOX (widget);
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
gboolean had_focus = FALSE;
|
||||
GtkWidget *recurse_into;
|
||||
EggFlowBoxChildInfo *current_focus_child;
|
||||
EggFlowBoxChildInfo *next_focus_child;
|
||||
gboolean modify_selection_pressed;
|
||||
GdkModifierType state = 0;
|
||||
|
||||
recurse_into = NULL;
|
||||
|
||||
g_object_get (GTK_WIDGET (box), "has-focus", &had_focus, NULL);
|
||||
current_focus_child = NULL;
|
||||
next_focus_child = NULL;
|
||||
|
||||
if (had_focus)
|
||||
{
|
||||
/* If on row, going right, enter into possible container */
|
||||
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
|
||||
{
|
||||
if (priv->cursor_child != NULL)
|
||||
recurse_into = priv->cursor_child->widget;
|
||||
}
|
||||
current_focus_child = priv->cursor_child;
|
||||
}
|
||||
else if (gtk_container_get_focus_child ((GtkContainer *) box) != NULL)
|
||||
{
|
||||
/* There is a focus child, always navigate inside it first */
|
||||
recurse_into = gtk_container_get_focus_child ((GtkContainer *) box);
|
||||
current_focus_child = egg_flow_box_lookup_info (box, recurse_into);
|
||||
|
||||
/* If exiting child container to the left, select row or out */
|
||||
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
|
||||
next_focus_child = current_focus_child;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If coming from the left, enter into possible container */
|
||||
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
|
||||
{
|
||||
if (priv->selected_child != NULL)
|
||||
recurse_into = priv->selected_child->widget;
|
||||
}
|
||||
}
|
||||
|
||||
if (recurse_into != NULL)
|
||||
{
|
||||
if (gtk_widget_child_focus (recurse_into, direction))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (next_focus_child == NULL)
|
||||
{
|
||||
if (current_focus_child != NULL)
|
||||
{
|
||||
GSequenceIter *i;
|
||||
|
||||
if (direction == GTK_DIR_LEFT)
|
||||
{
|
||||
i = egg_flow_box_get_previous_visible (box, current_focus_child->iter);
|
||||
if (i != NULL)
|
||||
next_focus_child = g_sequence_get (i);
|
||||
}
|
||||
else if (direction == GTK_DIR_RIGHT)
|
||||
{
|
||||
i = egg_flow_box_get_next_visible (box, current_focus_child->iter);
|
||||
if (i != NULL && !g_sequence_iter_is_end (i))
|
||||
next_focus_child = g_sequence_get (i);
|
||||
}
|
||||
else if (direction == GTK_DIR_UP)
|
||||
{
|
||||
i = egg_flow_box_get_above_visible (box, current_focus_child->iter);
|
||||
if (i != NULL && !g_sequence_iter_is_end (i))
|
||||
next_focus_child = g_sequence_get (i);
|
||||
}
|
||||
else if (direction == GTK_DIR_DOWN)
|
||||
{
|
||||
i = egg_flow_box_get_below_visible (box, current_focus_child->iter);
|
||||
if (i != NULL && !g_sequence_iter_is_end (i))
|
||||
next_focus_child = g_sequence_get (i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case GTK_DIR_UP:
|
||||
case GTK_DIR_TAB_BACKWARD:
|
||||
next_focus_child = priv->selected_child;
|
||||
if (next_focus_child == NULL)
|
||||
next_focus_child = egg_flow_box_get_last_visible (box);
|
||||
break;
|
||||
default:
|
||||
next_focus_child = priv->selected_child;
|
||||
if (next_focus_child == NULL)
|
||||
next_focus_child =
|
||||
egg_flow_box_get_first_visible (box);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next_focus_child == NULL)
|
||||
{
|
||||
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN
|
||||
|| direction == GTK_DIR_LEFT || direction == GTK_DIR_RIGHT)
|
||||
{
|
||||
if (gtk_widget_keynav_failed (GTK_WIDGET (box), direction))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
modify_selection_pressed = FALSE;
|
||||
if (gtk_get_current_event_state (&state))
|
||||
{
|
||||
GdkModifierType modify_mod_mask;
|
||||
|
||||
modify_mod_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (box),
|
||||
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
||||
if ((state & modify_mod_mask) == modify_mod_mask)
|
||||
modify_selection_pressed = TRUE;
|
||||
}
|
||||
|
||||
if (modify_selection_pressed)
|
||||
egg_flow_box_update_cursor (box, next_focus_child);
|
||||
else
|
||||
egg_flow_box_select_child_info (box, next_focus_child);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
EggFlowBoxChildInfo *child;
|
||||
GtkStateFlags state;
|
||||
|
@ -2242,6 +2625,204 @@ child_flags_find_or_add (ChildFlags *array,
|
|||
return &array[*array_length - 1];
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_add_move_binding (GtkBindingSet *binding_set,
|
||||
guint keyval,
|
||||
GdkModifierType modmask,
|
||||
GtkMovementStep step,
|
||||
gint count)
|
||||
{
|
||||
gtk_binding_entry_add_signal (binding_set,
|
||||
keyval,
|
||||
modmask,
|
||||
"move-cursor",
|
||||
(guint) 2,
|
||||
GTK_TYPE_MOVEMENT_STEP,
|
||||
step,
|
||||
G_TYPE_INT,
|
||||
count,
|
||||
NULL);
|
||||
|
||||
if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
|
||||
return;
|
||||
|
||||
gtk_binding_entry_add_signal (binding_set,
|
||||
keyval,
|
||||
GDK_CONTROL_MASK,
|
||||
"move-cursor",
|
||||
(guint) 2,
|
||||
GTK_TYPE_MOVEMENT_STEP,
|
||||
step,
|
||||
G_TYPE_INT,
|
||||
count,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_real_activate_cursor_child (EggFlowBox *box)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
|
||||
egg_flow_box_select_and_activate (box, priv->cursor_child);
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_real_toggle_cursor_child (EggFlowBox *box)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
|
||||
if (priv->cursor_child == NULL)
|
||||
return;
|
||||
|
||||
if (priv->selection_mode == GTK_SELECTION_SINGLE &&
|
||||
priv->cursor_child->selected)
|
||||
egg_flow_box_unselect_child_info (box, priv->cursor_child);
|
||||
else
|
||||
egg_flow_box_select_and_activate (box, priv->cursor_child);
|
||||
}
|
||||
|
||||
static void
|
||||
egg_flow_box_real_move_cursor (EggFlowBox *box,
|
||||
GtkMovementStep step,
|
||||
gint count)
|
||||
{
|
||||
EggFlowBoxPrivate *priv = box->priv;
|
||||
GdkModifierType state;
|
||||
gboolean modify_selection_pressed;
|
||||
EggFlowBoxChildInfo *child;
|
||||
GdkModifierType modify_mod_mask;
|
||||
EggFlowBoxChildInfo *prev;
|
||||
EggFlowBoxChildInfo *next;
|
||||
gint page_size;
|
||||
GSequenceIter *iter;
|
||||
gint start_y;
|
||||
gint end_y;
|
||||
|
||||
modify_selection_pressed = FALSE;
|
||||
|
||||
if (gtk_get_current_event_state (&state))
|
||||
{
|
||||
modify_mod_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (box),
|
||||
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
||||
if ((state & modify_mod_mask) == modify_mod_mask)
|
||||
modify_selection_pressed = TRUE;
|
||||
}
|
||||
|
||||
child = NULL;
|
||||
switch (step)
|
||||
{
|
||||
case GTK_MOVEMENT_BUFFER_ENDS:
|
||||
if (count < 0)
|
||||
child = egg_flow_box_get_first_visible (box);
|
||||
else
|
||||
child = egg_flow_box_get_last_visible (box);
|
||||
break;
|
||||
case GTK_MOVEMENT_DISPLAY_LINES:
|
||||
if (priv->cursor_child != NULL)
|
||||
{
|
||||
iter = priv->cursor_child->iter;
|
||||
|
||||
while (count < 0 && iter != NULL)
|
||||
{
|
||||
iter = egg_flow_box_get_previous_visible (box, iter);
|
||||
count = count + 1;
|
||||
}
|
||||
while (count > 0 && iter != NULL)
|
||||
{
|
||||
iter = egg_flow_box_get_next_visible (box, iter);
|
||||
count = count - 1;
|
||||
}
|
||||
|
||||
if (iter != NULL && !g_sequence_iter_is_end (iter))
|
||||
child = g_sequence_get (iter);
|
||||
}
|
||||
break;
|
||||
case GTK_MOVEMENT_PAGES:
|
||||
page_size = 100;
|
||||
if (priv->adjustment != NULL)
|
||||
page_size = gtk_adjustment_get_page_increment (priv->adjustment);
|
||||
|
||||
if (priv->cursor_child != NULL)
|
||||
{
|
||||
start_y = priv->cursor_child->area.y;
|
||||
end_y = start_y;
|
||||
iter = priv->cursor_child->iter;
|
||||
|
||||
child = priv->cursor_child;
|
||||
if (count < 0)
|
||||
{
|
||||
gint i = 0;
|
||||
|
||||
/* Up */
|
||||
while (iter != NULL && !g_sequence_iter_is_begin (iter))
|
||||
{
|
||||
iter = egg_flow_box_get_previous_visible (box, iter);
|
||||
if (iter == NULL)
|
||||
break;
|
||||
|
||||
prev = g_sequence_get (iter);
|
||||
|
||||
/* go up an even number of rows */
|
||||
if (i % priv->cur_children_per_line == 0
|
||||
&& prev->area.y < start_y - page_size)
|
||||
break;
|
||||
|
||||
child = prev;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gint i = 0;
|
||||
|
||||
/* Down */
|
||||
while (iter != NULL && !g_sequence_iter_is_end (iter))
|
||||
{
|
||||
iter = egg_flow_box_get_next_visible (box, iter);
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
break;
|
||||
|
||||
next = g_sequence_get (iter);
|
||||
|
||||
if (i % priv->cur_children_per_line == 0
|
||||
&& next->area.y > start_y + page_size)
|
||||
break;
|
||||
|
||||
child = next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
end_y = child->area.y;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (child == NULL || child == priv->cursor_child)
|
||||
{
|
||||
GtkDirectionType direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
|
||||
|
||||
if (!gtk_widget_keynav_failed (GTK_WIDGET (box), direction))
|
||||
{
|
||||
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (box));
|
||||
|
||||
if (toplevel)
|
||||
gtk_widget_child_focus (toplevel,
|
||||
direction == GTK_DIR_UP ?
|
||||
GTK_DIR_TAB_BACKWARD :
|
||||
GTK_DIR_TAB_FORWARD);
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
egg_flow_box_update_cursor (box, child);
|
||||
if (!modify_selection_pressed)
|
||||
egg_flow_box_select_child_info (box, child);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
egg_flow_box_real_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
|
@ -2343,6 +2924,7 @@ egg_flow_box_finalize (GObject *obj)
|
|||
|
||||
g_sequence_free (priv->children);
|
||||
g_hash_table_unref (priv->child_hash);
|
||||
g_clear_object (&priv->adjustment);
|
||||
|
||||
G_OBJECT_CLASS (egg_flow_box_parent_class)->finalize (obj);
|
||||
}
|
||||
|
@ -2353,6 +2935,7 @@ egg_flow_box_class_init (EggFlowBoxClass *class)
|
|||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
|
||||
GtkBindingSet *binding_set;
|
||||
|
||||
object_class->finalize = egg_flow_box_finalize;
|
||||
object_class->get_property = egg_flow_box_get_property;
|
||||
|
@ -2363,6 +2946,7 @@ egg_flow_box_class_init (EggFlowBoxClass *class)
|
|||
widget_class->motion_notify_event = egg_flow_box_real_motion_notify_event;
|
||||
widget_class->size_allocate = egg_flow_box_real_size_allocate;
|
||||
widget_class->realize = egg_flow_box_real_realize;
|
||||
widget_class->focus = egg_flow_box_real_focus;
|
||||
widget_class->draw = egg_flow_box_real_draw;
|
||||
widget_class->button_press_event = egg_flow_box_real_button_press_event;
|
||||
widget_class->button_release_event = egg_flow_box_real_button_release_event;
|
||||
|
@ -2378,6 +2962,10 @@ egg_flow_box_class_init (EggFlowBoxClass *class)
|
|||
container_class->child_type = egg_flow_box_real_child_type;
|
||||
gtk_container_class_handle_border_width (container_class);
|
||||
|
||||
class->activate_cursor_child = egg_flow_box_real_activate_cursor_child;
|
||||
class->toggle_cursor_child = egg_flow_box_real_toggle_cursor_child;
|
||||
class->move_cursor = egg_flow_box_real_move_cursor;
|
||||
|
||||
g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
|
@ -2521,6 +3109,59 @@ egg_flow_box_class_init (EggFlowBoxClass *class)
|
|||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
signals[ACTIVATE_CURSOR_CHILD] = g_signal_new ("activate-cursor-child",
|
||||
EGG_TYPE_FLOW_BOX,
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (EggFlowBoxClass, activate_cursor_child),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
signals[TOGGLE_CURSOR_CHILD] = g_signal_new ("toggle-cursor-child",
|
||||
EGG_TYPE_FLOW_BOX,
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (EggFlowBoxClass, toggle_cursor_child),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
signals[MOVE_CURSOR] = g_signal_new ("move-cursor",
|
||||
EGG_TYPE_FLOW_BOX,
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (EggFlowBoxClass, move_cursor),
|
||||
NULL, NULL,
|
||||
_egg_marshal_VOID__ENUM_INT,
|
||||
G_TYPE_NONE, 2,
|
||||
GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT);
|
||||
|
||||
widget_class->activate_signal = signals[ACTIVATE_CURSOR_CHILD];
|
||||
|
||||
binding_set = gtk_binding_set_by_class (class);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_Home, 0,
|
||||
GTK_MOVEMENT_BUFFER_ENDS, -1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
|
||||
GTK_MOVEMENT_BUFFER_ENDS, -1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_End, 0,
|
||||
GTK_MOVEMENT_BUFFER_ENDS, 1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0,
|
||||
GTK_MOVEMENT_BUFFER_ENDS, 1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
|
||||
GTK_MOVEMENT_DISPLAY_LINES, -1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
|
||||
GTK_MOVEMENT_DISPLAY_LINES, -1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
|
||||
GTK_MOVEMENT_DISPLAY_LINES, 1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
|
||||
GTK_MOVEMENT_DISPLAY_LINES, 1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
|
||||
GTK_MOVEMENT_PAGES, -1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
|
||||
GTK_MOVEMENT_PAGES, -1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
|
||||
GTK_MOVEMENT_PAGES, 1);
|
||||
egg_flow_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
|
||||
GTK_MOVEMENT_PAGES, 1);
|
||||
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
|
||||
"toggle-cursor-child", 0, NULL);
|
||||
|
||||
g_type_class_add_private (class, sizeof (EggFlowBoxPrivate));
|
||||
}
|
||||
|
@ -2543,6 +3184,7 @@ egg_flow_box_init (EggFlowBox *box)
|
|||
priv->child_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
|
||||
priv->activate_on_single_click = TRUE;
|
||||
|
||||
gtk_widget_set_can_focus (GTK_WIDGET (box), TRUE);
|
||||
gtk_widget_set_has_window (GTK_WIDGET (box), TRUE);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,9 @@ struct _EggFlowBoxClass
|
|||
|
||||
void (* child_activated) (EggFlowBox *self, GtkWidget *child);
|
||||
void (* selected_children_changed) (EggFlowBox *self);
|
||||
void (*activate_cursor_child) (EggFlowBox *self);
|
||||
void (*toggle_cursor_child) (EggFlowBox *self);
|
||||
void (*move_cursor) (EggFlowBox *self, GtkMovementStep step, gint count);
|
||||
};
|
||||
|
||||
GType egg_flow_box_get_type (void) G_GNUC_CONST;
|
||||
|
@ -107,6 +110,8 @@ void egg_flow_box_select_child (EggFlowBox
|
|||
GtkSelectionMode egg_flow_box_get_selection_mode (EggFlowBox *box);
|
||||
void egg_flow_box_set_selection_mode (EggFlowBox *box,
|
||||
GtkSelectionMode mode);
|
||||
void egg_flow_box_set_adjustment (EggFlowBox *box,
|
||||
GtkAdjustment *adjustment);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
@ -338,6 +338,9 @@ create_window (void)
|
|||
gtk_widget_show (flowbox);
|
||||
gtk_container_add (GTK_CONTAINER (swindow), flowbox);
|
||||
|
||||
egg_flow_box_set_adjustment (EGG_FLOW_BOX (flowbox),
|
||||
gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (swindow)));
|
||||
|
||||
g_signal_connect (flowbox, "child-activated", G_CALLBACK (on_child_activated), NULL);
|
||||
g_signal_connect (flowbox, "selected-children-changed", G_CALLBACK (on_selected_children_changed), NULL);
|
||||
|
||||
|
|
Loading…
Reference in a new issue