switch: Respect text direction
authorSergey Bugaev <bugaevc@gmail.com>
Thu, 14 Sep 2023 16:04:24 +0000 (19:04 +0300)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 19 Sep 2023 20:32:27 +0000 (16:32 -0400)
In RTL, we want the active state to mean the handle is on the left.

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/1489

Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
gtk/gtkswitch.c

index 7193d8b7c531889875dfc3bb5bde74e58079aa10..e4e36ddeb34dfe569959ced4ae4cb278f4407210 100644 (file)
@@ -140,6 +140,16 @@ G_DEFINE_TYPE_WITH_CODE (GtkSwitch, gtk_switch, GTK_TYPE_WIDGET,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE,
                                                 gtk_switch_actionable_iface_init))
 
+static gboolean
+is_right_side (GtkWidget *widget,
+               gboolean   active)
+{
+  if (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+    return active;
+  else
+    return !active;
+}
+
 static void
 gtk_switch_end_toggle_animation (GtkSwitch *self)
 {
@@ -156,16 +166,18 @@ gtk_switch_on_frame_clock_update (GtkWidget     *widget,
                                   gpointer       user_data)
 {
   GtkSwitch *self = GTK_SWITCH (widget);
+  double progress;
 
   gtk_progress_tracker_advance_frame (&self->tracker,
                                       gdk_frame_clock_get_frame_time (clock));
 
   if (gtk_progress_tracker_get_state (&self->tracker) != GTK_PROGRESS_STATE_AFTER)
     {
-      if (self->is_active)
-        self->handle_pos = 1.0 - gtk_progress_tracker_get_ease_out_cubic (&self->tracker, FALSE);
+      progress = gtk_progress_tracker_get_ease_out_cubic (&self->tracker, FALSE);
+      if (is_right_side (widget, self->is_active))
+        self->handle_pos = 1.0 - progress;
       else
-        self->handle_pos = gtk_progress_tracker_get_ease_out_cubic (&self->tracker, FALSE);
+        self->handle_pos = progress;
     }
   else
     {
@@ -251,7 +263,7 @@ gtk_switch_pan_gesture_pan (GtkGesturePan   *gesture,
 
   gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
 
-  if (self->is_active)
+  if (is_right_side (widget, self->is_active))
     offset += width / 2;
 
   offset /= width / 2;
@@ -268,6 +280,7 @@ gtk_switch_pan_gesture_drag_end (GtkGestureDrag *gesture,
                                  double          y,
                                  GtkSwitch      *self)
 {
+  GtkWidget *widget = GTK_WIDGET (self);
   GdkEventSequence *sequence;
   gboolean active;
 
@@ -278,16 +291,19 @@ gtk_switch_pan_gesture_drag_end (GtkGestureDrag *gesture,
       /* if half the handle passed the middle of the switch, then we
        * consider it to be on
        */
-      active = self->handle_pos >= 0.5;
+      if (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+        active = self->handle_pos >= 0.5;
+      else
+        active = self->handle_pos <= 0.5;
     }
   else if (!gtk_gesture_handles_sequence (self->click_gesture, sequence))
     active = self->is_active;
   else
     return;
 
-  self->handle_pos = active ? 1.0 : 0.0;
+  self->handle_pos = is_right_side (widget, active) ? 1.0 : 0.0;
   gtk_switch_set_active (self, active);
-  gtk_widget_queue_allocate (GTK_WIDGET (self));
+  gtk_widget_queue_allocate (widget);
 }
 
 static void
@@ -354,6 +370,8 @@ gtk_switch_allocate (GtkWidget *widget,
   /* Center ON icon in left half */
   gtk_widget_measure (self->on_image, GTK_ORIENTATION_HORIZONTAL, -1, &min, NULL, NULL, NULL);
   child_alloc.x = ((width / 2) - min) / 2;
+  if (is_right_side (widget, FALSE))
+    child_alloc.x += width / 2;
   child_alloc.width = min;
   gtk_widget_measure (self->on_image, GTK_ORIENTATION_VERTICAL, min, &min, NULL, NULL, NULL);
   child_alloc.y = (height - min) / 2;
@@ -362,7 +380,9 @@ gtk_switch_allocate (GtkWidget *widget,
 
   /* Center OFF icon in right half */
   gtk_widget_measure (self->off_image, GTK_ORIENTATION_HORIZONTAL, -1, &min, NULL, NULL, NULL);
-  child_alloc.x = (width / 2) + ((width / 2) - min) / 2;
+  child_alloc.x = ((width / 2) - min) / 2;
+  if (is_right_side (widget, TRUE))
+    child_alloc.x += width / 2;
   child_alloc.width = min;
   gtk_widget_measure (self->off_image, GTK_ORIENTATION_VERTICAL, min, &min, NULL, NULL, NULL);
   child_alloc.y = (height - min) / 2;
@@ -370,6 +390,18 @@ gtk_switch_allocate (GtkWidget *widget,
   gtk_widget_size_allocate (self->off_image, &child_alloc, -1);
 }
 
+static void
+gtk_switch_direction_changed (GtkWidget       *widget,
+                              GtkTextDirection previous_dir)
+{
+  GtkSwitch *self = GTK_SWITCH (widget);
+
+  self->handle_pos = 1.0 - self->handle_pos;
+  gtk_widget_queue_allocate (widget);
+
+  GTK_WIDGET_CLASS (gtk_switch_parent_class)->direction_changed (widget, previous_dir);
+}
+
 static void
 gtk_switch_set_action_name (GtkActionable *actionable,
                             const char    *action_name)
@@ -552,6 +584,8 @@ gtk_switch_class_init (GtkSwitchClass *klass)
 
   g_object_class_install_properties (gobject_class, LAST_PROP, switch_props);
 
+  widget_class->direction_changed = gtk_switch_direction_changed;
+
   klass->activate = gtk_switch_activate;
   klass->state_set = state_set;
 
@@ -676,6 +710,10 @@ gtk_switch_init (GtkSwitch *self)
   gtk_accessible_update_state (GTK_ACCESSIBLE (self),
                                GTK_ACCESSIBLE_STATE_CHECKED, FALSE,
                                -1);
+  if (is_right_side (GTK_WIDGET (self), FALSE))
+    self->handle_pos = 1.0;
+  else
+    self->handle_pos = 0.0;
 }
 
 /**
@@ -714,7 +752,7 @@ gtk_switch_set_active (GtkSwitch *self,
 
       self->is_active = is_active;
 
-      if (self->is_active)
+      if (is_right_side (GTK_WIDGET (self), self->is_active))
         self->handle_pos = 1.0;
       else
         self->handle_pos = 0.0;