1 /* GTK - The GIMP Toolkit
3 * Copyright (C) 2010 Intel Corporation
4 * Copyright (C) 2010 RedHat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 * Emmanuele Bassi <ebassi@linux.intel.com>
23 * Matthias Clasen <mclasen@redhat.com>
25 * Based on similar code from Mx.
30 * @Short_Description: A "light switch" style toggle
32 * @See_Also: #GtkToggleButton
34 * #GtkSwitch is a widget that has two states: on or off. The user can control
35 * which state should be active by clicking the empty area, or by dragging the
41 #include "gtkswitch.h"
43 #include "gtkactivatable.h"
45 #include "gtkprivate.h"
46 #include "gtktoggleaction.h"
47 #include "gtkwidget.h"
48 #include "gtkmarshalers.h"
49 #include "gtkapplicationprivate.h"
50 #include "gtkactionable.h"
51 #include "a11y/gtkswitchaccessible.h"
55 #define DEFAULT_SLIDER_WIDTH (36)
56 #define DEFAULT_SLIDER_HEIGHT (22)
58 struct _GtkSwitchPrivate
60 GdkWindow *event_window;
64 GVariant *action_target;
65 GSimpleActionObserver *action_observer;
73 guint is_dragging : 1;
76 guint use_action_appearance : 1;
84 PROP_USE_ACTION_APPEARANCE,
96 static guint signals[LAST_SIGNAL] = { 0 };
98 static GParamSpec *switch_props[LAST_PROP] = { NULL, };
100 static void gtk_switch_hierarchy_changed (GtkWidget *widget,
101 GtkWidget *previous_toplevel);
103 static void gtk_switch_actionable_iface_init (GtkActionableInterface *iface);
104 static void gtk_switch_activatable_interface_init (GtkActivatableIface *iface);
106 G_DEFINE_TYPE_WITH_CODE (GtkSwitch, gtk_switch, GTK_TYPE_WIDGET,
107 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE,
108 gtk_switch_actionable_iface_init)
109 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
110 gtk_switch_activatable_interface_init));
113 gtk_switch_button_press (GtkWidget *widget,
114 GdkEventButton *event)
116 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
117 GtkAllocation allocation;
119 gtk_widget_get_allocation (widget, &allocation);
123 /* if the event occurred in the "off" area, then this
126 if (event->x <= allocation.width / 2)
128 priv->in_press = TRUE;
132 priv->offset = event->x - allocation.width / 2;
136 /* if the event occurred in the "on" area, then this
139 if (event->x >= allocation.width / 2)
141 priv->in_press = TRUE;
145 priv->offset = event->x;
148 priv->drag_start = event->x;
150 g_object_get (gtk_widget_get_settings (widget),
151 "gtk-dnd-drag-threshold", &priv->drag_threshold,
158 gtk_switch_motion (GtkWidget *widget,
159 GdkEventMotion *event)
161 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
163 /* if this is a direct toggle we don't handle motion */
167 if (ABS (event->x - priv->drag_start) < priv->drag_threshold)
170 if (event->state & GDK_BUTTON1_MASK)
172 gint position = event->x - priv->offset;
173 GtkAllocation allocation;
174 GtkStyleContext *context;
177 gint width, focus_width, focus_pad;
179 gtk_widget_style_get (widget,
180 "focus-line-width", &focus_width,
181 "focus-padding", &focus_pad,
184 context = gtk_widget_get_style_context (widget);
185 state = gtk_widget_get_state_flags (widget);
187 gtk_style_context_save (context);
188 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
189 gtk_style_context_get_padding (context, state, &padding);
190 gtk_style_context_restore (context);
192 gtk_widget_get_allocation (widget, &allocation);
194 width = allocation.width - 2 * (focus_width + focus_pad);
196 /* constrain the handle within the trough width */
197 if (position > (width / 2) - padding.right)
198 priv->handle_x = width / 2 - padding.right;
199 else if (position < padding.left)
202 priv->handle_x = position;
204 priv->is_dragging = TRUE;
206 /* we need to redraw the handle */
207 gtk_widget_queue_draw (widget);
216 gtk_switch_button_release (GtkWidget *widget,
217 GdkEventButton *event)
219 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
220 GtkAllocation allocation;
222 gtk_widget_get_allocation (widget, &allocation);
224 /* dragged toggles have a "soft" grab, so we don't care whether we
225 * are in the switch or not when the button is released; we do care
226 * for direct toggles, instead
228 if (!priv->is_dragging && !priv->in_switch)
234 priv->in_press = FALSE;
235 gtk_switch_set_active (GTK_SWITCH (widget), !priv->is_active);
240 /* toggle the switch if the handle was clicked but a drag had not been
242 if (!priv->is_dragging && !priv->in_press)
244 gtk_switch_set_active (GTK_SWITCH (widget), !priv->is_active);
250 if (priv->is_dragging)
252 priv->is_dragging = FALSE;
254 /* if half the handle passed the middle of the switch, then we
255 * consider it to be on
257 if ((priv->handle_x + (allocation.width / 4)) >= (allocation.width / 2))
259 gtk_switch_set_active (GTK_SWITCH (widget), TRUE);
260 priv->handle_x = allocation.width / 2;
264 gtk_switch_set_active (GTK_SWITCH (widget), FALSE);
268 gtk_widget_queue_draw (widget);
277 gtk_switch_enter (GtkWidget *widget,
278 GdkEventCrossing *event)
280 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
282 if (event->window == priv->event_window)
283 priv->in_switch = TRUE;
289 gtk_switch_leave (GtkWidget *widget,
290 GdkEventCrossing *event)
292 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
294 if (event->window == priv->event_window)
295 priv->in_switch = FALSE;
301 gtk_switch_activate (GtkSwitch *sw)
303 GtkSwitchPrivate *priv = sw->priv;
305 gtk_switch_set_active (sw, !priv->is_active);
309 gtk_switch_get_preferred_width (GtkWidget *widget,
313 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
314 GtkStyleContext *context;
317 gint width, slider_width, focus_width, focus_pad;
319 PangoRectangle logical_rect;
321 context = gtk_widget_get_style_context (widget);
322 state = gtk_widget_get_state_flags (widget);
325 state |= GTK_STATE_FLAG_ACTIVE;
327 gtk_style_context_save (context);
329 gtk_style_context_set_state (context, state);
330 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
331 gtk_style_context_get_padding (context, state, &padding);
333 width = padding.left + padding.right;
335 gtk_style_context_restore (context);
337 gtk_widget_style_get (widget,
338 "slider-width", &slider_width,
339 "focus-line-width", &focus_width,
340 "focus-padding", &focus_pad,
343 width += 2 * (focus_width + focus_pad);
345 /* Translators: if the "on" state label requires more than three
346 * glyphs then use MEDIUM VERTICAL BAR (U+2759) as the text for
349 layout = gtk_widget_create_pango_layout (widget, C_("switch", "ON"));
350 pango_layout_get_extents (layout, NULL, &logical_rect);
351 pango_extents_to_pixels (&logical_rect, NULL);
352 width += MAX (logical_rect.width, slider_width);
354 /* Translators: if the "off" state label requires more than three
355 * glyphs then use WHITE CIRCLE (U+25CB) as the text for the state
357 pango_layout_set_text (layout, C_("switch", "OFF"), -1);
358 pango_layout_get_extents (layout, NULL, &logical_rect);
359 pango_extents_to_pixels (&logical_rect, NULL);
360 width += MAX (logical_rect.width, slider_width);
362 g_object_unref (layout);
372 gtk_switch_get_preferred_height (GtkWidget *widget,
376 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
377 GtkStyleContext *context;
380 gint height, focus_width, focus_pad;
382 PangoRectangle logical_rect;
385 context = gtk_widget_get_style_context (widget);
386 state = gtk_widget_get_state_flags (widget);
389 state |= GTK_STATE_FLAG_ACTIVE;
391 gtk_style_context_save (context);
393 gtk_style_context_set_state (context, state);
394 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
395 gtk_style_context_get_padding (context, state, &padding);
397 height = padding.top + padding.bottom;
399 gtk_style_context_restore (context);
401 gtk_widget_style_get (widget,
402 "focus-line-width", &focus_width,
403 "focus-padding", &focus_pad,
406 height += 2 * (focus_width + focus_pad);
408 str = g_strdup_printf ("%s%s",
410 C_("switch", "OFF"));
412 layout = gtk_widget_create_pango_layout (widget, str);
413 pango_layout_get_extents (layout, NULL, &logical_rect);
414 pango_extents_to_pixels (&logical_rect, NULL);
415 height += MAX (DEFAULT_SLIDER_HEIGHT, logical_rect.height);
417 g_object_unref (layout);
428 gtk_switch_size_allocate (GtkWidget *widget,
429 GtkAllocation *allocation)
431 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
433 gtk_widget_set_allocation (widget, allocation);
435 if (gtk_widget_get_realized (widget))
436 gdk_window_move_resize (priv->event_window,
444 gtk_switch_realize (GtkWidget *widget)
446 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
447 GdkWindow *parent_window;
448 GdkWindowAttr attributes;
449 gint attributes_mask;
450 GtkAllocation allocation;
452 gtk_widget_set_realized (widget, TRUE);
453 parent_window = gtk_widget_get_parent_window (widget);
454 gtk_widget_set_window (widget, parent_window);
455 g_object_ref (parent_window);
457 gtk_widget_get_allocation (widget, &allocation);
459 attributes.window_type = GDK_WINDOW_CHILD;
460 attributes.wclass = GDK_INPUT_ONLY;
461 attributes.x = allocation.x;
462 attributes.y = allocation.y;
463 attributes.width = allocation.width;
464 attributes.height = allocation.height;
465 attributes.event_mask = gtk_widget_get_events (widget);
466 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
467 GDK_BUTTON_RELEASE_MASK |
468 GDK_BUTTON1_MOTION_MASK |
469 GDK_POINTER_MOTION_HINT_MASK |
470 GDK_POINTER_MOTION_MASK |
471 GDK_ENTER_NOTIFY_MASK |
472 GDK_LEAVE_NOTIFY_MASK);
473 attributes_mask = GDK_WA_X | GDK_WA_Y;
475 priv->event_window = gdk_window_new (parent_window,
478 gdk_window_set_user_data (priv->event_window, widget);
482 gtk_switch_unrealize (GtkWidget *widget)
484 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
486 if (priv->event_window != NULL)
488 gdk_window_set_user_data (priv->event_window, NULL);
489 gdk_window_destroy (priv->event_window);
490 priv->event_window = NULL;
493 GTK_WIDGET_CLASS (gtk_switch_parent_class)->unrealize (widget);
497 gtk_switch_map (GtkWidget *widget)
499 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
501 GTK_WIDGET_CLASS (gtk_switch_parent_class)->map (widget);
503 if (priv->event_window)
504 gdk_window_show (priv->event_window);
508 gtk_switch_unmap (GtkWidget *widget)
510 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
512 if (priv->event_window)
513 gdk_window_hide (priv->event_window);
515 GTK_WIDGET_CLASS (gtk_switch_parent_class)->unmap (widget);
519 gtk_switch_paint_handle (GtkWidget *widget,
523 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
524 GtkStyleContext *context = gtk_widget_get_style_context (widget);
527 state = gtk_widget_get_state_flags (widget);
530 state |= GTK_STATE_FLAG_ACTIVE;
532 gtk_style_context_save (context);
533 gtk_style_context_set_state (context, state);
534 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
536 gtk_render_slider (context, cr,
538 box->width, box->height,
539 GTK_ORIENTATION_HORIZONTAL);
541 gtk_style_context_restore (context);
545 gtk_switch_draw (GtkWidget *widget,
548 GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
549 GtkStyleContext *context;
552 PangoFontDescription *desc;
553 const PangoFontDescription *style_desc;
555 gint label_x, label_y;
558 gint focus_width, focus_pad;
559 gint x, y, width, height;
560 gint font_size, style_font_size;
562 gtk_widget_style_get (widget,
563 "focus-line-width", &focus_width,
564 "focus-padding", &focus_pad,
567 context = gtk_widget_get_style_context (widget);
568 state = gtk_widget_get_state_flags (widget);
571 state |= GTK_STATE_FLAG_ACTIVE;
573 gtk_style_context_save (context);
575 gtk_style_context_set_state (context, state);
576 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
578 gtk_style_context_get_padding (context, state, &padding);
580 gtk_style_context_restore (context);
584 width = gtk_widget_get_allocated_width (widget);
585 height = gtk_widget_get_allocated_height (widget);
587 if (gtk_widget_has_visible_focus (widget))
588 gtk_render_focus (context, cr, x, y, width, height);
590 x += focus_width + focus_pad;
591 y += focus_width + focus_pad;
592 width -= 2 * (focus_width + focus_pad);
593 height -= 2 * (focus_width + focus_pad);
595 gtk_style_context_save (context);
596 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
597 gtk_style_context_set_state (context, state);
599 gtk_render_background (context, cr, x, y, width, height);
600 gtk_render_frame (context, cr, x, y, width, height);
602 width -= padding.left + padding.right;
603 height -= padding.top + padding.bottom;
609 handle.width = width / 2;
610 handle.height = height;
612 /* Translators: if the "on" state label requires more than three
613 * glyphs then use MEDIUM VERTICAL BAR (U+2759) as the text for
616 layout = gtk_widget_create_pango_layout (widget, C_("switch", "ON"));
618 /* FIXME: this should be really done in the theme, but overriding font size
619 * from it doesn't currently work. So we have to hardcode this here and
620 * below for the "OFF" label.
622 desc = pango_font_description_new ();
624 style_desc = gtk_style_context_get_font (context, state);
625 style_font_size = pango_font_description_get_size (style_desc);
626 font_size = MAX (style_font_size - 1 * PANGO_SCALE, ceil (style_font_size * PANGO_SCALE_SMALL));
628 pango_font_description_set_size (desc, font_size);
630 pango_layout_set_font_description (layout, desc);
632 pango_layout_get_extents (layout, NULL, &rect);
633 pango_extents_to_pixels (&rect, NULL);
635 label_x = x + ((width / 2) - rect.width) / 2;
636 label_y = y + (height - rect.height) / 2;
638 gtk_render_layout (context, cr, label_x, label_y, layout);
640 g_object_unref (layout);
642 /* Translators: if the "off" state label requires more than three
643 * glyphs then use WHITE CIRCLE (U+25CB) as the text for the state
645 layout = gtk_widget_create_pango_layout (widget, C_("switch", "OFF"));
646 pango_layout_set_font_description (layout, desc);
648 pango_layout_get_extents (layout, NULL, &rect);
649 pango_extents_to_pixels (&rect, NULL);
651 label_x = x + (width / 2) + ((width / 2) - rect.width) / 2;
652 label_y = y + (height - rect.height) / 2;
654 gtk_render_layout (context, cr, label_x, label_y, layout);
656 g_object_unref (layout);
658 if (priv->is_dragging)
659 handle.x = x + priv->handle_x;
660 else if (priv->is_active)
661 handle.x = x + width - handle.width;
665 gtk_style_context_restore (context);
667 gtk_switch_paint_handle (widget, cr, &handle);
669 pango_font_description_free (desc);
675 gtk_switch_set_related_action (GtkSwitch *sw,
678 GtkSwitchPrivate *priv = sw->priv;
680 if (priv->action == action)
683 gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (sw), action);
685 priv->action = action;
689 gtk_switch_set_use_action_appearance (GtkSwitch *sw,
690 gboolean use_appearance)
692 GtkSwitchPrivate *priv = sw->priv;
694 if (priv->use_action_appearance != use_appearance)
696 priv->use_action_appearance = use_appearance;
698 gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (sw), priv->action);
703 gtk_switch_update_action_observer (GtkSwitch *sw)
707 /* we are the only owner so this will clear all the signals */
708 g_clear_object (&sw->priv->action_observer);
710 window = gtk_widget_get_toplevel (GTK_WIDGET (sw));
712 if (GTK_IS_APPLICATION_WINDOW (window) && sw->priv->action_name)
714 GSimpleActionObserver *observer;
716 observer = gtk_application_window_create_observer (GTK_APPLICATION_WINDOW (window),
717 sw->priv->action_name,
718 sw->priv->action_target);
720 g_object_bind_property (observer, "active", sw, "active", G_BINDING_SYNC_CREATE);
721 g_object_bind_property (observer, "enabled", sw, "sensitive", G_BINDING_SYNC_CREATE);
723 sw->priv->action_observer = observer;
728 gtk_switch_set_action_name (GtkActionable *actionable,
729 const gchar *action_name)
731 GtkSwitch *sw = GTK_SWITCH (actionable);
733 g_return_if_fail (GTK_IS_SWITCH (sw));
735 g_free (sw->priv->action_name);
736 sw->priv->action_name = g_strdup (action_name);
738 gtk_switch_update_action_observer (sw);
740 g_object_notify (G_OBJECT (sw), "action-name");
744 gtk_switch_set_action_target_value (GtkActionable *actionable,
745 GVariant *action_target)
747 GtkSwitch *sw = GTK_SWITCH (actionable);
749 g_return_if_fail (GTK_IS_SWITCH (sw));
751 if (action_target != sw->priv->action_target &&
752 (!action_target || !sw->priv->action_target ||
753 !g_variant_equal (action_target, sw->priv->action_target)))
755 if (sw->priv->action_target)
756 g_variant_unref (sw->priv->action_target);
758 sw->priv->action_target = NULL;
761 sw->priv->action_target = g_variant_ref_sink (action_target);
763 gtk_switch_update_action_observer (sw);
765 g_object_notify (G_OBJECT (sw), "action-target");
770 gtk_switch_get_action_name (GtkActionable *actionable)
772 GtkSwitch *sw = GTK_SWITCH (actionable);
774 return sw->priv->action_name;
778 gtk_switch_get_action_target_value (GtkActionable *actionable)
780 GtkSwitch *sw = GTK_SWITCH (actionable);
782 return sw->priv->action_target;
786 gtk_switch_actionable_iface_init (GtkActionableInterface *iface)
788 iface->get_action_name = gtk_switch_get_action_name;
789 iface->set_action_name = gtk_switch_set_action_name;
790 iface->get_action_target_value = gtk_switch_get_action_target_value;
791 iface->set_action_target_value = gtk_switch_set_action_target_value;
795 gtk_switch_hierarchy_changed (GtkWidget *widget,
796 GtkWidget *previous_toplevel)
798 GtkSwitch *sw = GTK_SWITCH (widget);
799 GtkWidgetClass *parent_class;
801 parent_class = GTK_WIDGET_CLASS (gtk_switch_parent_class);
802 if (parent_class->hierarchy_changed)
803 parent_class->hierarchy_changed (widget, previous_toplevel);
805 if (sw->priv->action_name)
809 toplevel = gtk_widget_get_toplevel (widget);
811 if (toplevel != previous_toplevel)
812 gtk_switch_update_action_observer (sw);
817 gtk_switch_set_property (GObject *gobject,
822 GtkSwitch *sw = GTK_SWITCH (gobject);
827 gtk_switch_set_active (sw, g_value_get_boolean (value));
830 case PROP_RELATED_ACTION:
831 gtk_switch_set_related_action (sw, g_value_get_object (value));
834 case PROP_USE_ACTION_APPEARANCE:
835 gtk_switch_set_use_action_appearance (sw, g_value_get_boolean (value));
838 case PROP_ACTION_NAME:
839 gtk_switch_set_action_name (GTK_ACTIONABLE (sw), g_value_get_string (value));
842 case PROP_ACTION_TARGET:
843 gtk_switch_set_action_target_value (GTK_ACTIONABLE (sw), g_value_get_variant (value));
847 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
852 gtk_switch_get_property (GObject *gobject,
857 GtkSwitchPrivate *priv = GTK_SWITCH (gobject)->priv;
862 g_value_set_boolean (value, priv->is_active);
865 case PROP_RELATED_ACTION:
866 g_value_set_object (value, priv->action);
869 case PROP_USE_ACTION_APPEARANCE:
870 g_value_set_boolean (value, priv->use_action_appearance);
873 case PROP_ACTION_NAME:
874 g_value_set_string (value, priv->action_name);
877 case PROP_ACTION_TARGET:
878 g_value_set_variant (value, priv->action_target);
882 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
887 gtk_switch_dispose (GObject *object)
889 GtkSwitchPrivate *priv = GTK_SWITCH (object)->priv;
891 g_clear_object (&priv->action_observer);
893 if (priv->action_name)
895 g_free (priv->action_name);
896 priv->action_name = NULL;
901 gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (object), NULL);
905 G_OBJECT_CLASS (gtk_switch_parent_class)->dispose (object);
909 gtk_switch_class_init (GtkSwitchClass *klass)
911 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
912 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
913 gpointer activatable_iface;
915 g_type_class_add_private (klass, sizeof (GtkSwitchPrivate));
917 activatable_iface = g_type_default_interface_peek (GTK_TYPE_ACTIVATABLE);
918 switch_props[PROP_RELATED_ACTION] =
919 g_param_spec_override ("related-action",
920 g_object_interface_find_property (activatable_iface,
923 switch_props[PROP_USE_ACTION_APPEARANCE] =
924 g_param_spec_override ("use-action-appearance",
925 g_object_interface_find_property (activatable_iface,
926 "use-action-appearance"));
931 * Whether the #GtkSwitch widget is in its on or off state.
933 switch_props[PROP_ACTIVE] =
934 g_param_spec_boolean ("active",
936 P_("Whether the switch is on or off"),
938 GTK_PARAM_READWRITE);
940 gobject_class->set_property = gtk_switch_set_property;
941 gobject_class->get_property = gtk_switch_get_property;
942 gobject_class->dispose = gtk_switch_dispose;
944 g_object_class_install_properties (gobject_class, LAST_PROP, switch_props);
946 widget_class->get_preferred_width = gtk_switch_get_preferred_width;
947 widget_class->get_preferred_height = gtk_switch_get_preferred_height;
948 widget_class->size_allocate = gtk_switch_size_allocate;
949 widget_class->realize = gtk_switch_realize;
950 widget_class->unrealize = gtk_switch_unrealize;
951 widget_class->map = gtk_switch_map;
952 widget_class->unmap = gtk_switch_unmap;
953 widget_class->draw = gtk_switch_draw;
954 widget_class->button_press_event = gtk_switch_button_press;
955 widget_class->button_release_event = gtk_switch_button_release;
956 widget_class->motion_notify_event = gtk_switch_motion;
957 widget_class->enter_notify_event = gtk_switch_enter;
958 widget_class->leave_notify_event = gtk_switch_leave;
959 widget_class->hierarchy_changed = gtk_switch_hierarchy_changed;
961 klass->activate = gtk_switch_activate;
964 * GtkSwitch:slider-width:
966 * The minimum width of the #GtkSwitch handle, in pixels.
968 gtk_widget_class_install_style_property (widget_class,
969 g_param_spec_int ("slider-width",
971 P_("The minimum width of the handle"),
972 DEFAULT_SLIDER_WIDTH, G_MAXINT,
973 DEFAULT_SLIDER_WIDTH,
974 GTK_PARAM_READABLE));
977 * GtkSwitch::activate:
978 * @widget: the object which received the signal.
980 * The ::activate signal on GtkSwitch is an action signal and
981 * emitting it causes the switch to animate.
982 * Applications should never connect to this signal, but use the
983 * notify::active signal.
986 g_signal_new (I_("activate"),
987 G_OBJECT_CLASS_TYPE (gobject_class),
988 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
989 G_STRUCT_OFFSET (GtkSwitchClass, activate),
991 _gtk_marshal_VOID__VOID,
993 widget_class->activate_signal = signals[ACTIVATE];
995 g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
996 g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");
998 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SWITCH_ACCESSIBLE);
1002 gtk_switch_init (GtkSwitch *self)
1004 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTK_TYPE_SWITCH, GtkSwitchPrivate);
1005 self->priv->use_action_appearance = TRUE;
1006 gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
1007 gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
1013 * Creates a new #GtkSwitch widget.
1015 * Return value: the newly created #GtkSwitch instance
1020 gtk_switch_new (void)
1022 return g_object_new (GTK_TYPE_SWITCH, NULL);
1026 * gtk_switch_set_active:
1028 * @is_active: %TRUE if @sw should be active, and %FALSE otherwise
1030 * Changes the state of @sw to the desired one.
1035 gtk_switch_set_active (GtkSwitch *sw,
1038 GtkSwitchPrivate *priv;
1040 g_return_if_fail (GTK_IS_SWITCH (sw));
1042 is_active = !!is_active;
1046 if (priv->is_active != is_active)
1048 AtkObject *accessible;
1050 GtkStyleContext *context;
1052 widget = GTK_WIDGET (sw);
1053 priv->is_active = is_active;
1055 g_object_notify_by_pspec (G_OBJECT (sw), switch_props[PROP_ACTIVE]);
1057 if (priv->action_observer)
1058 g_simple_action_observer_activate (priv->action_observer);
1061 gtk_action_activate (priv->action);
1063 accessible = gtk_widget_get_accessible (GTK_WIDGET (sw));
1064 atk_object_notify_state_change (accessible, ATK_STATE_CHECKED, priv->is_active);
1066 if (gtk_widget_get_realized (widget))
1068 context = gtk_widget_get_style_context (widget);
1069 gtk_style_context_notify_state_change (context,
1070 gtk_widget_get_window (widget),
1071 NULL, GTK_STATE_ACTIVE, is_active);
1074 gtk_widget_queue_draw (GTK_WIDGET (sw));
1079 * gtk_switch_get_active:
1082 * Gets whether the #GtkSwitch is in its "on" or "off" state.
1084 * Return value: %TRUE if the #GtkSwitch is active, and %FALSE otherwise
1089 gtk_switch_get_active (GtkSwitch *sw)
1091 g_return_val_if_fail (GTK_IS_SWITCH (sw), FALSE);
1093 return sw->priv->is_active;
1097 gtk_switch_update (GtkActivatable *activatable,
1099 const gchar *property_name)
1101 if (strcmp (property_name, "visible") == 0)
1103 if (gtk_action_is_visible (action))
1104 gtk_widget_show (GTK_WIDGET (activatable));
1106 gtk_widget_hide (GTK_WIDGET (activatable));
1108 else if (strcmp (property_name, "sensitive") == 0)
1110 gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));
1112 else if (strcmp (property_name, "active") == 0)
1114 gtk_action_block_activate (action);
1115 gtk_switch_set_active (GTK_SWITCH (activatable), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
1116 gtk_action_unblock_activate (action);
1121 gtk_switch_sync_action_properties (GtkActivatable *activatable,
1127 if (gtk_action_is_visible (action))
1128 gtk_widget_show (GTK_WIDGET (activatable));
1130 gtk_widget_hide (GTK_WIDGET (activatable));
1132 gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));
1134 gtk_action_block_activate (action);
1135 gtk_switch_set_active (GTK_SWITCH (activatable), gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
1136 gtk_action_unblock_activate (action);
1140 gtk_switch_activatable_interface_init (GtkActivatableIface *iface)
1142 iface->update = gtk_switch_update;
1143 iface->sync_action_properties = gtk_switch_sync_action_properties;