mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-28 14:31:10 +00:00
gtktexthandle: Update to gtk4 rendering/input
GtkTextHandle was neglected by whoever removed the ::draw signal, leaving it entirely broken. Update to using GtkGizmo so we can implement snapshot of text handles. Input has received a revamp too, handling is done through a GtkGestureDrag and coordinate calculations simplified by storing the delta to the hotspot on ::begin instead of ::update, as this value is constant throughout the gesture. Widget state management on crossing events happens implicitly, so no longer needs to be done here. Last but not least, CSS has also been updated so handles are rendered at the correct size and proportion, and with the padding that code expects of it.
This commit is contained in:
parent
ffd89d29df
commit
b3e91b7111
@ -23,6 +23,8 @@
|
|||||||
#include "gtkwindowprivate.h"
|
#include "gtkwindowprivate.h"
|
||||||
#include "gtkcssnodeprivate.h"
|
#include "gtkcssnodeprivate.h"
|
||||||
#include "gtkwidgetprivate.h"
|
#include "gtkwidgetprivate.h"
|
||||||
|
#include "gtkgizmoprivate.h"
|
||||||
|
#include "gtkrendericonprivate.h"
|
||||||
#include "gtkintl.h"
|
#include "gtkintl.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
@ -93,30 +95,6 @@ _gtk_text_handle_get_size (GtkTextHandle *handle,
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
_gtk_text_handle_draw (GtkTextHandle *handle,
|
|
||||||
cairo_t *cr,
|
|
||||||
GtkTextHandlePosition pos)
|
|
||||||
{
|
|
||||||
GtkTextHandlePrivate *priv;
|
|
||||||
HandleWindow *handle_window;
|
|
||||||
GtkStyleContext *context;
|
|
||||||
gint width, height;
|
|
||||||
|
|
||||||
priv = handle->priv;
|
|
||||||
handle_window = &priv->windows[pos];
|
|
||||||
|
|
||||||
context = gtk_widget_get_style_context (handle_window->widget);
|
|
||||||
_gtk_text_handle_get_size (handle, pos, &width, &height);
|
|
||||||
|
|
||||||
cairo_save (cr);
|
|
||||||
cairo_translate (cr, handle_window->border.left, handle_window->border.top);
|
|
||||||
|
|
||||||
gtk_render_handle (context, cr, 0, 0, width, height);
|
|
||||||
|
|
||||||
cairo_restore (cr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
_text_handle_pos_from_widget (GtkTextHandle *handle,
|
_text_handle_pos_from_widget (GtkTextHandle *handle,
|
||||||
GtkWidget *widget)
|
GtkWidget *widget)
|
||||||
@ -131,28 +109,6 @@ _text_handle_pos_from_widget (GtkTextHandle *handle,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gtk_text_handle_widget_draw (GtkWidget *widget,
|
|
||||||
cairo_t *cr,
|
|
||||||
GtkTextHandle *handle)
|
|
||||||
{
|
|
||||||
gint pos;
|
|
||||||
|
|
||||||
pos = _text_handle_pos_from_widget (handle, widget);
|
|
||||||
|
|
||||||
if (pos < 0)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Show the invisible border */
|
|
||||||
cairo_set_source_rgba (cr, 1, 0, 0, 0.5);
|
|
||||||
cairo_paint (cr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_gtk_text_handle_draw (handle, cr, pos);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_text_handle_set_state (GtkTextHandle *handle,
|
gtk_text_handle_set_state (GtkTextHandle *handle,
|
||||||
gint pos,
|
gint pos,
|
||||||
@ -181,101 +137,86 @@ gtk_text_handle_unset_state (GtkTextHandle *handle,
|
|||||||
gtk_widget_queue_draw (priv->windows[pos].widget);
|
gtk_widget_queue_draw (priv->windows[pos].widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static void
|
||||||
gtk_text_handle_widget_event (GtkWidget *widget,
|
handle_drag_begin (GtkGestureDrag *gesture,
|
||||||
GdkEvent *event,
|
gdouble x,
|
||||||
GtkTextHandle *handle)
|
gdouble y,
|
||||||
|
GtkTextHandle *handle)
|
||||||
{
|
{
|
||||||
GtkTextHandlePrivate *priv;
|
GtkTextHandlePrivate *priv = handle->priv;
|
||||||
GdkEventType event_type;
|
GtkWidget *widget;
|
||||||
gdouble event_x, event_y;
|
|
||||||
guint state;
|
|
||||||
gint pos;
|
gint pos;
|
||||||
GdkCrossingMode mode;
|
|
||||||
|
|
||||||
priv = handle->priv;
|
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
|
||||||
pos = _text_handle_pos_from_widget (handle, widget);
|
pos = _text_handle_pos_from_widget (handle, widget);
|
||||||
|
|
||||||
if (pos < 0)
|
if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
|
||||||
return FALSE;
|
priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
|
||||||
|
x -= gtk_widget_get_width (widget) / 2;
|
||||||
|
else if ((pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
|
||||||
|
priv->windows[pos].dir == GTK_TEXT_DIR_RTL) ||
|
||||||
|
(pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START &&
|
||||||
|
priv->windows[pos].dir != GTK_TEXT_DIR_RTL))
|
||||||
|
x -= gtk_widget_get_width (widget);
|
||||||
|
|
||||||
event_type = gdk_event_get_event_type (event);
|
y += priv->windows[pos].border.top / 2;
|
||||||
gdk_event_get_coords (event, &event_x, &event_y);
|
|
||||||
gdk_event_get_crossing_mode (event, &mode);
|
|
||||||
|
|
||||||
if (event_type == GDK_BUTTON_PRESS)
|
priv->windows[pos].dx = x;
|
||||||
{
|
priv->windows[pos].dy = y;
|
||||||
priv->windows[pos].dx = event_x;
|
priv->windows[pos].dragged = TRUE;
|
||||||
priv->windows[pos].dy = event_y;
|
gtk_text_handle_set_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
|
||||||
priv->windows[pos].dragged = TRUE;
|
g_signal_emit (handle, signals[DRAG_STARTED], 0, pos);
|
||||||
gtk_text_handle_set_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
|
|
||||||
g_signal_emit (handle, signals[DRAG_STARTED], 0, pos);
|
|
||||||
}
|
|
||||||
else if (event_type == GDK_BUTTON_RELEASE)
|
|
||||||
{
|
|
||||||
g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
|
|
||||||
priv->windows[pos].dragged = FALSE;
|
|
||||||
gtk_text_handle_unset_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
|
|
||||||
}
|
|
||||||
else if (event_type == GDK_ENTER_NOTIFY)
|
|
||||||
gtk_text_handle_set_state (handle, pos, GTK_STATE_FLAG_PRELIGHT);
|
|
||||||
else if (event_type == GDK_LEAVE_NOTIFY)
|
|
||||||
{
|
|
||||||
if (!priv->windows[pos].dragged &&
|
|
||||||
(mode == GDK_CROSSING_NORMAL ||
|
|
||||||
mode == GDK_CROSSING_UNGRAB))
|
|
||||||
gtk_text_handle_unset_state (handle, pos, GTK_STATE_FLAG_PRELIGHT);
|
|
||||||
}
|
|
||||||
else if (event_type == GDK_MOTION_NOTIFY &&
|
|
||||||
gdk_event_get_state (event, &state) &&
|
|
||||||
state & GDK_BUTTON1_MASK &&
|
|
||||||
priv->windows[pos].dragged)
|
|
||||||
{
|
|
||||||
gint x, y, handle_width, handle_height;
|
|
||||||
cairo_rectangle_int_t rect;
|
|
||||||
GtkAllocation allocation;
|
|
||||||
GtkWidget *window;
|
|
||||||
|
|
||||||
window = gtk_widget_get_parent (priv->windows[pos].widget);
|
|
||||||
gtk_widget_get_allocation (priv->windows[pos].widget, &allocation);
|
|
||||||
_gtk_text_handle_get_size (handle, pos, &handle_width, &handle_height);
|
|
||||||
|
|
||||||
_gtk_window_get_popover_position (GTK_WINDOW (window),
|
|
||||||
priv->windows[pos].widget,
|
|
||||||
NULL, &rect);
|
|
||||||
|
|
||||||
x = rect.x + event_x - priv->windows[pos].dx;
|
|
||||||
y = rect.y + event_y - priv->windows[pos].dy +
|
|
||||||
priv->windows[pos].border.top / 2;
|
|
||||||
|
|
||||||
if (pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
|
|
||||||
priv->mode == GTK_TEXT_HANDLE_MODE_CURSOR)
|
|
||||||
x += handle_width / 2;
|
|
||||||
else if ((pos == GTK_TEXT_HANDLE_POSITION_CURSOR &&
|
|
||||||
priv->windows[pos].dir == GTK_TEXT_DIR_RTL) ||
|
|
||||||
(pos == GTK_TEXT_HANDLE_POSITION_SELECTION_START &&
|
|
||||||
priv->windows[pos].dir != GTK_TEXT_DIR_RTL))
|
|
||||||
x += handle_width;
|
|
||||||
|
|
||||||
gtk_widget_translate_coordinates (window, priv->parent, x, y, &x, &y);
|
|
||||||
g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_text_handle_widget_style_updated (GtkWidget *widget,
|
handle_drag_update (GtkGestureDrag *gesture,
|
||||||
GtkTextHandle *handle)
|
gdouble offset_x,
|
||||||
|
gdouble offset_y,
|
||||||
|
GtkTextHandle *handle)
|
||||||
{
|
{
|
||||||
GtkTextHandlePrivate *priv;
|
GtkTextHandlePrivate *priv = handle->priv;
|
||||||
|
gdouble start_x, start_y;
|
||||||
|
gint pos, x, y;
|
||||||
|
|
||||||
priv = handle->priv;
|
pos = _text_handle_pos_from_widget (handle,
|
||||||
gtk_style_context_set_parent (gtk_widget_get_style_context (widget),
|
gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
|
||||||
gtk_widget_get_style_context (priv->parent));
|
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
|
||||||
|
|
||||||
_gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_START);
|
gtk_widget_translate_coordinates (priv->windows[pos].widget, priv->parent,
|
||||||
_gtk_text_handle_update (handle, GTK_TEXT_HANDLE_POSITION_SELECTION_END);
|
start_x + offset_x - priv->windows[pos].dx,
|
||||||
|
start_y + offset_y - priv->windows[pos].dy,
|
||||||
|
&x, &y);
|
||||||
|
g_signal_emit (handle, signals[HANDLE_DRAGGED], 0, pos, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_drag_end (GtkGestureDrag *gesture,
|
||||||
|
gdouble offset_x,
|
||||||
|
gdouble offset_y,
|
||||||
|
GtkTextHandle *handle)
|
||||||
|
{
|
||||||
|
GtkTextHandlePrivate *priv = handle->priv;
|
||||||
|
gint pos;
|
||||||
|
|
||||||
|
pos = _text_handle_pos_from_widget (handle,
|
||||||
|
gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)));
|
||||||
|
g_signal_emit (handle, signals[DRAG_FINISHED], 0, pos);
|
||||||
|
priv->windows[pos].dragged = FALSE;
|
||||||
|
gtk_text_handle_unset_state (handle, pos, GTK_STATE_FLAG_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
snapshot_func (GtkGizmo *gizmo,
|
||||||
|
GtkSnapshot *snapshot)
|
||||||
|
{
|
||||||
|
GtkCssStyle *style = gtk_css_node_get_style (gtk_widget_get_css_node (GTK_WIDGET (gizmo)));
|
||||||
|
|
||||||
|
gtk_css_style_snapshot_icon (style,
|
||||||
|
snapshot,
|
||||||
|
gtk_widget_get_width (GTK_WIDGET (gizmo)),
|
||||||
|
gtk_widget_get_height (GTK_WIDGET (gizmo)),
|
||||||
|
GTK_CSS_IMAGE_BUILTIN_HANDLE);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkWidget *
|
static GtkWidget *
|
||||||
@ -290,18 +231,21 @@ _gtk_text_handle_ensure_widget (GtkTextHandle *handle,
|
|||||||
{
|
{
|
||||||
GtkWidget *widget, *window;
|
GtkWidget *widget, *window;
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
|
GtkEventController *controller;
|
||||||
|
|
||||||
widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
widget = gtk_gizmo_new (I_("cursor-handle"),
|
||||||
|
NULL, NULL, snapshot_func);
|
||||||
|
|
||||||
gtk_widget_set_direction (widget, priv->windows[pos].dir);
|
gtk_widget_set_direction (widget, priv->windows[pos].dir);
|
||||||
|
|
||||||
g_signal_connect (widget, "draw",
|
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
|
||||||
G_CALLBACK (gtk_text_handle_widget_draw), handle);
|
g_signal_connect (controller, "drag-begin",
|
||||||
g_signal_connect (widget, "event",
|
G_CALLBACK (handle_drag_begin), handle);
|
||||||
G_CALLBACK (gtk_text_handle_widget_event), handle);
|
g_signal_connect (controller, "drag-update",
|
||||||
g_signal_connect (widget, "style-updated",
|
G_CALLBACK (handle_drag_update), handle);
|
||||||
G_CALLBACK (gtk_text_handle_widget_style_updated),
|
g_signal_connect (controller, "drag-end",
|
||||||
handle);
|
G_CALLBACK (handle_drag_end), handle);
|
||||||
|
gtk_widget_add_controller (widget, controller);
|
||||||
|
|
||||||
priv->windows[pos].widget = g_object_ref_sink (widget);
|
priv->windows[pos].widget = g_object_ref_sink (widget);
|
||||||
window = gtk_widget_get_ancestor (priv->parent, GTK_TYPE_WINDOW);
|
window = gtk_widget_get_ancestor (priv->parent, GTK_TYPE_WINDOW);
|
||||||
@ -309,7 +253,6 @@ _gtk_text_handle_ensure_widget (GtkTextHandle *handle,
|
|||||||
|
|
||||||
context = gtk_widget_get_style_context (widget);
|
context = gtk_widget_get_style_context (widget);
|
||||||
gtk_style_context_set_parent (context, gtk_widget_get_style_context (priv->parent));
|
gtk_style_context_set_parent (context, gtk_widget_get_style_context (priv->parent));
|
||||||
gtk_css_node_set_name (gtk_widget_get_css_node (widget), I_("cursor-handle"));
|
|
||||||
if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
|
if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
|
||||||
{
|
{
|
||||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
|
gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
|
||||||
|
@ -4420,6 +4420,12 @@ cursor-handle {
|
|||||||
background-image: none;
|
background-image: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-style: none;
|
border-style: none;
|
||||||
|
min-width: 20px;
|
||||||
|
min-height: 24px;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-top: 24px;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
|
||||||
@each $s,$as in ('',''),
|
@each $s,$as in ('',''),
|
||||||
(':hover','-hover'),
|
(':hover','-hover'),
|
||||||
@ -4428,14 +4434,12 @@ cursor-handle {
|
|||||||
$_url: 'assets/text-select-start#{$as}#{$asset_suffix}';
|
$_url: 'assets/text-select-start#{$as}#{$asset_suffix}';
|
||||||
-gtk-icon-source: -gtk-scaled(url('#{$_url}.png'),
|
-gtk-icon-source: -gtk-scaled(url('#{$_url}.png'),
|
||||||
url('#{$_url}@2.png'));
|
url('#{$_url}@2.png'));
|
||||||
padding-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.bottom#{$s}:dir(ltr), &.top#{$s}:dir(rtl) {
|
&.bottom#{$s}:dir(ltr), &.top#{$s}:dir(rtl) {
|
||||||
$_url: 'assets/text-select-end#{$as}#{$asset_suffix}';
|
$_url: 'assets/text-select-end#{$as}#{$asset_suffix}';
|
||||||
-gtk-icon-source: -gtk-scaled(url('#{$_url}.png'),
|
-gtk-icon-source: -gtk-scaled(url('#{$_url}.png'),
|
||||||
url('#{$_url}@2.png'));
|
url('#{$_url}@2.png'));
|
||||||
padding-right: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.insertion-cursor#{$s}:dir(ltr), &.insertion-cursor#{$s}:dir(rtl) {
|
&.insertion-cursor#{$s}:dir(ltr), &.insertion-cursor#{$s}:dir(rtl) {
|
||||||
|
Loading…
Reference in New Issue
Block a user