/* 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 "gtktexthandleprivate.h"
#include "gtkbinlayout.h"
#include "gtkcssboxesimplprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkeventcontrollermotion.h"
#include "gtkgesturedrag.h"
#include "gtkgizmoprivate.h"
#include "gtkmarshalers.h"
#include "gdk/gdkmarshalers.h"
#include "gtknativeprivate.h"
#include "gtkprivatetypebuiltins.h"
#include "gtkrendericonprivate.h"
#include "gtksnapshot.h"
#include "gtkwidgetprivate.h"
#include "gtkwindowprivate.h"
#include "gtkprivate.h"
#include "gdk/gdksurfaceprivate.h"
#include "gsk/gskrendererprivate.h"
#define DEBUG_TEXTHANDLE_LOGIC 0
enum {
DRAG_STARTED,
HANDLE_DRAGGED,
DRAG_FINISHED,
LAST_SIGNAL
};
struct _GtkTextHandle
{
GtkWidget parent_instance;
GdkSurface *surface;
GskRenderer *renderer;
GtkEventController *controller;
GtkWidget *controller_widget;
GtkWidget *contents;
#if DEBUG_TEXTHANDLE_LOGIC
GtkEventController *motion;
graphene_point_t mpos;
const GdkRGBA *mpos_color;
#endif
GdkRectangle pointing_to;
GtkBorder border;
graphene_matrix_t surface_transform;
guint surface_transform_changed_cb;
guint role : 2 ;
guint dragged : 1 ;
guint mode_visible : 1 ;
guint user_visible : 1 ;
guint has_point : 1 ;
};
static void gtk_text_handle_native_interface_init (GtkNativeInterface *iface);
static void handle_drag_begin (GtkGestureDrag *gesture,
double x,
double y,
GtkTextHandle *handle);
static void handle_drag_update (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkWidget *widget);
static void handle_drag_end (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkTextHandle *handle);
G_DEFINE_TYPE_WITH_CODE (GtkTextHandle, gtk_text_handle, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_NATIVE,
gtk_text_handle_native_interface_init))
static guint signals[LAST_SIGNAL] = { 0 };
#if DEBUG_TEXTHANDLE_LOGIC
static const GdkRGBA gdk_texthandle_debug_color_red = { .red = 1 ., .green = 0 ., .blue = 0 ., .alpha = 1 . };
static const GdkRGBA gdk_texthandle_debug_color_blue = { .red = 0 ., .green = 0 ., .blue = 1 ., .alpha = 1 . };
static void
handle_get_input_extents (GtkTextHandle *handle,
GtkBorder *border);
static void
handle_do_toplevel_transform (GtkTextHandle *handle, gfloat *tx, gfloat *ty);
static void
handle_motion (GtkEventController *motion, gdouble x, gdouble y, GtkTextHandle *handle)
{
GtkBorder input_extents;
graphene_point_t p;
handle_get_input_extents (handle, &input_extents);
graphene_point_init (&p, x, y);
handle_do_toplevel_transform (handle, &p.x, &p.y);
graphene_point_init_from_point(&handle->mpos, &p);
if (p.x < input_extents.left || p.x >= input_extents.right ||
p.y < input_extents.top || p.y >= input_extents.bottom)
handle->mpos_color = &gdk_texthandle_debug_color_red;
else
handle->mpos_color = &gdk_texthandle_debug_color_blue;
gtk_widget_queue_draw (GTK_WIDGET (handle));
}
static void
handle_motion_leave (GtkEventController *motion, GtkTextHandle *handle)
{
handle->mpos_color = NULL;
}
/* transform tip-space to widget-space - only needed for visualization */
static void
handle_do_tip_transform (GtkTextHandle *handle, gfloat *tx, gfloat *ty)
{
GtkWidget *widget = GTK_WIDGET (handle);
if (handle->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
{
*tx += gtk_widget_get_width (widget) / 2 .f;
}
else if ((handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END &&
gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ||
(handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START &&
gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL))
{
*tx += gtk_widget_get_width (widget);
}
else
{
*tx += 0 .;
}
}
#endif
static GdkSurface *
gtk_text_handle_native_get_surface (GtkNative *native)
{
return GTK_TEXT_HANDLE (native)->surface;
}
static GskRenderer *
gtk_text_handle_native_get_renderer (GtkNative *native)
{
return GTK_TEXT_HANDLE (native)->renderer;
}
static void
gtk_text_handle_native_get_surface_transform (GtkNative *native,
double *x,
double *y)
{
GtkCssBoxes css_boxes;
const graphene_rect_t *margin_rect;
gtk_css_boxes_init (&css_boxes, GTK_WIDGET (native));
margin_rect = gtk_css_boxes_get_margin_rect (&css_boxes);
*x = - margin_rect->origin.x;
*y = - margin_rect->origin.y;
}
static void
gtk_text_handle_get_padding (GtkTextHandle *handle,
GtkBorder *padding)
{
GtkWidget *widget = GTK_WIDGET (handle);
GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
padding->left = gtk_css_number_value_get (style->size->padding_left, 100 );
padding->right = gtk_css_number_value_get (style->size->padding_right, 100 );
padding->top = gtk_css_number_value_get (style->size->padding_top, 100 );
padding->bottom = gtk_css_number_value_get (style->size->padding_bottom, 100 );
}
static void
gtk_text_handle_present_surface (GtkTextHandle *handle)
{
GtkWidget *widget = GTK_WIDGET (handle);
GdkPopupLayout *layout;
GdkRectangle rect;
GtkRequisition req;
GtkWidget *parent;
GtkNative *native;
graphene_point_t point = GRAPHENE_POINT_INIT (handle->pointing_to.x, handle->pointing_to.y);
graphene_point_t transformed;
double nx, ny;
gtk_widget_get_preferred_size (widget, NULL, &req);
gtk_text_handle_get_padding (handle, &handle->border);
parent = gtk_widget_get_parent (widget);
native = gtk_widget_get_native (parent);
gtk_native_get_surface_transform (native, &nx, &ny);
if (!gtk_widget_compute_transform (GTK_WIDGET (native), parent, &handle->surface_transform))
graphene_matrix_init_identity (&handle->surface_transform);
if (!gtk_widget_compute_point (parent, GTK_WIDGET (native),
&point, &transformed))
transformed = point;
rect.x = (int )(transformed.x + nx);
rect.y = (int )(transformed.y + ny) + handle->pointing_to.height - handle->border.top;
rect.width = req.width - handle->border.left - handle->border.right;
rect.height = 1 ;
if (handle->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
rect.x -= rect.width / 2 ;
else if ((handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END &&
gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ||
(handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START &&
gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL))
rect.x -= rect.width;
layout = gdk_popup_layout_new (&rect,
GDK_GRAVITY_SOUTH,
GDK_GRAVITY_NORTH);
gdk_popup_present (GDK_POPUP (handle->surface),
MAX (req.width, 1 ),
MAX (req.height, 1 ),
layout);
gdk_popup_layout_unref (layout);
}
void
gtk_text_handle_present (GtkTextHandle *handle)
{
GtkWidget *widget = GTK_WIDGET (handle);
if (!_gtk_widget_get_alloc_needed (widget))
gtk_widget_ensure_allocate (widget);
else if (gtk_widget_get_visible (widget))
gtk_text_handle_present_surface (handle);
}
static void
gtk_text_handle_native_layout (GtkNative *native,
int width,
int height)
{
GtkWidget *widget = GTK_WIDGET (native);
if (_gtk_widget_get_alloc_needed (widget))
gtk_widget_allocate (widget, width, height, -1 , NULL);
else
gtk_widget_ensure_allocate (widget);
}
static void
gtk_text_handle_native_interface_init (GtkNativeInterface *iface)
{
iface->get_surface = gtk_text_handle_native_get_surface;
iface->get_renderer = gtk_text_handle_native_get_renderer;
iface->get_surface_transform = gtk_text_handle_native_get_surface_transform;
iface->layout = gtk_text_handle_native_layout;
}
static void
surface_mapped_changed (GtkWidget *widget)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
gtk_widget_set_visible (widget, gdk_surface_get_mapped (handle->surface));
}
static void
gtk_text_handle_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (widget));
#if DEBUG_TEXTHANDLE_LOGIC
GtkTextHandle *handle = (GtkTextHandle*)widget;
GtkBorder border;
handle_get_input_extents (GTK_TEXT_HANDLE (widget), &border);
gtk_snapshot_save (snapshot);
/* transform to tip-space */
graphene_point_t tr = GRAPHENE_POINT_INIT_ZERO;
handle_do_tip_transform (handle, &tr.x, &tr.y);
gtk_snapshot_translate(snapshot, &tr);
/* draw extents (happens to work as popup surface allocated is big enough to include them) */
GskRoundedRect rect = GSK_ROUNDED_RECT_INIT(border.left, border.top, border.right - border.left, border.bottom - border.top);
float rl[4 ] = { 2 ., 2 ., 2 ., 2 . };
GdkRGBA clrl[4 ] = { gdk_texthandle_debug_color_blue, gdk_texthandle_debug_color_blue, gdk_texthandle_debug_color_blue, gdk_texthandle_debug_color_blue };
gtk_snapshot_append_border (snapshot, &rect, rl, clrl);
/* draw current pointer */
if (handle->mpos_color)
{
GskPathBuilder *pb = gsk_path_builder_new ();
gsk_path_builder_add_circle (pb, &handle->mpos, 4 .);
GskPath *p = gsk_path_builder_free_to_path (pb);
gtk_snapshot_append_fill (snapshot, p, GSK_FILL_RULE_WINDING, handle->mpos_color);
gsk_path_unref (p);
}
gtk_snapshot_restore (snapshot);
/* draw widget bounds */
GskRoundedRect irect = GSK_ROUNDED_RECT_INIT(0 , 0 , gtk_widget_get_width (widget), gtk_widget_get_height (widget));
float irl[4 ] = { 2 ., 2 ., 2 ., 2 . };
GdkRGBA iclrl[4 ] = { gdk_texthandle_debug_color_red, gdk_texthandle_debug_color_red, gdk_texthandle_debug_color_red, gdk_texthandle_debug_color_red };
gtk_snapshot_append_border (snapshot, &irect, irl, iclrl);
#endif
gtk_css_style_snapshot_icon (style,
snapshot,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->snapshot (widget, snapshot);
}
static void
gtk_text_handle_realize (GtkWidget *widget)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
GdkSurface *parent_surface;
GtkWidget *parent;
cairo_region_t *region;
parent = gtk_widget_get_parent (widget);
parent_surface = gtk_native_get_surface (gtk_widget_get_native (parent));
handle->surface = gdk_surface_new_popup (parent_surface, FALSE );
gdk_surface_set_widget (handle->surface, widget);
region = cairo_region_create ();
gdk_surface_set_input_region (handle->surface, region);
cairo_region_destroy (region);
g_signal_connect_swapped (handle->surface, "notify::mapped" ,
G_CALLBACK (surface_mapped_changed), widget);
GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->realize (widget);
handle->renderer = gsk_renderer_new_for_surface_full (handle->surface, TRUE );
gtk_native_realize (GTK_NATIVE (handle));
}
static void
gtk_text_handle_unrealize (GtkWidget *widget)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
gtk_native_unrealize (GTK_NATIVE (handle));
GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->unrealize (widget);
gsk_renderer_unrealize (handle->renderer);
g_clear_object (&handle->renderer);
g_signal_handlers_disconnect_by_func (handle->surface, surface_mapped_changed, widget);
gdk_surface_set_widget (handle->surface, NULL);
g_clear_pointer (&handle->surface, gdk_surface_destroy);
}
static void
text_handle_set_up_gesture (GtkTextHandle *handle)
{
GtkNative *native;
/* The drag gesture is hooked on the parent native */
native = gtk_widget_get_native (gtk_widget_get_parent (GTK_WIDGET (handle)));
handle->controller_widget = GTK_WIDGET (native);
handle->controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
gtk_event_controller_set_propagation_phase (handle->controller,
GTK_PHASE_CAPTURE);
g_signal_connect (handle->controller, "drag-begin" ,
G_CALLBACK (handle_drag_begin), handle);
g_signal_connect (handle->controller, "drag-update" ,
G_CALLBACK (handle_drag_update), handle);
g_signal_connect (handle->controller, "drag-end" ,
G_CALLBACK (handle_drag_end), handle);
#if DEBUG_TEXTHANDLE_LOGIC
handle->motion = gtk_event_controller_motion_new ();
gtk_event_controller_set_propagation_phase (handle->motion, GTK_PHASE_CAPTURE);
g_signal_connect (handle->motion, "motion" , G_CALLBACK (handle_motion), handle);
g_signal_connect (handle->motion, "leave" , G_CALLBACK (handle_motion_leave), handle);
gtk_widget_add_controller (handle->controller_widget, handle->motion);
#endif
gtk_widget_add_controller (handle->controller_widget, handle->controller);
}
static void
unset_surface_transform_changed_cb (gpointer user_data)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (user_data);
graphene_matrix_init_identity (&handle->surface_transform);
handle->surface_transform_changed_cb = 0 ;
}
static gboolean
surface_transform_changed_cb (GtkWidget *widget,
const graphene_matrix_t *transform,
gpointer user_data)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (user_data);
gtk_text_handle_present_surface (handle);
return G_SOURCE_CONTINUE;
}
static void
gtk_text_handle_map (GtkWidget *widget)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
handle->surface_transform_changed_cb =
gtk_widget_add_surface_transform_changed_callback (gtk_widget_get_parent (widget),
surface_transform_changed_cb,
widget,
unset_surface_transform_changed_cb);
GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->map (widget);
if (handle->has_point)
{
gtk_text_handle_present_surface (handle);
text_handle_set_up_gesture (handle);
}
}
static void
gtk_text_handle_unmap (GtkWidget *widget)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
gtk_widget_remove_surface_transform_changed_callback (gtk_widget_get_parent (widget),
handle->surface_transform_changed_cb);
handle->surface_transform_changed_cb = 0 ;
GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->unmap (widget);
gdk_surface_hide (handle->surface);
if (handle->controller_widget)
{
#if DEBUG_TEXTHANDLE_LOGIC
gtk_widget_remove_controller (handle->controller_widget,
handle->motion);
handle->motion = NULL;
#endif
gtk_widget_remove_controller (handle->controller_widget,
handle->controller);
handle->controller_widget = NULL;
handle->controller = NULL;
}
}
static void
gtk_text_handle_dispose (GObject *object)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (object);
g_clear_pointer (&handle->contents, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_text_handle_parent_class)->dispose (object);
}
static void
gtk_text_handle_class_init (GtkTextHandleClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_text_handle_dispose;
widget_class->snapshot = gtk_text_handle_snapshot;
widget_class->realize = gtk_text_handle_realize;
widget_class->unrealize = gtk_text_handle_unrealize;
widget_class->map = gtk_text_handle_map;
widget_class->unmap = gtk_text_handle_unmap;
signals[HANDLE_DRAGGED] =
g_signal_new (I_("handle-dragged" ),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST, 0 ,
NULL, NULL,
_gdk_marshal_VOID__INT_INT,
G_TYPE_NONE, 2 ,
G_TYPE_INT, G_TYPE_INT);
g_signal_set_va_marshaller (signals[HANDLE_DRAGGED],
G_OBJECT_CLASS_TYPE (object_class),
_gdk_marshal_VOID__INT_INTv);
signals[DRAG_STARTED] =
g_signal_new (I_("drag-started" ),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST, 0 ,
NULL, NULL,
NULL,
G_TYPE_NONE, 0 , G_TYPE_NONE);
signals[DRAG_FINISHED] =
g_signal_new (I_("drag-finished" ),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST, 0 ,
NULL, NULL,
NULL,
G_TYPE_NONE, 0 , G_TYPE_NONE);
gtk_widget_class_set_css_name (widget_class, I_("cursor-handle" ));
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
}
/* Relative to pointing_to x/y */
static void
handle_get_input_extents (GtkTextHandle *handle,
GtkBorder *border)
{
GtkWidget *widget = GTK_WIDGET (handle);
if (handle->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
{
border->left = (-gtk_widget_get_width (widget) / 2 ) - handle->border.left;
border->right = (gtk_widget_get_width (widget) / 2 ) + handle->border.right;
}
else if ((handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END &&
gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ||
(handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START &&
gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL))
{
border->left = -gtk_widget_get_width (widget) - handle->border.left;
border->right = handle->border.right;
}
else
{
border->left = -handle->border.left;
border->right = gtk_widget_get_width (widget) + handle->border.right;
}
border->top = - handle->border.top;
border->bottom = gtk_widget_get_height (widget) + handle->border.bottom;
}
/* transform controller-space to tip-space */
static void
handle_do_toplevel_transform (GtkTextHandle *handle, gfloat *tx, gfloat *ty)
{
graphene_point_t p;
/* controller-space to parent-space */
graphene_matrix_transform_point (&handle->surface_transform,
&GRAPHENE_POINT_INIT (*tx, *ty),
&p);
/* parent-space to tip-space */
p.x -= handle->pointing_to.x;
p.y -= handle->pointing_to.y + handle->pointing_to.height;
*tx = p.x;
*ty = p.y;
}
static void
handle_drag_begin (GtkGestureDrag *gesture,
double x,
double y,
GtkTextHandle *handle)
{
GtkBorder input_extents;
graphene_point_t p;
handle_get_input_extents (handle, &input_extents);
graphene_point_init (&p, x, y);
handle_do_toplevel_transform (handle, &p.x, &p.y);
if (p.x < input_extents.left || p.x >= input_extents.right ||
p.y < input_extents.top || p.y >= input_extents.bottom)
{
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
return ;
}
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
handle->dragged = TRUE ;
g_signal_emit (handle, signals[DRAG_STARTED], 0 );
}
static void
handle_drag_update (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkWidget *widget)
{
GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
double start_x, start_y;
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
graphene_point_t p;
graphene_matrix_transform_point (&handle->surface_transform,
&GRAPHENE_POINT_INIT (start_x + offset_x, start_y + offset_y),
&p);
p.y -= handle->pointing_to.height / 2 .f;
g_signal_emit (widget, signals[HANDLE_DRAGGED], 0 , (int )p.x, (int )p.y);
}
static void
handle_drag_end (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
GtkTextHandle *handle)
{
GdkEventSequence *sequence;
GtkEventSequenceState state;
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
state = gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence);
if (state == GTK_EVENT_SEQUENCE_CLAIMED)
g_signal_emit (handle, signals[DRAG_FINISHED], 0 );
handle->dragged = FALSE ;
}
static void
gtk_text_handle_update_for_role (GtkTextHandle *handle)
{
GtkWidget *widget = GTK_WIDGET (handle);
if (handle->role == GTK_TEXT_HANDLE_ROLE_CURSOR)
{
gtk_widget_remove_css_class (widget, "top" );
gtk_widget_add_css_class (widget, "bottom" );
gtk_widget_add_css_class (widget, "insertion-cursor" );
}
else if (handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_END)
{
gtk_widget_remove_css_class (widget, "top" );
gtk_widget_add_css_class (widget, "bottom" );
gtk_widget_remove_css_class (widget, "insertion-cursor" );
}
else if (handle->role == GTK_TEXT_HANDLE_ROLE_SELECTION_START)
{
gtk_widget_add_css_class (widget, "top" );
gtk_widget_remove_css_class (widget, "bottom" );
gtk_widget_remove_css_class (widget, "insertion-cursor" );
}
gtk_widget_queue_draw (widget);
}
static void
gtk_text_handle_init (GtkTextHandle *handle)
{
graphene_matrix_init_identity (&handle->surface_transform);
handle->contents = gtk_gizmo_new ("contents" , NULL, NULL, NULL, NULL, NULL, NULL);
gtk_widget_set_can_target (handle->contents, FALSE );
gtk_widget_set_parent (handle->contents, GTK_WIDGET (handle));
gtk_text_handle_update_for_role (handle);
}
GtkTextHandle *
gtk_text_handle_new (GtkWidget *parent)
{
GtkTextHandle *handle;
handle = g_object_new (GTK_TYPE_TEXT_HANDLE, NULL);
gtk_widget_set_parent (GTK_WIDGET (handle), parent);
return handle;
}
void
gtk_text_handle_set_role (GtkTextHandle *handle,
GtkTextHandleRole role)
{
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
if (handle->role == role)
return ;
handle->role = role;
gtk_text_handle_update_for_role (handle);
if (gtk_widget_get_visible (GTK_WIDGET (handle)))
{
if (handle->has_point)
gtk_text_handle_present_surface (handle);
}
}
GtkTextHandleRole
gtk_text_handle_get_role (GtkTextHandle *handle)
{
g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), GTK_TEXT_HANDLE_ROLE_CURSOR);
return handle->role;
}
void
gtk_text_handle_set_position (GtkTextHandle *handle,
const GdkRectangle *rect)
{
g_return_if_fail (GTK_IS_TEXT_HANDLE (handle));
if (handle->pointing_to.x == rect->x &&
handle->pointing_to.y == rect->y &&
handle->pointing_to.width == rect->width &&
handle->pointing_to.height == rect->height)
return ;
handle->pointing_to = *rect;
handle->has_point = TRUE ;
if (gtk_widget_is_visible (GTK_WIDGET (handle)))
gtk_text_handle_present_surface (handle);
}
gboolean
gtk_text_handle_get_is_dragged (GtkTextHandle *handle)
{
g_return_val_if_fail (GTK_IS_TEXT_HANDLE (handle), FALSE );
return handle->dragged;
}
Messung V0.5 in Prozent C=98 H=94 G=95
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland