clutter/actor: Save key-focus state and unset it before destruction

When clutter actors with key focus are destroyed we emit ::key-focus-out on
them just after their destruction. This is against our assumption that no
signal should be emitted after "::destroy" (see GNOME/mutter!769 [1]), and
in fact could cause the shell to do actions that we won't ever stop on
destroy callback.

To avoid this to happen, use a private function to set its key-state (so we
can avoid looking for the stage) and emit ::key-focus-in/out events and use
this value in both clutter_actor_has_key_focus(),
clutter_actor_grab_key_focus() and on unmap and destruction to unset the
stage key focus before we emit the ::destroy signal.

As result of this, we can now avoid to unset the key focus on actor
destruction in the stage.

[1] https://gitlab.gnome.org/GNOME/mutter/merge_requests/769

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1704


(cherry picked from commit c2d03bf73e)
This commit is contained in:
Marco Trevisan (Treviño) 2019-10-10 16:11:20 +00:00 committed by Jonas Ådahl
parent b6ed7d6781
commit a22731e2b2
3 changed files with 48 additions and 43 deletions

View File

@ -274,6 +274,9 @@ void _clutter_actor_set_enable_paint_unmapped
void _clutter_actor_set_has_pointer (ClutterActor *self,
gboolean has_pointer);
void _clutter_actor_set_has_key_focus (ClutterActor *self,
gboolean has_key_focus);
void _clutter_actor_queue_redraw_with_clip (ClutterActor *self,
ClutterRedrawFlags flags,
const ClutterPaintVolume *clip_volume);

View File

@ -835,6 +835,7 @@ struct _ClutterActorPrivate
guint enable_model_view_transform : 1;
guint enable_paint_unmapped : 1;
guint has_pointer : 1;
guint has_key_focus : 1;
guint propagated_one_redraw : 1;
guint paint_volume_valid : 1;
guint last_paint_volume_valid : 1;
@ -1691,6 +1692,20 @@ clutter_actor_is_mapped (ClutterActor *self)
return CLUTTER_ACTOR_IS_MAPPED (self);
}
static void
maybe_unset_key_focus (ClutterActor *self)
{
ClutterActor *stage;
if (!self->priv->has_key_focus)
return;
stage = _clutter_actor_get_stage_internal (self);
if (stage)
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL);
}
static void
clutter_actor_real_unmap (ClutterActor *self)
{
@ -1724,17 +1739,7 @@ clutter_actor_real_unmap (ClutterActor *self)
/* relinquish keyboard focus if we were unmapped while owning it */
if (!CLUTTER_ACTOR_IS_TOPLEVEL (self))
{
ClutterStage *stage;
stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
if (stage != NULL &&
clutter_stage_get_key_focus (stage) == self)
{
clutter_stage_set_key_focus (stage, NULL);
}
}
maybe_unset_key_focus (self);
}
/**
@ -6064,6 +6069,8 @@ clutter_actor_dispose (GObject *object)
object->ref_count,
g_type_name (G_OBJECT_TYPE (self)));
maybe_unset_key_focus (self);
/* Stop the emission of any property change */
g_object_freeze_notify (object);
@ -15979,6 +15986,9 @@ clutter_actor_grab_key_focus (ClutterActor *self)
g_return_if_fail (CLUTTER_IS_ACTOR (self));
if (self->priv->has_key_focus)
return;
stage = _clutter_actor_get_stage_internal (self);
if (stage != NULL)
clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self);
@ -16768,6 +16778,23 @@ _clutter_actor_set_has_pointer (ClutterActor *self,
}
}
void
_clutter_actor_set_has_key_focus (ClutterActor *self,
gboolean has_key_focus)
{
ClutterActorPrivate *priv = self->priv;
if (priv->has_key_focus != has_key_focus)
{
priv->has_key_focus = has_key_focus;
if (has_key_focus)
g_signal_emit (self, actor_signals[KEY_FOCUS_IN], 0);
else
g_signal_emit (self, actor_signals[KEY_FOCUS_OUT], 0);
}
}
/**
* clutter_actor_get_text_direction:
* @self: a #ClutterActor
@ -17616,15 +17643,9 @@ clutter_actor_clear_effects (ClutterActor *self)
gboolean
clutter_actor_has_key_focus (ClutterActor *self)
{
ClutterActor *stage;
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
stage = _clutter_actor_get_stage_internal (self);
if (stage == NULL)
return FALSE;
return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self;
return self->priv->has_key_focus;
}
static gboolean

View File

@ -1066,10 +1066,7 @@ clutter_stage_emit_key_focus_event (ClutterStage *stage,
if (priv->key_focused_actor == NULL)
return;
if (focus_in)
g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
else
g_signal_emit_by_name (priv->key_focused_actor, "key-focus-out");
_clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), focus_in);
g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_KEY_FOCUS]);
}
@ -3011,14 +3008,6 @@ clutter_stage_get_title (ClutterStage *stage)
return stage->priv->title;
}
static void
on_key_focus_destroy (ClutterActor *actor,
ClutterStage *stage)
{
/* unset the key focus */
clutter_stage_set_key_focus (stage, NULL);
}
/**
* clutter_stage_set_key_focus:
* @stage: the #ClutterStage
@ -3058,18 +3047,14 @@ clutter_stage_set_key_focus (ClutterStage *stage,
old_focused_actor = priv->key_focused_actor;
/* set key_focused_actor to NULL before emitting the signal or someone
* might hide the previously focused actor in the signal handler and we'd
* get re-entrant call and get glib critical from g_object_weak_unref
* might hide the previously focused actor in the signal handler
*/
g_signal_handlers_disconnect_by_func (priv->key_focused_actor,
G_CALLBACK (on_key_focus_destroy),
stage);
priv->key_focused_actor = NULL;
g_signal_emit_by_name (old_focused_actor, "key-focus-out");
_clutter_actor_set_has_key_focus (old_focused_actor, FALSE);
}
else
g_signal_emit_by_name (stage, "key-focus-out");
_clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), FALSE);
/* Note, if someone changes key focus in focus-out signal handler we'd be
* overriding the latter call below moving the focus where it was originally
@ -3079,14 +3064,10 @@ clutter_stage_set_key_focus (ClutterStage *stage,
if (actor != NULL)
{
priv->key_focused_actor = actor;
g_signal_connect (actor,
"destroy", G_CALLBACK (on_key_focus_destroy),
stage);
g_signal_emit_by_name (priv->key_focused_actor, "key-focus-in");
_clutter_actor_set_has_key_focus (actor, FALSE);
}
else
g_signal_emit_by_name (stage, "key-focus-in");
_clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), TRUE);
g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_KEY_FOCUS]);
}