Compare commits
25 Commits
rendering-
...
touch-text
Author | SHA1 | Date | |
---|---|---|---|
|
39c8db124d | ||
|
1c11f6ed1a | ||
|
291ff93a6b | ||
|
ccc4f0f133 | ||
|
eecd228493 | ||
|
f69179b562 | ||
|
b79946956b | ||
|
1b5615df75 | ||
|
adce361ec5 | ||
|
209e39ab8a | ||
|
3158630a68 | ||
|
2b02d97a93 | ||
|
f194d499c3 | ||
|
3fd45e0267 | ||
|
2a537b5b8b | ||
|
89e420b390 | ||
|
426e8c8c97 | ||
|
78a8782b13 | ||
|
da8de20928 | ||
|
2eaabf5ad3 | ||
|
e299dfa462 | ||
|
edd21e61f2 | ||
|
80a89c5245 | ||
|
739b6e1862 | ||
|
03a7331c5c |
@@ -394,8 +394,9 @@ gtk_appchooser_impl_c_sources = \
|
||||
gtkappchooseronlinepk.c
|
||||
endif
|
||||
|
||||
gtk_private_type_h_sources = \
|
||||
gtkcsstypesprivate.h
|
||||
gtk_private_type_h_sources = \
|
||||
gtkcsstypesprivate.h \
|
||||
gtktexthandleprivate.h
|
||||
|
||||
|
||||
# GTK+ header files that don't get installed
|
||||
@@ -525,6 +526,7 @@ gtk_private_h_sources = \
|
||||
gtktextbtree.h \
|
||||
gtktextbufferserialize.h \
|
||||
gtktextchildprivate.h \
|
||||
gtktexthandleprivate.h \
|
||||
gtktextiterprivate.h \
|
||||
gtktextmarkprivate.h \
|
||||
gtktextsegment.h \
|
||||
@@ -821,6 +823,7 @@ gtk_base_c_sources = \
|
||||
gtktextbufferserialize.c \
|
||||
gtktextchild.c \
|
||||
gtktextdisplay.c \
|
||||
gtktexthandle.c \
|
||||
gtktextiter.c \
|
||||
gtktextlayout.c \
|
||||
gtktextmark.c \
|
||||
|
321
gtk/gtkentry.c
321
gtk/gtkentry.c
@@ -65,6 +65,7 @@
|
||||
#include "gtkicontheme.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtktexthandleprivate.h"
|
||||
|
||||
#include "a11y/gtkentryaccessible.h"
|
||||
|
||||
@@ -160,6 +161,8 @@ struct _GtkEntryPrivate
|
||||
|
||||
gchar *placeholder_text;
|
||||
|
||||
GtkTextHandle *text_handle;
|
||||
|
||||
gfloat xalign;
|
||||
|
||||
gint ascent; /* font ascent in pango units */
|
||||
@@ -215,6 +218,7 @@ struct _GtkEntryPrivate
|
||||
guint select_words : 1;
|
||||
guint select_lines : 1;
|
||||
guint truncate_multiline : 1;
|
||||
guint update_handles_on_focus : 1;
|
||||
};
|
||||
|
||||
struct _EntryIconInfo
|
||||
@@ -314,6 +318,7 @@ enum {
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
static gboolean test_touchscreen = FALSE;
|
||||
|
||||
typedef enum {
|
||||
CURSOR_STANDARD,
|
||||
@@ -576,6 +581,13 @@ static GdkPixbuf * gtk_entry_ensure_pixbuf (GtkEntry *en
|
||||
static void gtk_entry_update_cached_style_values(GtkEntry *entry);
|
||||
static gboolean get_middle_click_paste (GtkEntry *entry);
|
||||
|
||||
/* GtkTextHandle handlers */
|
||||
static void gtk_entry_handle_dragged (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gint x,
|
||||
gint y,
|
||||
GtkEntry *entry);
|
||||
|
||||
/* Completion */
|
||||
static gint gtk_entry_completion_timeout (gpointer data);
|
||||
static gboolean gtk_entry_completion_key_press (GtkWidget *widget,
|
||||
@@ -1935,6 +1947,7 @@ gtk_entry_class_init (GtkEntryClass *class)
|
||||
G_PARAM_DEPRECATED));
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (GtkEntryPrivate));
|
||||
test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN_FEATURES") != NULL;
|
||||
|
||||
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE);
|
||||
}
|
||||
@@ -2552,6 +2565,10 @@ gtk_entry_init (GtkEntry *entry)
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
|
||||
|
||||
gtk_entry_update_cached_style_values (entry);
|
||||
|
||||
priv->text_handle = _gtk_text_handle_new (GTK_WIDGET (entry));
|
||||
g_signal_connect (priv->text_handle, "handle-dragged",
|
||||
G_CALLBACK (gtk_entry_handle_dragged), entry);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2789,6 +2806,7 @@ gtk_entry_finalize (GObject *object)
|
||||
if (priv->recompute_idle)
|
||||
g_source_remove (priv->recompute_idle);
|
||||
|
||||
g_object_unref (priv->text_handle);
|
||||
g_free (priv->placeholder_text);
|
||||
g_free (priv->im_module);
|
||||
|
||||
@@ -3010,6 +3028,9 @@ gtk_entry_unmap (GtkWidget *widget)
|
||||
EntryIconInfo *icon_info = NULL;
|
||||
gint i;
|
||||
|
||||
_gtk_text_handle_set_mode (priv->text_handle,
|
||||
GTK_TEXT_HANDLE_MODE_NONE);
|
||||
|
||||
for (i = 0; i < MAX_ICONS; i++)
|
||||
{
|
||||
if ((icon_info = priv->icons[i]) != NULL)
|
||||
@@ -3083,7 +3104,7 @@ gtk_entry_realize (GtkWidget *widget)
|
||||
|
||||
gtk_entry_adjust_scroll (entry);
|
||||
gtk_entry_update_primary_selection (entry);
|
||||
|
||||
_gtk_text_handle_set_relative_to (priv->text_handle, priv->text_area);
|
||||
|
||||
/* If the icon positions are already setup, create their windows.
|
||||
* Otherwise if they don't exist yet, then construct_icon_info()
|
||||
@@ -3111,6 +3132,7 @@ gtk_entry_unrealize (GtkWidget *widget)
|
||||
gtk_entry_reset_layout (entry);
|
||||
|
||||
gtk_im_context_set_client_window (priv->im_context, NULL);
|
||||
_gtk_text_handle_set_relative_to (priv->text_handle, NULL);
|
||||
|
||||
clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY);
|
||||
if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
|
||||
@@ -3852,7 +3874,108 @@ in_selection (GtkEntry *entry,
|
||||
g_free (ranges);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
_gtk_entry_move_handle (GtkEntry *entry,
|
||||
GtkTextHandlePosition pos,
|
||||
gint x,
|
||||
gint y,
|
||||
gint height)
|
||||
{
|
||||
GtkEntryPrivate *priv = entry->priv;
|
||||
|
||||
if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
|
||||
(x < 0 || x > gdk_window_get_width (priv->text_area)))
|
||||
{
|
||||
/* Hide the handle if it's not being manipulated
|
||||
* and fell outside of the visible text area.
|
||||
*/
|
||||
_gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkRectangle rect;
|
||||
|
||||
rect.x = CLAMP (x, 0, gdk_window_get_width (priv->text_area));
|
||||
rect.y = y;
|
||||
rect.width = 1;
|
||||
rect.height = height;
|
||||
|
||||
_gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
|
||||
_gtk_text_handle_set_position (priv->text_handle, pos, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
_gtk_entry_get_selection_bound_location (GtkEntry *entry)
|
||||
{
|
||||
GtkEntryPrivate *priv = entry->priv;
|
||||
PangoLayout *layout;
|
||||
PangoRectangle pos;
|
||||
gint x;
|
||||
const gchar *text;
|
||||
gint index;
|
||||
|
||||
layout = gtk_entry_ensure_layout (entry, FALSE);
|
||||
text = pango_layout_get_text (layout);
|
||||
index = g_utf8_offset_to_pointer (text, priv->selection_bound) - text;
|
||||
pango_layout_index_to_pos (layout, index, &pos);
|
||||
|
||||
if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
|
||||
x = (pos.x + pos.width) / PANGO_SCALE;
|
||||
else
|
||||
x = pos.x / PANGO_SCALE;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_entry_update_handles (GtkEntry *entry,
|
||||
GtkTextHandleMode mode)
|
||||
{
|
||||
GtkEntryPrivate *priv = entry->priv;
|
||||
gint strong_x, height;
|
||||
gint cursor, bound;
|
||||
|
||||
_gtk_text_handle_set_mode (priv->text_handle, mode);
|
||||
|
||||
/* Wait for recomputation before repositioning */
|
||||
if (priv->recompute_idle != 0)
|
||||
return;
|
||||
|
||||
height = gdk_window_get_height (priv->text_area);
|
||||
|
||||
gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
|
||||
cursor = strong_x - priv->scroll_offset;
|
||||
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
|
||||
{
|
||||
gint start, end;
|
||||
|
||||
bound = _gtk_entry_get_selection_bound_location (entry) - priv->scroll_offset;
|
||||
|
||||
if (priv->selection_bound > priv->current_pos)
|
||||
{
|
||||
start = cursor;
|
||||
end = bound;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = bound;
|
||||
end = cursor;
|
||||
}
|
||||
|
||||
/* Update start selection bound */
|
||||
_gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_START,
|
||||
start, 0, height);
|
||||
_gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_END,
|
||||
end, 0, height);
|
||||
}
|
||||
else
|
||||
_gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_CURSOR,
|
||||
cursor, 0, height);
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_entry_button_press (GtkWidget *widget,
|
||||
GdkEventButton *event)
|
||||
@@ -3920,7 +4043,14 @@ gtk_entry_button_press (GtkWidget *widget,
|
||||
else if (event->button == GDK_BUTTON_PRIMARY)
|
||||
{
|
||||
gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
|
||||
gboolean is_touchscreen;
|
||||
GdkDevice *source;
|
||||
|
||||
source = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
is_touchscreen = test_touchscreen ||
|
||||
gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN;
|
||||
|
||||
priv->update_handles_on_focus = is_touchscreen;
|
||||
priv->select_words = FALSE;
|
||||
priv->select_lines = FALSE;
|
||||
|
||||
@@ -3998,7 +4128,12 @@ gtk_entry_button_press (GtkWidget *widget,
|
||||
priv->drag_start_y = event->y;
|
||||
}
|
||||
else
|
||||
gtk_editable_set_position (editable, tmp_pos);
|
||||
{
|
||||
gtk_editable_set_position (editable, tmp_pos);
|
||||
|
||||
if (is_touchscreen)
|
||||
_gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
|
||||
}
|
||||
break;
|
||||
|
||||
case GDK_2BUTTON_PRESS:
|
||||
@@ -4009,6 +4144,9 @@ gtk_entry_button_press (GtkWidget *widget,
|
||||
priv->in_drag = FALSE;
|
||||
priv->select_words = TRUE;
|
||||
gtk_entry_select_word (entry);
|
||||
|
||||
if (is_touchscreen)
|
||||
_gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION);
|
||||
break;
|
||||
|
||||
case GDK_3BUTTON_PRESS:
|
||||
@@ -4019,6 +4157,8 @@ gtk_entry_button_press (GtkWidget *widget,
|
||||
priv->in_drag = FALSE;
|
||||
priv->select_lines = TRUE;
|
||||
gtk_entry_select_line (entry);
|
||||
if (is_touchscreen)
|
||||
_gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -4088,9 +4228,16 @@ gtk_entry_button_release (GtkWidget *widget,
|
||||
if (priv->in_drag)
|
||||
{
|
||||
gint tmp_pos = gtk_entry_find_position (entry, priv->drag_start_x);
|
||||
GdkDevice *source;
|
||||
|
||||
gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos);
|
||||
|
||||
source = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (test_touchscreen ||
|
||||
gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN)
|
||||
_gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
|
||||
|
||||
priv->in_drag = 0;
|
||||
}
|
||||
|
||||
@@ -4212,13 +4359,22 @@ gtk_entry_motion_notify (GtkWidget *widget,
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkInputSource input_source;
|
||||
GdkDevice *source;
|
||||
guint length;
|
||||
|
||||
length = gtk_entry_buffer_get_length (get_buffer (entry));
|
||||
|
||||
if (event->y < 0)
|
||||
tmp_pos = 0;
|
||||
else if (event->y >= gdk_window_get_height (priv->text_area))
|
||||
tmp_pos = gtk_entry_buffer_get_length (get_buffer (entry));
|
||||
tmp_pos = length;
|
||||
else
|
||||
tmp_pos = gtk_entry_find_position (entry, event->x + priv->scroll_offset);
|
||||
|
||||
source = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
input_source = gdk_device_get_source (source);
|
||||
|
||||
if (priv->select_words)
|
||||
{
|
||||
gint min, max;
|
||||
@@ -4254,13 +4410,20 @@ gtk_entry_motion_notify (GtkWidget *widget,
|
||||
if (priv->current_pos != max)
|
||||
pos = min;
|
||||
}
|
||||
|
||||
|
||||
gtk_entry_set_positions (entry, pos, bound);
|
||||
}
|
||||
else
|
||||
gtk_entry_set_positions (entry, tmp_pos, -1);
|
||||
|
||||
/* Update touch handles' position */
|
||||
if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN)
|
||||
_gtk_entry_update_handles (entry,
|
||||
(priv->current_pos == priv->selection_bound) ?
|
||||
GTK_TEXT_HANDLE_MODE_CURSOR :
|
||||
GTK_TEXT_HANDLE_MODE_SELECTION);
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -4300,6 +4463,8 @@ gtk_entry_key_press (GtkWidget *widget,
|
||||
|
||||
gtk_entry_reset_blink_time (entry);
|
||||
gtk_entry_pend_cursor_blink (entry);
|
||||
_gtk_text_handle_set_mode (priv->text_handle,
|
||||
GTK_TEXT_HANDLE_MODE_NONE);
|
||||
|
||||
if (priv->editable)
|
||||
{
|
||||
@@ -4392,6 +4557,10 @@ gtk_entry_focus_in (GtkWidget *widget,
|
||||
gtk_entry_check_cursor_blink (entry);
|
||||
}
|
||||
|
||||
if (priv->update_handles_on_focus &&
|
||||
gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), NULL, NULL))
|
||||
_gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -4404,6 +4573,9 @@ gtk_entry_focus_out (GtkWidget *widget,
|
||||
GtkEntryCompletion *completion;
|
||||
GdkKeymap *keymap;
|
||||
|
||||
_gtk_text_handle_set_mode (priv->text_handle,
|
||||
GTK_TEXT_HANDLE_MODE_NONE);
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
|
||||
@@ -5585,10 +5757,17 @@ recompute_idle_func (gpointer data)
|
||||
|
||||
if (gtk_widget_has_screen (GTK_WIDGET (entry)))
|
||||
{
|
||||
GtkTextHandleMode handle_mode;
|
||||
|
||||
gtk_entry_adjust_scroll (entry);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (entry));
|
||||
|
||||
update_im_cursor_location (entry);
|
||||
|
||||
handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
|
||||
|
||||
if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
|
||||
_gtk_entry_update_handles (entry, handle_mode);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@@ -6020,6 +6199,70 @@ gtk_entry_draw_cursor (GtkEntry *entry,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_entry_handle_dragged (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gint x,
|
||||
gint y,
|
||||
GtkEntry *entry)
|
||||
{
|
||||
gint cursor_pos, selection_bound_pos, tmp_pos;
|
||||
GtkEntryPrivate *priv = entry->priv;
|
||||
GtkTextHandleMode mode;
|
||||
gint *min, *max;
|
||||
|
||||
cursor_pos = priv->current_pos;
|
||||
selection_bound_pos = priv->selection_bound;
|
||||
mode = _gtk_text_handle_get_mode (handle);
|
||||
tmp_pos = gtk_entry_find_position (entry, x + priv->scroll_offset);
|
||||
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
|
||||
cursor_pos >= selection_bound_pos)
|
||||
{
|
||||
max = &cursor_pos;
|
||||
min = &selection_bound_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = &selection_bound_pos;
|
||||
min = &cursor_pos;
|
||||
}
|
||||
|
||||
if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
|
||||
{
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
|
||||
{
|
||||
gint min_pos;
|
||||
|
||||
min_pos = MAX (*min + 1, 0);
|
||||
tmp_pos = MAX (tmp_pos, min_pos);
|
||||
}
|
||||
|
||||
*max = tmp_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
|
||||
{
|
||||
gint max_pos;
|
||||
|
||||
max_pos = *max - 1;
|
||||
*min = MIN (tmp_pos, max_pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor_pos != priv->current_pos ||
|
||||
selection_bound_pos != priv->selection_bound)
|
||||
{
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
|
||||
gtk_entry_set_positions (entry, cursor_pos, cursor_pos);
|
||||
else
|
||||
gtk_entry_set_positions (entry, cursor_pos, selection_bound_pos);
|
||||
|
||||
_gtk_entry_update_handles (entry, mode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_entry_reset_im_context (GtkEntry *entry)
|
||||
{
|
||||
@@ -6182,6 +6425,23 @@ gtk_entry_get_cursor_locations (GtkEntry *entry,
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_gtk_entry_get_is_selection_handle_dragged (GtkEntry *entry)
|
||||
{
|
||||
GtkEntryPrivate *priv = entry->priv;
|
||||
GtkTextHandlePosition pos;
|
||||
|
||||
if (_gtk_text_handle_get_mode (priv->text_handle) != GTK_TEXT_HANDLE_MODE_SELECTION)
|
||||
return FALSE;
|
||||
|
||||
if (priv->current_pos >= priv->selection_bound)
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
|
||||
else
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
|
||||
|
||||
return _gtk_text_handle_get_is_dragged (priv->text_handle, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_entry_adjust_scroll (GtkEntry *entry)
|
||||
{
|
||||
@@ -6194,6 +6454,7 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
|
||||
PangoLayout *layout;
|
||||
PangoLayoutLine *line;
|
||||
PangoRectangle logical_rect;
|
||||
GtkTextHandleMode handle_mode;
|
||||
|
||||
if (!gtk_widget_get_realized (GTK_WIDGET (entry)))
|
||||
return;
|
||||
@@ -6230,22 +6491,33 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
|
||||
|
||||
priv->scroll_offset = CLAMP (priv->scroll_offset, min_offset, max_offset);
|
||||
|
||||
/* And make sure cursors are on screen. Note that the cursor is
|
||||
* actually drawn one pixel into the INNER_BORDER space on
|
||||
* the right, when the scroll is at the utmost right. This
|
||||
* looks better to to me than confining the cursor inside the
|
||||
* border entirely, though it means that the cursor gets one
|
||||
* pixel closer to the edge of the widget on the right than
|
||||
* on the left. This might need changing if one changed
|
||||
* INNER_BORDER from 2 to 1, as one would do on a
|
||||
* small-screen-real-estate display.
|
||||
*
|
||||
* We always make sure that the strong cursor is on screen, and
|
||||
* put the weak cursor on screen if possible.
|
||||
*/
|
||||
if (_gtk_entry_get_is_selection_handle_dragged (entry))
|
||||
{
|
||||
/* The text handle corresponding to the selection bound is
|
||||
* being dragged, ensure it stays onscreen even if we scroll
|
||||
* cursors away, this is so both handles can cause content
|
||||
* to scroll.
|
||||
*/
|
||||
strong_x = weak_x = _gtk_entry_get_selection_bound_location (entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* And make sure cursors are on screen. Note that the cursor is
|
||||
* actually drawn one pixel into the INNER_BORDER space on
|
||||
* the right, when the scroll is at the utmost right. This
|
||||
* looks better to to me than confining the cursor inside the
|
||||
* border entirely, though it means that the cursor gets one
|
||||
* pixel closer to the edge of the widget on the right than
|
||||
* on the left. This might need changing if one changed
|
||||
* INNER_BORDER from 2 to 1, as one would do on a
|
||||
* small-screen-real-estate display.
|
||||
*
|
||||
* We always make sure that the strong cursor is on screen, and
|
||||
* put the weak cursor on screen if possible.
|
||||
*/
|
||||
gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
|
||||
}
|
||||
|
||||
gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
|
||||
|
||||
strong_xoffset = strong_x - priv->scroll_offset;
|
||||
|
||||
if (strong_xoffset < 0)
|
||||
@@ -6272,6 +6544,11 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (entry), "scroll-offset");
|
||||
|
||||
handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
|
||||
|
||||
if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
|
||||
_gtk_entry_update_handles (entry, handle_mode);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -6294,7 +6571,7 @@ gtk_entry_move_adjustments (GtkEntry *entry)
|
||||
|
||||
gtk_widget_get_allocation (widget, &allocation);
|
||||
|
||||
/* Cursor position, layout offset, border width, and widget allocation */
|
||||
/* Cursor/char position, layout offset, border width, and widget allocation */
|
||||
gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &x, NULL);
|
||||
get_layout_position (entry, &layout_x, NULL);
|
||||
_gtk_entry_get_borders (entry, &borders);
|
||||
|
@@ -69,6 +69,7 @@ VOID:ENUM,FLOAT
|
||||
VOID:ENUM,FLOAT,BOOLEAN
|
||||
VOID:ENUM,INT
|
||||
VOID:ENUM,INT,BOOLEAN
|
||||
VOID:ENUM,INT,INT
|
||||
VOID:ENUM,BOXED
|
||||
VOID:ENUM,STRING
|
||||
VOID:FLAGS
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#include "gtkcsstypesprivate.h"
|
||||
#include "gtktexthandleprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
@@ -2907,6 +2907,9 @@ gtk_scrolled_window_captured_event (GtkWidget *widget,
|
||||
gboolean retval = FALSE;
|
||||
GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW (widget)->priv;
|
||||
|
||||
if (gdk_window_get_window_type (event->any.window) == GDK_WINDOW_TEMP)
|
||||
return FALSE;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case GDK_TOUCH_BEGIN:
|
||||
|
@@ -701,6 +701,18 @@ struct _GtkStyleContextClass
|
||||
*/
|
||||
#define GTK_STYLE_CLASS_LEVEL_BAR "level-bar"
|
||||
|
||||
/**
|
||||
* GTK_STYLE_CLASS_CURSOR_HANDLE:
|
||||
*
|
||||
*/
|
||||
#define GTK_STYLE_CLASS_CURSOR_HANDLE "cursor-handle"
|
||||
|
||||
/**
|
||||
* GTK_STYLE_CLASS_INVERTED_CURSOR_HANDLE:
|
||||
*
|
||||
*/
|
||||
#define GTK_STYLE_CLASS_INVERTED_CURSOR_HANDLE "inverted-cursor-handle"
|
||||
|
||||
/* Predefined set of widget regions */
|
||||
|
||||
/**
|
||||
|
666
gtk/gtktexthandle.c
Normal file
666
gtk/gtktexthandle.c
Normal file
@@ -0,0 +1,666 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright © 2012 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gtkprivatetypebuiltins.h"
|
||||
#include "gtktexthandleprivate.h"
|
||||
#include "gtkmarshalers.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkintl.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
typedef struct _GtkTextHandlePrivate GtkTextHandlePrivate;
|
||||
typedef struct _HandleWindow HandleWindow;
|
||||
|
||||
enum {
|
||||
HANDLE_DRAGGED,
|
||||
DRAG_FINISHED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_PARENT,
|
||||
PROP_RELATIVE_TO
|
||||
};
|
||||
|
||||
struct _HandleWindow
|
||||
{
|
||||
GdkWindow *window;
|
||||
GdkRectangle pointing_to;
|
||||
gint dx;
|
||||
gint dy;
|
||||
guint dragged : 1;
|
||||
};
|
||||
|
||||
struct _GtkTextHandlePrivate
|
||||
{
|
||||
HandleWindow windows[2];
|
||||
GtkWidget *parent;
|
||||
GdkWindow *relative_to;
|
||||
|
||||
gulong draw_signal_id;
|
||||
gulong event_signal_id;
|
||||
gulong style_updated_id;
|
||||
gulong composited_changed_id;
|
||||
guint realized : 1;
|
||||
guint mode : 2;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkTextHandle, _gtk_text_handle, G_TYPE_OBJECT)
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
_gtk_text_handle_get_size (GtkTextHandle *handle,
|
||||
gint *width,
|
||||
gint *height)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
gint w, h;
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
gtk_widget_style_get (priv->parent,
|
||||
"text-handle-width", &w,
|
||||
"text-handle-height", &h,
|
||||
NULL);
|
||||
if (width)
|
||||
*width = w;
|
||||
|
||||
if (height)
|
||||
*height = h;
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_handle_draw (GtkTextHandle *handle,
|
||||
cairo_t *cr,
|
||||
GtkTextHandlePosition pos)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
GtkStyleContext *context;
|
||||
gint width, height;
|
||||
|
||||
priv = handle->priv;
|
||||
cairo_save (cr);
|
||||
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba (cr, 0, 0, 0, 0);
|
||||
cairo_paint (cr);
|
||||
|
||||
context = gtk_widget_get_style_context (priv->parent);
|
||||
gtk_style_context_save (context);
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
|
||||
|
||||
if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_CURSOR_HANDLE);
|
||||
else
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_INVERTED_CURSOR_HANDLE);
|
||||
|
||||
_gtk_text_handle_get_size (handle, &width, &height);
|
||||
gtk_render_slider (context, cr, 0, 0, width, height,
|
||||
GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
gtk_style_context_restore (context);
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_handle_update_shape (GtkTextHandle *handle,
|
||||
GdkWindow *window)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
if (gtk_widget_is_composited (priv->parent))
|
||||
gdk_window_shape_combine_region (window, NULL, 0, 0);
|
||||
else
|
||||
{
|
||||
GtkTextHandlePosition pos;
|
||||
cairo_surface_t *surface;
|
||||
cairo_region_t *region;
|
||||
cairo_t *cr;
|
||||
|
||||
surface =
|
||||
gdk_window_create_similar_surface (window,
|
||||
CAIRO_CONTENT_COLOR_ALPHA,
|
||||
gdk_window_get_width (window),
|
||||
gdk_window_get_height (window));
|
||||
|
||||
if (window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
|
||||
else if (window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
|
||||
|
||||
cr = cairo_create (surface);
|
||||
_gtk_text_handle_draw (handle, cr, pos);
|
||||
cairo_destroy (cr);
|
||||
|
||||
region = gdk_cairo_region_create_from_surface (surface);
|
||||
gdk_window_shape_combine_region (window, region, 0, 0);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
cairo_region_destroy (region);
|
||||
}
|
||||
}
|
||||
|
||||
static GdkWindow *
|
||||
_gtk_text_handle_create_window (GtkTextHandle *handle)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
GdkRGBA bg = { 0, 0, 0, 0 };
|
||||
GdkWindowAttr attributes;
|
||||
GdkWindow *window;
|
||||
GdkVisual *visual;
|
||||
gint mask;
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
attributes.x = 0;
|
||||
attributes.y = 0;
|
||||
_gtk_text_handle_get_size (handle, &attributes.width, &attributes.height);
|
||||
attributes.window_type = GDK_WINDOW_TEMP;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.event_mask = (GDK_EXPOSURE_MASK |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_BUTTON1_MOTION_MASK);
|
||||
|
||||
mask = GDK_WA_X | GDK_WA_Y;
|
||||
|
||||
visual = gdk_screen_get_rgba_visual (gtk_widget_get_screen (priv->parent));
|
||||
|
||||
if (visual)
|
||||
{
|
||||
attributes.visual = visual;
|
||||
mask |= GDK_WA_VISUAL;
|
||||
}
|
||||
|
||||
window = gdk_window_new (NULL, &attributes, mask);
|
||||
gdk_window_set_user_data (window, priv->parent);
|
||||
gdk_window_set_background_rgba (window, &bg);
|
||||
|
||||
_gtk_text_handle_update_shape (handle, window);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_text_handle_widget_draw (GtkWidget *widget,
|
||||
cairo_t *cr,
|
||||
GtkTextHandle *handle)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
GtkTextHandlePosition pos;
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
if (!priv->realized)
|
||||
return FALSE;
|
||||
|
||||
if (gtk_cairo_should_draw_window (cr, priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window))
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
|
||||
else if (gtk_cairo_should_draw_window (cr, priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window))
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
_gtk_text_handle_draw (handle, cr, pos);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_text_handle_widget_event (GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
GtkTextHandle *handle)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
GtkTextHandlePosition pos;
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
if (event->any.window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
|
||||
else if (event->any.window == priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
|
||||
pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS)
|
||||
{
|
||||
priv->windows[pos].dx = event->button.x;
|
||||
priv->windows[pos].dy = event->button.y;
|
||||
priv->windows[pos].dragged = TRUE;
|
||||
}
|
||||
else if (event->type == GDK_BUTTON_RELEASE)
|
||||
{
|
||||
g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
|
||||
priv->windows[pos].dx = priv->windows[pos].dy = 0;
|
||||
priv->windows[pos].dragged = FALSE;
|
||||
}
|
||||
else if (event->type == GDK_MOTION_NOTIFY && priv->windows[pos].dragged)
|
||||
{
|
||||
gint x, y, width, height;
|
||||
|
||||
_gtk_text_handle_get_size (handle, &width, &height);
|
||||
gdk_window_get_origin (priv->relative_to, &x, &y);
|
||||
|
||||
x = event->motion.x_root - priv->windows[pos].dx + (width / 2) - x;
|
||||
y = event->motion.y_root - priv->windows[pos].dy - y;
|
||||
|
||||
if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START)
|
||||
y += height;
|
||||
|
||||
g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_handle_update_window (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
HandleWindow *handle_window;
|
||||
gboolean visible;
|
||||
gint x, y;
|
||||
|
||||
priv = handle->priv;
|
||||
handle_window = &priv->windows[pos];
|
||||
|
||||
if (!handle_window->window)
|
||||
return;
|
||||
|
||||
/* Get current state and destroy */
|
||||
visible = gdk_window_is_visible (handle_window->window);
|
||||
|
||||
if (visible)
|
||||
{
|
||||
gint width;
|
||||
|
||||
_gtk_text_handle_get_size (handle, &width, NULL);
|
||||
gdk_window_get_root_coords (handle_window->window,
|
||||
width / 2, 0, &x, &y);
|
||||
}
|
||||
|
||||
gdk_window_destroy (handle_window->window);
|
||||
|
||||
/* Create new window and apply old state */
|
||||
handle_window->window = _gtk_text_handle_create_window (handle);
|
||||
|
||||
if (visible)
|
||||
{
|
||||
gdk_window_show (handle_window->window);
|
||||
_gtk_text_handle_set_position (handle, pos,
|
||||
&handle_window->pointing_to);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_handle_update_windows (GtkTextHandle *handle)
|
||||
{
|
||||
_gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
|
||||
_gtk_text_handle_update_window (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_handle_constructed (GObject *object)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
priv = GTK_TEXT_HANDLE (object)->priv;
|
||||
g_assert (priv->parent != NULL);
|
||||
|
||||
priv->draw_signal_id =
|
||||
g_signal_connect (priv->parent, "draw",
|
||||
G_CALLBACK (gtk_text_handle_widget_draw),
|
||||
object);
|
||||
priv->event_signal_id =
|
||||
g_signal_connect (priv->parent, "event",
|
||||
G_CALLBACK (gtk_text_handle_widget_event),
|
||||
object);
|
||||
priv->composited_changed_id =
|
||||
g_signal_connect_swapped (priv->parent, "composited-changed",
|
||||
G_CALLBACK (_gtk_text_handle_update_windows),
|
||||
object);
|
||||
priv->style_updated_id =
|
||||
g_signal_connect_swapped (priv->parent, "style-updated",
|
||||
G_CALLBACK (_gtk_text_handle_update_windows),
|
||||
object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_handle_finalize (GObject *object)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
priv = GTK_TEXT_HANDLE (object)->priv;
|
||||
|
||||
if (priv->relative_to)
|
||||
g_object_unref (priv->relative_to);
|
||||
|
||||
if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window)
|
||||
gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
|
||||
|
||||
if (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window)
|
||||
gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
|
||||
|
||||
if (g_signal_handler_is_connected (priv->parent, priv->draw_signal_id))
|
||||
g_signal_handler_disconnect (priv->parent, priv->draw_signal_id);
|
||||
|
||||
if (g_signal_handler_is_connected (priv->parent, priv->event_signal_id))
|
||||
g_signal_handler_disconnect (priv->parent, priv->event_signal_id);
|
||||
|
||||
if (g_signal_handler_is_connected (priv->parent, priv->composited_changed_id))
|
||||
g_signal_handler_disconnect (priv->parent, priv->composited_changed_id);
|
||||
|
||||
if (g_signal_handler_is_connected (priv->parent, priv->style_updated_id))
|
||||
g_signal_handler_disconnect (priv->parent, priv->style_updated_id);
|
||||
|
||||
G_OBJECT_CLASS (_gtk_text_handle_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_handle_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
GtkTextHandle *handle;
|
||||
|
||||
handle = GTK_TEXT_HANDLE (object);
|
||||
priv = handle->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PARENT:
|
||||
priv->parent = g_value_get_object (value);
|
||||
break;
|
||||
case PROP_RELATIVE_TO:
|
||||
_gtk_text_handle_set_relative_to (handle,
|
||||
g_value_get_object (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_handle_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
priv = GTK_TEXT_HANDLE (object)->priv;
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PARENT:
|
||||
g_value_set_object (value, priv->parent);
|
||||
break;
|
||||
case PROP_RELATIVE_TO:
|
||||
g_value_set_object (value, priv->relative_to);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_handle_class_init (GtkTextHandleClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->constructed = gtk_text_handle_constructed;
|
||||
object_class->finalize = gtk_text_handle_finalize;
|
||||
object_class->set_property = gtk_text_handle_set_property;
|
||||
object_class->get_property = gtk_text_handle_get_property;
|
||||
|
||||
signals[HANDLE_DRAGGED] =
|
||||
g_signal_new (I_("handle-dragged"),
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GtkTextHandleClass, handle_dragged),
|
||||
NULL, NULL,
|
||||
_gtk_marshal_VOID__ENUM_INT_INT,
|
||||
G_TYPE_NONE, 3,
|
||||
GTK_TYPE_TEXT_HANDLE_POSITION,
|
||||
G_TYPE_INT, G_TYPE_INT);
|
||||
signals[DRAG_FINISHED] =
|
||||
g_signal_new (I_("drag-finished"),
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
G_SIGNAL_RUN_LAST, 0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__ENUM,
|
||||
G_TYPE_NONE, 1,
|
||||
GTK_TYPE_TEXT_HANDLE_POSITION);
|
||||
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_PARENT,
|
||||
g_param_spec_object ("parent",
|
||||
P_("Parent widget"),
|
||||
P_("Parent widget"),
|
||||
GTK_TYPE_WIDGET,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY));
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_RELATIVE_TO,
|
||||
g_param_spec_object ("relative-to",
|
||||
P_("Window"),
|
||||
P_("Window the coordinates are based upon"),
|
||||
GDK_TYPE_WINDOW,
|
||||
GTK_PARAM_READWRITE));
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (GtkTextHandlePrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_handle_init (GtkTextHandle *handle)
|
||||
{
|
||||
handle->priv = G_TYPE_INSTANCE_GET_PRIVATE (handle,
|
||||
GTK_TYPE_TEXT_HANDLE,
|
||||
GtkTextHandlePrivate);
|
||||
}
|
||||
|
||||
GtkTextHandle *
|
||||
_gtk_text_handle_new (GtkWidget *parent)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_TEXT_HANDLE,
|
||||
"parent", parent,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_text_handle_set_relative_to (GtkTextHandle *handle,
|
||||
GdkWindow *window)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
|
||||
g_return_if_fail (!window || GDK_IS_WINDOW (window));
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
if (priv->relative_to)
|
||||
{
|
||||
gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
|
||||
gdk_window_destroy (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
|
||||
g_object_unref (priv->relative_to);
|
||||
}
|
||||
|
||||
if (window)
|
||||
{
|
||||
priv->relative_to = g_object_ref (window);
|
||||
priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window =
|
||||
_gtk_text_handle_create_window (handle);
|
||||
priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window =
|
||||
_gtk_text_handle_create_window (handle);
|
||||
priv->realized = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window = NULL;
|
||||
priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window = NULL;
|
||||
priv->relative_to = NULL;
|
||||
priv->realized = FALSE;
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (handle), "relative-to");
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_text_handle_set_mode (GtkTextHandle *handle,
|
||||
GtkTextHandleMode mode)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
|
||||
|
||||
priv = handle->priv;
|
||||
|
||||
if (priv->mode == mode)
|
||||
return;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case GTK_TEXT_HANDLE_MODE_CURSOR:
|
||||
/* Only display one handle */
|
||||
gdk_window_show (priv->windows[GTK_TEXT_HANDLE_POSITION_CURSOR].window);
|
||||
gdk_window_hide (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
|
||||
break;
|
||||
case GTK_TEXT_HANDLE_MODE_SELECTION:
|
||||
/* Display both handles */
|
||||
gdk_window_show (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
|
||||
gdk_window_show (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
|
||||
break;
|
||||
case GTK_TEXT_HANDLE_MODE_NONE:
|
||||
default:
|
||||
gdk_window_hide (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_START].window);
|
||||
gdk_window_hide (priv->windows[GTK_TEXT_HANDLE_POSITION_SELECTION_END].window);
|
||||
break;
|
||||
}
|
||||
|
||||
priv->mode = mode;
|
||||
}
|
||||
|
||||
GtkTextHandleMode
|
||||
_gtk_text_handle_get_mode (GtkTextHandle *handle)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), GTK_TEXT_HANDLE_MODE_NONE);
|
||||
|
||||
priv = handle->priv;
|
||||
return priv->mode;
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_text_handle_set_position (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
GdkRectangle *rect)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
gint x, y, width, height;
|
||||
HandleWindow *handle_window;
|
||||
|
||||
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
|
||||
|
||||
priv = handle->priv;
|
||||
pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_START);
|
||||
|
||||
if (!priv->realized)
|
||||
return;
|
||||
|
||||
if (priv->mode == GTK_TEXT_HANDLE_MODE_NONE ||
|
||||
(priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
|
||||
pos != GTK_TEXT_HANDLE_POSITION_CURSOR))
|
||||
return;
|
||||
|
||||
gdk_window_get_root_coords (priv->relative_to,
|
||||
rect->x, rect->y,
|
||||
&x, &y);
|
||||
_gtk_text_handle_get_size (handle, &width, &height);
|
||||
handle_window = &priv->windows[pos];
|
||||
|
||||
if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR)
|
||||
y += rect->height;
|
||||
else
|
||||
y -= height;
|
||||
|
||||
x -= width / 2;
|
||||
|
||||
gdk_window_move (handle_window->window, x, y);
|
||||
handle_window->pointing_to = *rect;
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_text_handle_set_visible (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gboolean visible)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
GdkWindow *window;
|
||||
|
||||
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
|
||||
|
||||
priv = handle->priv;
|
||||
pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_START);
|
||||
|
||||
if (!priv->realized)
|
||||
return;
|
||||
|
||||
window = priv->windows[pos].window;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (!visible)
|
||||
gdk_window_hide (window);
|
||||
else
|
||||
{
|
||||
if (priv->mode == GTK_TEXT_HANDLE_MODE_NONE ||
|
||||
(priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
|
||||
pos != GTK_TEXT_HANDLE_POSITION_CURSOR))
|
||||
return;
|
||||
|
||||
if (!gdk_window_is_visible (window))
|
||||
gdk_window_show (window);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gtk_text_handle_get_is_dragged (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos)
|
||||
{
|
||||
GtkTextHandlePrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), FALSE);
|
||||
|
||||
priv = handle->priv;
|
||||
pos = CLAMP (pos, GTK_TEXT_HANDLE_POSITION_CURSOR,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_START);
|
||||
|
||||
return priv->windows[pos].dragged;
|
||||
}
|
90
gtk/gtktexthandleprivate.h
Normal file
90
gtk/gtktexthandleprivate.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright © 2012 Carlos Garnacho <carlosg@gnome.org>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __GTK_TEXT_HANDLE_PRIVATE_H__
|
||||
#define __GTK_TEXT_HANDLE_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_TEXT_HANDLE (_gtk_text_handle_get_type ())
|
||||
#define GTK_TEXT_HANDLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_TEXT_HANDLE, GtkTextHandle))
|
||||
#define GTK_TEXT_HANDLE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_TEXT_HANDLE, GtkTextHandleClass))
|
||||
#define GTK_IS_TEXT_HANDLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_TEXT_HANDLE))
|
||||
#define GTK_IS_TEXT_HANDLE_CLASS(o) (G_TYPE_CHECK_CLASS_TYPE ((o), GTK_TYPE_TEXT_HANDLE))
|
||||
#define GTK_TEXT_HANDLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_TEXT_HANDLE, GtkTextHandleClass))
|
||||
|
||||
typedef struct _GtkTextHandle GtkTextHandle;
|
||||
typedef struct _GtkTextHandleClass GtkTextHandleClass;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_TEXT_HANDLE_POSITION_CURSOR,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_START,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_END = GTK_TEXT_HANDLE_POSITION_CURSOR
|
||||
} GtkTextHandlePosition;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_TEXT_HANDLE_MODE_NONE,
|
||||
GTK_TEXT_HANDLE_MODE_CURSOR,
|
||||
GTK_TEXT_HANDLE_MODE_SELECTION
|
||||
} GtkTextHandleMode;
|
||||
|
||||
struct _GtkTextHandle
|
||||
{
|
||||
GObject parent_instance;
|
||||
gpointer priv;
|
||||
};
|
||||
|
||||
struct _GtkTextHandleClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* handle_dragged) (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gint x,
|
||||
gint y);
|
||||
void (* drag_finished) (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos);
|
||||
};
|
||||
|
||||
GType _gtk_text_handle_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkTextHandle * _gtk_text_handle_new (GtkWidget *parent);
|
||||
|
||||
void _gtk_text_handle_set_mode (GtkTextHandle *handle,
|
||||
GtkTextHandleMode mode);
|
||||
GtkTextHandleMode
|
||||
_gtk_text_handle_get_mode (GtkTextHandle *handle);
|
||||
void _gtk_text_handle_set_position (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
GdkRectangle *rect);
|
||||
void _gtk_text_handle_set_visible (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gboolean visible);
|
||||
|
||||
void _gtk_text_handle_set_relative_to (GtkTextHandle *handle,
|
||||
GdkWindow *window);
|
||||
|
||||
gboolean _gtk_text_handle_get_is_dragged (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_TEXT_HANDLE_PRIVATE_H__ */
|
@@ -50,6 +50,7 @@
|
||||
#include "gtkwindow.h"
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtktexthandleprivate.h"
|
||||
|
||||
#include "a11y/gtktextviewaccessible.h"
|
||||
|
||||
@@ -132,6 +133,7 @@ struct _GtkTextViewPrivate
|
||||
GdkDevice *dnd_device;
|
||||
|
||||
gulong selection_drag_handler;
|
||||
GtkTextHandle *text_handle;
|
||||
|
||||
GtkTextWindow *text_window;
|
||||
GtkTextWindow *left_window;
|
||||
@@ -229,6 +231,8 @@ struct _GtkTextViewPrivate
|
||||
* driving the scrollable adjustment values */
|
||||
guint hscroll_policy : 1;
|
||||
guint vscroll_policy : 1;
|
||||
|
||||
guint update_handles_on_focus : 1;
|
||||
};
|
||||
|
||||
struct _GtkTextPendingScroll
|
||||
@@ -454,6 +458,8 @@ static void gtk_text_view_target_list_notify (GtkTextBuffer *buffer,
|
||||
static void gtk_text_view_paste_done_handler (GtkTextBuffer *buffer,
|
||||
GtkClipboard *clipboard,
|
||||
gpointer data);
|
||||
static void gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer,
|
||||
gpointer data);
|
||||
static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
|
||||
GtkTextIter *cursor,
|
||||
gint *x,
|
||||
@@ -497,6 +503,15 @@ static void gtk_text_view_forall (GtkContainer *container,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data);
|
||||
|
||||
/* GtkTextHandle handlers */
|
||||
static void gtk_text_view_handle_dragged (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gint x,
|
||||
gint y,
|
||||
GtkTextView *text_view);
|
||||
static void _gtk_text_view_update_handles (GtkTextView *text_view,
|
||||
GtkTextHandleMode mode);
|
||||
|
||||
/* FIXME probably need the focus methods. */
|
||||
|
||||
typedef struct _GtkTextViewChild GtkTextViewChild;
|
||||
@@ -559,6 +574,7 @@ static gint text_window_get_height (GtkTextWindow *win);
|
||||
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
static gboolean test_touchscreen = FALSE;
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_CONTAINER,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
|
||||
@@ -1390,6 +1406,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
|
||||
g_type_class_add_private (gobject_class, sizeof (GtkTextViewPrivate));
|
||||
|
||||
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_VIEW_ACCESSIBLE);
|
||||
test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN_FEATURES") != NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1456,6 +1473,10 @@ gtk_text_view_init (GtkTextView *text_view)
|
||||
|
||||
/* We handle all our own redrawing */
|
||||
gtk_widget_set_redraw_on_allocate (widget, FALSE);
|
||||
|
||||
priv->text_handle = _gtk_text_handle_new (widget);
|
||||
g_signal_connect (priv->text_handle, "handle-dragged",
|
||||
G_CALLBACK (gtk_text_view_handle_dragged), text_view);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1558,6 +1579,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view,
|
||||
g_signal_handlers_disconnect_by_func (priv->buffer,
|
||||
gtk_text_view_paste_done_handler,
|
||||
text_view);
|
||||
g_signal_handlers_disconnect_by_func (priv->buffer,
|
||||
gtk_text_view_buffer_changed_handler,
|
||||
text_view);
|
||||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
|
||||
{
|
||||
@@ -1607,6 +1631,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view,
|
||||
g_signal_connect (priv->buffer, "paste-done",
|
||||
G_CALLBACK (gtk_text_view_paste_done_handler),
|
||||
text_view);
|
||||
g_signal_connect (priv->buffer, "changed",
|
||||
G_CALLBACK (gtk_text_view_buffer_changed_handler),
|
||||
text_view);
|
||||
|
||||
gtk_text_view_target_list_notify (priv->buffer, NULL, text_view);
|
||||
|
||||
@@ -1616,6 +1643,8 @@ gtk_text_view_set_buffer (GtkTextView *text_view,
|
||||
GDK_SELECTION_PRIMARY);
|
||||
gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
|
||||
}
|
||||
|
||||
_gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE);
|
||||
}
|
||||
|
||||
_gtk_text_view_accessible_set_buffer (text_view, old_buffer);
|
||||
@@ -3106,6 +3135,7 @@ gtk_text_view_finalize (GObject *object)
|
||||
if (priv->bottom_window)
|
||||
text_window_free (priv->bottom_window);
|
||||
|
||||
g_object_unref (priv->text_handle);
|
||||
g_object_unref (priv->im_context);
|
||||
|
||||
g_free (priv->im_module);
|
||||
@@ -4107,6 +4137,8 @@ gtk_text_view_realize (GtkWidget *widget)
|
||||
|
||||
/* Ensure updating the spot location. */
|
||||
gtk_text_view_update_im_spot_location (text_view);
|
||||
|
||||
_gtk_text_handle_set_relative_to (priv->text_handle, priv->text_window->window);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -4147,6 +4179,8 @@ gtk_text_view_unrealize (GtkWidget *widget)
|
||||
if (priv->bottom_window)
|
||||
text_window_unrealize (priv->bottom_window);
|
||||
|
||||
_gtk_text_handle_set_relative_to (priv->text_handle, NULL);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget);
|
||||
}
|
||||
|
||||
@@ -4414,6 +4448,174 @@ emit_event_on_tags (GtkWidget *widget,
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_view_set_handle_position (GtkTextView *text_view,
|
||||
GtkTextIter *iter,
|
||||
GtkTextHandlePosition pos)
|
||||
{
|
||||
GtkTextViewPrivate *priv;
|
||||
GdkRectangle rect;
|
||||
gint x, y;
|
||||
|
||||
priv = text_view->priv;
|
||||
gtk_text_view_get_cursor_locations (text_view, iter, &rect, NULL);
|
||||
|
||||
x = rect.x - priv->xoffset;
|
||||
y = rect.y - priv->yoffset;
|
||||
|
||||
if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
|
||||
(x < 0 || x > SCREEN_WIDTH (text_view) ||
|
||||
y < 0 || y > SCREEN_HEIGHT (text_view)))
|
||||
{
|
||||
/* Hide the handle if it's not being manipulated
|
||||
* and fell outside of the visible text area.
|
||||
*/
|
||||
_gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
|
||||
|
||||
rect.x = CLAMP (x, 0, SCREEN_WIDTH (text_view));
|
||||
rect.y = CLAMP (y, 0, SCREEN_HEIGHT (text_view));
|
||||
_gtk_text_handle_set_position (priv->text_handle, pos, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_view_handle_dragged (GtkTextHandle *handle,
|
||||
GtkTextHandlePosition pos,
|
||||
gint x,
|
||||
gint y,
|
||||
GtkTextView *text_view)
|
||||
{
|
||||
GtkTextViewPrivate *priv;
|
||||
GtkTextIter old_cursor, old_bound;
|
||||
GtkTextIter cursor, bound, iter;
|
||||
GtkTextIter *min, *max;
|
||||
GtkTextHandleMode mode;
|
||||
GtkTextBuffer *buffer;
|
||||
GtkTextHandlePosition cursor_pos;
|
||||
|
||||
priv = text_view->priv;
|
||||
buffer = get_buffer (text_view);
|
||||
mode = _gtk_text_handle_get_mode (handle);
|
||||
|
||||
gtk_text_layout_get_iter_at_pixel (priv->layout, &iter,
|
||||
x + priv->xoffset,
|
||||
y + priv->yoffset);
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &old_cursor,
|
||||
gtk_text_buffer_get_insert (buffer));
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &old_bound,
|
||||
gtk_text_buffer_get_selection_bound (buffer));
|
||||
cursor = old_cursor;
|
||||
bound = old_bound;
|
||||
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
|
||||
gtk_text_iter_compare (&cursor, &bound) >= 0)
|
||||
{
|
||||
cursor_pos = GTK_TEXT_HANDLE_POSITION_CURSOR;
|
||||
max = &cursor;
|
||||
min = &bound;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor_pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
|
||||
max = &bound;
|
||||
min = &cursor;
|
||||
}
|
||||
|
||||
if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
|
||||
{
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
|
||||
gtk_text_iter_compare (&iter, min) <= 0)
|
||||
{
|
||||
iter = *min;
|
||||
gtk_text_iter_forward_char (&iter);
|
||||
}
|
||||
|
||||
*max = iter;
|
||||
_gtk_text_view_set_handle_position (text_view, &iter, pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
|
||||
gtk_text_iter_compare (&iter, max) >= 0)
|
||||
{
|
||||
iter = *max;
|
||||
gtk_text_iter_backward_char (&iter);
|
||||
}
|
||||
|
||||
*min = iter;
|
||||
_gtk_text_view_set_handle_position (text_view, &iter, pos);
|
||||
}
|
||||
|
||||
if (gtk_text_iter_compare (&old_cursor, &cursor) != 0 ||
|
||||
gtk_text_iter_compare (&old_bound, &bound) != 0)
|
||||
{
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
|
||||
gtk_text_buffer_place_cursor (buffer, &cursor);
|
||||
else
|
||||
gtk_text_buffer_select_range (buffer, &cursor, &bound);
|
||||
|
||||
if (_gtk_text_handle_get_is_dragged (priv->text_handle, cursor_pos))
|
||||
gtk_text_view_scroll_mark_onscreen (text_view,
|
||||
gtk_text_buffer_get_insert (buffer));
|
||||
else
|
||||
gtk_text_view_scroll_mark_onscreen (text_view,
|
||||
gtk_text_buffer_get_selection_bound (buffer));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_text_view_update_handles (GtkTextView *text_view,
|
||||
GtkTextHandleMode mode)
|
||||
{
|
||||
GtkTextViewPrivate *priv = text_view->priv;
|
||||
GtkTextIter cursor, bound, min, max;
|
||||
GtkTextBuffer *buffer;
|
||||
|
||||
buffer = get_buffer (text_view);
|
||||
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &cursor,
|
||||
gtk_text_buffer_get_insert (buffer));
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &bound,
|
||||
gtk_text_buffer_get_selection_bound (buffer));
|
||||
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION &&
|
||||
gtk_text_iter_compare (&cursor, &bound) == 0)
|
||||
{
|
||||
mode = GTK_TEXT_HANDLE_MODE_CURSOR;
|
||||
}
|
||||
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_CURSOR &&
|
||||
(!gtk_widget_is_sensitive (GTK_WIDGET (text_view)) || !priv->cursor_visible))
|
||||
{
|
||||
mode = GTK_TEXT_HANDLE_MODE_NONE;
|
||||
}
|
||||
|
||||
_gtk_text_handle_set_mode (priv->text_handle, mode);
|
||||
|
||||
if (gtk_text_iter_compare (&cursor, &bound) >= 0)
|
||||
{
|
||||
min = bound;
|
||||
max = cursor;
|
||||
}
|
||||
else
|
||||
{
|
||||
min = cursor;
|
||||
max = bound;
|
||||
}
|
||||
|
||||
if (mode != GTK_TEXT_HANDLE_MODE_NONE)
|
||||
_gtk_text_view_set_handle_position (text_view, &max,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_END);
|
||||
|
||||
if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
|
||||
_gtk_text_view_set_handle_position (text_view, &min,
|
||||
GTK_TEXT_HANDLE_POSITION_SELECTION_START);
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
|
||||
{
|
||||
@@ -4545,6 +4747,9 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
|
||||
gtk_text_view_reset_blink_time (text_view);
|
||||
gtk_text_view_pend_cursor_blink (text_view);
|
||||
|
||||
_gtk_text_handle_set_mode (priv->text_handle,
|
||||
GTK_TEXT_HANDLE_MODE_NONE);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -4579,6 +4784,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
{
|
||||
GtkTextView *text_view;
|
||||
GtkTextViewPrivate *priv;
|
||||
GdkDevice *device;
|
||||
gboolean is_touchscreen;
|
||||
|
||||
text_view = GTK_TEXT_VIEW (widget);
|
||||
priv = text_view->priv;
|
||||
@@ -4602,10 +4809,16 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
|
||||
#endif
|
||||
|
||||
device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
is_touchscreen = test_touchscreen ||
|
||||
gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN;
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS)
|
||||
{
|
||||
gtk_text_view_reset_im_context (text_view);
|
||||
|
||||
priv->update_handles_on_focus = is_touchscreen;
|
||||
|
||||
if (gdk_event_triggers_context_menu ((GdkEvent *) event))
|
||||
{
|
||||
gtk_text_view_do_popup (text_view, event);
|
||||
@@ -4638,7 +4851,16 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkTextHandleMode mode;
|
||||
|
||||
gtk_text_view_start_selection_drag (text_view, &iter, event);
|
||||
|
||||
if (gtk_widget_is_sensitive (widget) && is_touchscreen)
|
||||
mode = GTK_TEXT_HANDLE_MODE_CURSOR;
|
||||
else
|
||||
mode = GTK_TEXT_HANDLE_MODE_NONE;
|
||||
|
||||
_gtk_text_view_update_handles (text_view, mode);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -4669,6 +4891,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
event->button == GDK_BUTTON_PRIMARY)
|
||||
{
|
||||
GtkTextIter iter;
|
||||
GtkTextHandleMode mode;
|
||||
|
||||
gtk_text_view_end_selection_drag (text_view);
|
||||
|
||||
@@ -4676,11 +4899,18 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
|
||||
&iter,
|
||||
event->x + priv->xoffset,
|
||||
event->y + priv->yoffset);
|
||||
|
||||
|
||||
gtk_text_view_start_selection_drag (text_view, &iter, event);
|
||||
|
||||
if (gtk_widget_is_sensitive (widget) && is_touchscreen)
|
||||
mode = GTK_TEXT_HANDLE_MODE_SELECTION;
|
||||
else
|
||||
mode = GTK_TEXT_HANDLE_MODE_NONE;
|
||||
|
||||
_gtk_text_view_update_handles (text_view, mode);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -4708,6 +4938,8 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
|
||||
return TRUE;
|
||||
else if (priv->pending_place_cursor_button == event->button)
|
||||
{
|
||||
GtkTextHandleMode mode;
|
||||
GdkDevice *device;
|
||||
GtkTextIter iter;
|
||||
|
||||
/* Unselect everything; we clicked inside selection, but
|
||||
@@ -4721,9 +4953,19 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
|
||||
|
||||
gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
|
||||
gtk_text_view_check_cursor_blink (text_view);
|
||||
|
||||
|
||||
device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
|
||||
if (gtk_widget_is_sensitive (widget) &&
|
||||
(test_touchscreen ||
|
||||
gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN))
|
||||
mode = GTK_TEXT_HANDLE_MODE_CURSOR;
|
||||
else
|
||||
mode = GTK_TEXT_HANDLE_MODE_NONE;
|
||||
|
||||
_gtk_text_view_update_handles (text_view, mode);
|
||||
priv->pending_place_cursor_button = 0;
|
||||
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -4770,6 +5012,10 @@ gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
|
||||
gtk_im_context_focus_in (priv->im_context);
|
||||
}
|
||||
|
||||
if (priv->update_handles_on_focus &&
|
||||
gtk_text_buffer_get_selection_bounds (get_buffer (text_view), NULL, NULL))
|
||||
_gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -4797,6 +5043,8 @@ gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
|
||||
g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
|
||||
keymap_direction_changed,
|
||||
text_view);
|
||||
_gtk_text_handle_set_mode (priv->text_handle,
|
||||
GTK_TEXT_HANDLE_MODE_NONE);
|
||||
|
||||
if (priv->editable)
|
||||
{
|
||||
@@ -6101,6 +6349,14 @@ gtk_text_view_paste_done_handler (GtkTextBuffer *buffer,
|
||||
priv->scroll_after_paste = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTextView *text_view = data;
|
||||
_gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_text_view_toggle_overwrite (GtkTextView *text_view)
|
||||
{
|
||||
@@ -6256,24 +6512,25 @@ get_iter_at_pointer (GtkTextView *text_view,
|
||||
}
|
||||
|
||||
static void
|
||||
move_mark_to_pointer_and_scroll (GtkTextView *text_view,
|
||||
const gchar *mark_name,
|
||||
GdkDevice *device)
|
||||
move_mark_to_pointer_and_scroll (GtkTextView *text_view,
|
||||
const gchar *mark_name,
|
||||
GdkDevice *device,
|
||||
GdkInputSource source)
|
||||
{
|
||||
GtkTextIter newplace;
|
||||
GtkTextBuffer *buffer;
|
||||
GtkTextMark *mark;
|
||||
|
||||
buffer = get_buffer (text_view);
|
||||
get_iter_at_pointer (text_view, device, &newplace, NULL, NULL);
|
||||
|
||||
mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
|
||||
|
||||
|
||||
mark = gtk_text_buffer_get_mark (buffer, mark_name);
|
||||
|
||||
/* This may invalidate the layout */
|
||||
DV(g_print (G_STRLOC": move mark\n"));
|
||||
|
||||
gtk_text_buffer_move_mark (get_buffer (text_view),
|
||||
mark,
|
||||
&newplace);
|
||||
|
||||
|
||||
gtk_text_buffer_move_mark (buffer, mark, &newplace);
|
||||
|
||||
DV(g_print (G_STRLOC": scrolling onscreen\n"));
|
||||
gtk_text_view_scroll_mark_onscreen (text_view, mark);
|
||||
|
||||
@@ -6458,16 +6715,22 @@ selection_motion_event_handler (GtkTextView *text_view,
|
||||
SelectionData *data)
|
||||
{
|
||||
GtkTextViewPrivate *priv;
|
||||
GdkInputSource input_source;
|
||||
GdkDevice *device;
|
||||
|
||||
priv = text_view->priv;
|
||||
gdk_event_request_motions (event);
|
||||
|
||||
device = gdk_event_get_source_device ((GdkEvent *) event);
|
||||
input_source = gdk_device_get_source (device);
|
||||
|
||||
if (priv->grab_device != event->device)
|
||||
return FALSE;
|
||||
|
||||
if (data->granularity == SELECT_CHARACTERS)
|
||||
{
|
||||
move_mark_to_pointer_and_scroll (text_view, "insert", event->device);
|
||||
move_mark_to_pointer_and_scroll (text_view, "insert",
|
||||
event->device, input_source);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -6491,7 +6754,7 @@ selection_motion_event_handler (GtkTextView *text_view,
|
||||
else
|
||||
gtk_text_buffer_select_range (buffer, &end, &orig_start);
|
||||
|
||||
gtk_text_view_scroll_mark_onscreen (text_view,
|
||||
gtk_text_view_scroll_mark_onscreen (text_view,
|
||||
gtk_text_buffer_get_insert (buffer));
|
||||
}
|
||||
|
||||
@@ -6506,6 +6769,9 @@ selection_motion_event_handler (GtkTextView *text_view,
|
||||
text_view->priv->scroll_timeout =
|
||||
gdk_threads_add_timeout (50, selection_scan_timeout, text_view);
|
||||
|
||||
if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN)
|
||||
_gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -7757,7 +8023,10 @@ gtk_text_view_value_changed (GtkAdjustment *adjustment,
|
||||
* changes made by the validation are pushed through.
|
||||
*/
|
||||
gtk_text_view_update_im_spot_location (text_view);
|
||||
|
||||
|
||||
_gtk_text_view_update_handles (text_view,
|
||||
_gtk_text_handle_get_mode (priv->text_handle));
|
||||
|
||||
DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
|
||||
}
|
||||
|
||||
@@ -7933,7 +8202,11 @@ gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
|
||||
}
|
||||
|
||||
if (need_reset)
|
||||
gtk_text_view_reset_im_context (text_view);
|
||||
{
|
||||
gtk_text_view_reset_im_context (text_view);
|
||||
_gtk_text_view_update_handles (text_view,
|
||||
_gtk_text_handle_get_mode (text_view->priv->text_handle));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -3292,6 +3292,19 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
1, G_MAXINT, 16,
|
||||
GTK_PARAM_READABLE));
|
||||
|
||||
gtk_widget_class_install_style_property (klass,
|
||||
g_param_spec_int ("text-handle-width",
|
||||
P_("Width of text selection handles"),
|
||||
P_("Width of text selection handles"),
|
||||
1, G_MAXINT, 16,
|
||||
GTK_PARAM_READABLE));
|
||||
gtk_widget_class_install_style_property (klass,
|
||||
g_param_spec_int ("text-handle-height",
|
||||
P_("Height of text selection handles"),
|
||||
P_("Height of text selection handles"),
|
||||
1, G_MAXINT, 20,
|
||||
GTK_PARAM_READABLE));
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GtkWidgetPrivate));
|
||||
|
||||
gtk_widget_class_set_accessible_type (klass, GTK_TYPE_WIDGET_ACCESSIBLE);
|
||||
|
Reference in New Issue
Block a user