lib: remove use of GtkPicture

We want a bit more control over things than we can get with GtkPicture so
do the snapshot/measure internally. This allows us to know where the
picture is to be allocated so that we can translate pointer events to the
location within the console.
This commit is contained in:
Christian Hergert 2023-02-15 06:47:10 -08:00
parent b626904171
commit a8505e1245
3 changed files with 232 additions and 83 deletions

View File

@ -37,10 +37,10 @@ typedef struct
*/ */
MksScreen *screen; MksScreen *screen;
/* The picture widget in our template which is rendering the screens /* The paintable containing the screen content */
* painable when available. GdkPaintable *paintable;
*/ gulong invalidate_contents_handler;
GtkPicture *picture; gulong invalidate_size_handler;
/* Used to update the cursor position by calling into the MksMouse /* Used to update the cursor position by calling into the MksMouse
* API using move_to/move_by. * API using move_to/move_by.
@ -67,7 +67,6 @@ typedef struct
enum { enum {
PROP_0, PROP_0,
PROP_CONTENT_FIT,
PROP_SCREEN, PROP_SCREEN,
N_PROPS N_PROPS
}; };
@ -76,6 +75,98 @@ G_DEFINE_TYPE_WITH_PRIVATE (MksDisplay, mks_display, GTK_TYPE_WIDGET)
static GParamSpec *properties [N_PROPS]; static GParamSpec *properties [N_PROPS];
static void
mks_display_get_paintable_area (MksDisplay *self,
graphene_rect_t *area)
{
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
int x, y, width, height;
double display_ratio;
double ratio;
double w, h;
g_assert (MKS_IS_DISPLAY (self));
g_assert (area != NULL);
width = gtk_widget_get_width (GTK_WIDGET (self));
height = gtk_widget_get_height (GTK_WIDGET (self));
display_ratio = (double)width / (double)height;
ratio = gdk_paintable_get_intrinsic_aspect_ratio (priv->paintable);
if (ratio > display_ratio)
{
w = width;
h = width / ratio;
}
else
{
w = height * ratio;
h = height;
}
x = (width - ceil (w)) / 2;
y = floor(height - ceil (h)) / 2;
*area = GRAPHENE_RECT_INIT (x, y, w, h);
}
static void
mks_display_invalidate_contents_cb (MksDisplay *self,
GdkPaintable *paintable)
{
g_assert (MKS_IS_DISPLAY (self));
g_assert (GDK_IS_PAINTABLE (paintable));
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
mks_display_invalidate_size_cb (MksDisplay *self,
GdkPaintable *paintable)
{
g_assert (MKS_IS_DISPLAY (self));
g_assert (GDK_IS_PAINTABLE (paintable));
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
mks_display_set_paintable (MksDisplay *self,
GdkPaintable *paintable)
{
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
g_assert (MKS_IS_DISPLAY (self));
g_assert (!paintable || GDK_IS_PAINTABLE (paintable));
if (priv->paintable == paintable)
return;
if (priv->paintable != NULL)
{
g_clear_signal_handler (&priv->invalidate_contents_handler, priv->paintable);
g_clear_signal_handler (&priv->invalidate_size_handler, priv->paintable);
g_clear_object (&priv->paintable);
}
if (paintable != NULL)
{
priv->paintable = g_object_ref (paintable);
priv->invalidate_contents_handler =
g_signal_connect_object (paintable,
"invalidate-contents",
G_CALLBACK (mks_display_invalidate_contents_cb),
self,
G_CONNECT_SWAPPED);
priv->invalidate_size_handler =
g_signal_connect_object (paintable,
"invalidate-size",
G_CALLBACK (mks_display_invalidate_size_cb),
self,
G_CONNECT_SWAPPED);
}
}
static void static void
mks_display_translate_keycode (MksDisplay *self, mks_display_translate_keycode (MksDisplay *self,
guint keyval, guint keyval,
@ -178,13 +269,36 @@ mks_display_key_key_released_cb (MksDisplay *self,
g_object_ref (self)); g_object_ref (self));
} }
static void static gboolean
mks_display_translate_coordinate (MksDisplay *self, mks_display_translate_coordinate (MksDisplay *self,
double *x, double *x,
double *y) double *y)
{ {
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
graphene_rect_t area;
int width;
int height;
g_assert (MKS_IS_DISPLAY (self)); g_assert (MKS_IS_DISPLAY (self));
if (priv->paintable == NULL)
return FALSE;
mks_display_get_paintable_area (self, &area);
if (!graphene_rect_contains_point (&area, &GRAPHENE_POINT_INIT (*x, *y)))
return FALSE;
*x -= area.origin.x;
*y -= area.origin.y;
width = gdk_paintable_get_intrinsic_width (priv->paintable);
height = gdk_paintable_get_intrinsic_height (priv->paintable);
*x = *x / area.size.width * width;
*y = *y / area.size.height * height;
return TRUE;
} }
static void static void
@ -276,8 +390,8 @@ mks_display_motion_enter_cb (MksDisplay *self,
g_assert (MKS_IS_DISPLAY (self)); g_assert (MKS_IS_DISPLAY (self));
g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion)); g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
mks_display_translate_coordinate (self, &x, &y); if (mks_display_translate_coordinate (self, &x, &y))
mks_display_motion (self, x, y); mks_display_motion (self, x, y);
} }
static void static void
@ -289,8 +403,8 @@ mks_display_motion_motion_cb (MksDisplay *self,
g_assert (MKS_IS_DISPLAY (self)); g_assert (MKS_IS_DISPLAY (self));
g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion)); g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
mks_display_translate_coordinate (self, &x, &y); if (mks_display_translate_coordinate (self, &x, &y))
mks_display_motion (self, x, y); mks_display_motion (self, x, y);
} }
static void static void
@ -429,7 +543,7 @@ mks_display_attach_cb (GObject *object,
if (priv->screen != screen) if (priv->screen != screen)
return; return;
gtk_picture_set_paintable (priv->picture, paintable); mks_display_set_paintable (self, paintable);
} }
static void static void
@ -457,7 +571,8 @@ mks_display_disconnect (MksDisplay *self)
g_assert (MKS_IS_DISPLAY (self)); g_assert (MKS_IS_DISPLAY (self));
g_assert (priv->screen != NULL); g_assert (priv->screen != NULL);
gtk_picture_set_paintable (priv->picture, NULL); mks_display_set_paintable (self, NULL);
g_clear_object (&priv->screen); g_clear_object (&priv->screen);
} }
@ -470,11 +585,88 @@ mks_display_dispose (GObject *object)
if (priv->screen != NULL) if (priv->screen != NULL)
mks_display_disconnect (self); mks_display_disconnect (self);
g_clear_pointer ((GtkWidget **)&priv->picture, gtk_widget_unparent);
G_OBJECT_CLASS (mks_display_parent_class)->dispose (object); G_OBJECT_CLASS (mks_display_parent_class)->dispose (object);
} }
static void
mks_display_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
MksDisplay *self = (MksDisplay *)widget;
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
graphene_rect_t area;
if (priv->paintable == NULL)
return;
mks_display_get_paintable_area (self, &area);
gtk_snapshot_save (snapshot);
gtk_snapshot_translate (snapshot, &area.origin);
gdk_paintable_snapshot (priv->paintable, snapshot, area.size.width, area.size.height);
gtk_snapshot_restore (snapshot);
}
static GtkSizeRequestMode
mks_display_get_request_mode (GtkWidget *widget)
{
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}
static void
mks_display_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
MksDisplay *self = (MksDisplay *)widget;
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
double min_width, min_height, nat_width, nat_height;
int default_width;
int default_height;
g_assert (MKS_IS_DISPLAY (self));
if (priv->paintable == NULL || for_size == 0)
{
*minimum = 0;
*natural = 0;
return;
}
default_width = gdk_paintable_get_intrinsic_width (priv->paintable);
default_height = gdk_paintable_get_intrinsic_width (priv->paintable);
gdk_paintable_compute_concrete_size (priv->paintable,
0, 0,
default_width, default_height,
&min_width, &min_height);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
gdk_paintable_compute_concrete_size (priv->paintable,
0,
for_size < 0 ? 0 : for_size,
default_width, default_height,
&nat_width, &nat_height);
*minimum = ceil (min_width);
*natural = ceil (nat_width);
}
else
{
gdk_paintable_compute_concrete_size (priv->paintable,
for_size < 0 ? 0 : for_size,
0,
default_width, default_height,
&nat_width, &nat_height);
*minimum = ceil (min_height);
*natural = ceil (nat_height);
}
}
static void static void
mks_display_get_property (GObject *object, mks_display_get_property (GObject *object,
guint prop_id, guint prop_id,
@ -485,10 +677,6 @@ mks_display_get_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_CONTENT_FIT:
g_value_set_enum (value, mks_display_get_content_fit (self));
break;
case PROP_SCREEN: case PROP_SCREEN:
g_value_set_object (value, mks_display_get_screen (self)); g_value_set_object (value, mks_display_get_screen (self));
break; break;
@ -508,10 +696,6 @@ mks_display_set_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_CONTENT_FIT:
mks_display_set_content_fit (self, g_value_get_enum (value));
break;
case PROP_SCREEN: case PROP_SCREEN:
mks_display_set_screen (self, g_value_get_object (value)); mks_display_set_screen (self, g_value_get_object (value));
break; break;
@ -531,11 +715,9 @@ mks_display_class_init (MksDisplayClass *klass)
object_class->get_property = mks_display_get_property; object_class->get_property = mks_display_get_property;
object_class->set_property = mks_display_set_property; object_class->set_property = mks_display_set_property;
properties [PROP_CONTENT_FIT] = widget_class->get_request_mode = mks_display_get_request_mode;
g_param_spec_enum ("content-fit", NULL, NULL, widget_class->measure = mks_display_measure;
GTK_TYPE_CONTENT_FIT, widget_class->snapshot = mks_display_snapshot;
GTK_CONTENT_FIT_SCALE_DOWN,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties[PROP_SCREEN] = properties[PROP_SCREEN] =
g_param_spec_object ("screen", NULL, NULL, g_param_spec_object ("screen", NULL, NULL,
@ -550,7 +732,6 @@ mks_display_class_init (MksDisplayClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, click); gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, click);
gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, key); gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, key);
gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, motion); gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, motion);
gtk_widget_class_bind_template_child_private (widget_class, MksDisplay, picture);
gtk_widget_class_bind_template_callback (widget_class, mks_display_click_pressed_cb); gtk_widget_class_bind_template_callback (widget_class, mks_display_click_pressed_cb);
gtk_widget_class_bind_template_callback (widget_class, mks_display_click_released_cb); gtk_widget_class_bind_template_callback (widget_class, mks_display_click_released_cb);
gtk_widget_class_bind_template_callback (widget_class, mks_display_motion_enter_cb); gtk_widget_class_bind_template_callback (widget_class, mks_display_motion_enter_cb);
@ -611,24 +792,3 @@ mks_display_set_screen (MksDisplay *self,
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCREEN]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCREEN]);
} }
GtkContentFit
mks_display_get_content_fit (MksDisplay *self)
{
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
g_return_val_if_fail (MKS_IS_DISPLAY (self), 0);
return gtk_picture_get_content_fit (priv->picture);
}
void
mks_display_set_content_fit (MksDisplay *self,
GtkContentFit content_fit)
{
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
g_return_if_fail (MKS_IS_DISPLAY (self));
gtk_picture_set_content_fit (priv->picture, content_fit);
}

View File

@ -41,16 +41,11 @@ struct _MksDisplayClass
}; };
MKS_AVAILABLE_IN_ALL MKS_AVAILABLE_IN_ALL
GtkWidget *mks_display_new (void); GtkWidget *mks_display_new (void);
MKS_AVAILABLE_IN_ALL MKS_AVAILABLE_IN_ALL
MksScreen *mks_display_get_screen (MksDisplay *self); MksScreen *mks_display_get_screen (MksDisplay *self);
MKS_AVAILABLE_IN_ALL MKS_AVAILABLE_IN_ALL
void mks_display_set_screen (MksDisplay *self, void mks_display_set_screen (MksDisplay *self,
MksScreen *screen); MksScreen *screen);
MKS_AVAILABLE_IN_ALL
GtkContentFit mks_display_get_content_fit (MksDisplay *self);
MKS_AVAILABLE_IN_ALL
void mks_display_set_content_fit (MksDisplay *self,
GtkContentFit content_fit);
G_END_DECLS G_END_DECLS

View File

@ -1,32 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="MksDisplay" parent="GtkWidget"> <template class="MksDisplay" parent="GtkWidget">
<property name="focusable">false</property> <property name="focusable">true</property>
<child> <child>
<object class="GtkPicture" id="picture"> <object class="GtkEventControllerMotion" id="motion">
<property name="content-fit">scale-down</property> <signal name="enter" handler="mks_display_motion_enter_cb" swapped="true"/>
<property name="focusable">true</property> <signal name="leave" handler="mks_display_motion_leave_cb" swapped="true"/>
<child> <signal name="motion" handler="mks_display_motion_motion_cb" swapped="true"/>
<object class="GtkEventControllerMotion" id="motion"> </object>
<signal name="enter" handler="mks_display_motion_enter_cb" swapped="true"/> </child>
<signal name="leave" handler="mks_display_motion_leave_cb" swapped="true"/> <child>
<signal name="motion" handler="mks_display_motion_motion_cb" swapped="true"/> <object class="GtkGestureClick" id="click">
</object> <property name="button">0</property>
</child> <property name="exclusive">true</property>
<child> <signal name="pressed" handler="mks_display_click_pressed_cb" swapped="true"/>
<object class="GtkGestureClick" id="click"> <signal name="released" handler="mks_display_click_released_cb" swapped="true"/>
<property name="button">0</property> </object>
<property name="exclusive">true</property> </child>
<signal name="pressed" handler="mks_display_click_pressed_cb" swapped="true"/> <child>
<signal name="released" handler="mks_display_click_released_cb" swapped="true"/> <object class="GtkEventControllerKey" id="key">
</object> <signal name="key-pressed" handler="mks_display_key_key_pressed_cb" swapped="true"/>
</child> <signal name="key-released" handler="mks_display_key_key_released_cb" swapped="true"/>
<child>
<object class="GtkEventControllerKey" id="key">
<signal name="key-pressed" handler="mks_display_key_key_pressed_cb" swapped="true"/>
<signal name="key-released" handler="mks_display_key_key_released_cb" swapped="true"/>
</object>
</child>
</object> </object>
</child> </child>
</template> </template>