GNOME Bugzilla – Bug 634987
add a switch widget
Last modified: 2010-11-29 13:03:00 UTC
One pattern we encounter often when designing apps or OS System Settings is an on/off switch to control a feature or service. A checkbutton is appropriate for boolean settings where a) it represents true or false b) there are no intermediate states between 1 and 0 c) the label text can easily communicate the unchecked state (or it is obvious). These aren't true for options that start services. Closer is an on/off radio button pair, however, this doesn't represent the intermediate state well either and it is somewhat visually awkward. It might be nice to have a switch widget that can represent: on, off, changing.
I'm currently implementing this widget, but whilst discussing on IRC, a new requirement popped up - asynchronous support for cancelling a toggle. now, this requirement is a bit odd, considering that no other UI control currently exposed by gtk+ satisfies it. so I'd love to hear a proper specification for this widget - i.e. how do you define the "changing" state, and how should the switch react to a long running operation. there's also the matter of defining the toggling interaction: clicking on the uncovered area makes sense for a desktop; there's also the dragging of the slider (with or without a threshold). finally: animation. do we animate the transition between states? ideally both the dragging and the animation can be added post-merge: they don't change the main interaction model, nor the API. the cancellability, if it is a requirement, might affect the API instead - so it would be good to have it defined, at least as a simple UX specification from which I can deduce the API.
Created attachment 175365 [details] [review] Add GtkSwitch, a "light-switch" like widget This is an initial implementation of the "light switch" widget; it has both direct and dragged toggling for the state changes, plus some key handling (pressing Enter will toggle the current state). Graphically, it maps the mockup done by jimmac available here: http://gitorious.org/gnome-design/gnome-design/blobs/master/mockups/theming/widget-factory.png It's been a while since I implemented gtk+ widgets, so it might have some brain damage - I apologize for that. It's lacking the animation between states, though it's not going to be much complicated to add. I'm still unsure about the :active property - prior art (in the form of the UISwitch control in the UIKit for iOS) would say that the property should be called :on - but set_on() and get_on() are pretty lousy names for accessors, and we do have in gtk+ the concept of "active/inactive". Reviews are most welcome.
Review of attachment 175365 [details] [review]: Missing addition to docs section. ::: gtk/gtkswitch.c @@ +231,3 @@ + if (event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_KP_Enter || + event->keyval == GDK_KEY_ISO_Enter) Make space work as well? @@ +257,3 @@ + * the state + */ + layout = gtk_widget_create_pango_layout (widget, _("ON")); Add context? @@ +265,3 @@ + * glyphs then use WHITE CIRCLE (U+25CB) as the text for the state + */ + pango_layout_set_text (layout, _("OFF"), -1); Here as well. @@ +294,3 @@ + * "off" states of the GtkSwitch + */ + layout = gtk_widget_create_pango_layout (widget, _("ON OFF")); context as well? By the way, why can't we concatenate this ourselves instead of making translators do it? @@ +408,3 @@ + gboolean is_sensitive = gtk_widget_is_sensitive (widget); + + gtk_paint_box (style, cr, gtk_paint_slider instead? So that it gets the same "dimples" as on scrollbars, and as used in the mockup. ::: gtk/gtkswitch.h @@ +30,3 @@ +{ + /*< private >*/ + GtkWidget parent_instance; Any reasons why it's not a descendant of GtkToggleButton instead, just like GtkCheckButton?
(In reply to comment #3) > Review of attachment 175365 [details] [review]: > > Missing addition to docs section. the whole docs stuff is probably going to wait on the finalization of the API, to simplify the patch. > ::: gtk/gtkswitch.c > @@ +231,3 @@ > + if (event->keyval == GDK_KEY_Return || > + event->keyval == GDK_KEY_KP_Enter || > + event->keyval == GDK_KEY_ISO_Enter) > > Make space work as well? good point. > @@ +257,3 @@ > + * the state > + */ > + layout = gtk_widget_create_pango_layout (widget, _("ON")); > > Add context? fixed > @@ +265,3 @@ > + * glyphs then use WHITE CIRCLE (U+25CB) as the text for the state > + */ > + pango_layout_set_text (layout, _("OFF"), -1); > > Here as well. fixed > @@ +294,3 @@ > + * "off" states of the GtkSwitch > + */ > + layout = gtk_widget_create_pango_layout (widget, _("ON OFF")); > > context as well? > By the way, why can't we concatenate this ourselves instead of making > translators do it? fixed > @@ +408,3 @@ > + gboolean is_sensitive = gtk_widget_is_sensitive (widget); > + > + gtk_paint_box (style, cr, > > gtk_paint_slider instead? So that it gets the same "dimples" as on scrollbars, > and as used in the mockup. ah, yes - good plan. > ::: gtk/gtkswitch.h > @@ +30,3 @@ > +{ > + /*< private >*/ > + GtkWidget parent_instance; > > Any reasons why it's not a descendant of GtkToggleButton instead, just like > GtkCheckButton? a switch is not a button painted differently: you can click it - but the clicks are attached to a particular area of the widget. it also has a handle. in terms of user interaction a switch is more similar to a scrollbar - though not exactly. also, using Button as the super-class would give us all the API of a Button - including the ::clicked signal, which I don't think make sense on a switch.
Created attachment 175371 [details] [review] Add GtkSwitch, a "light-switch" like widget / v2 Fixes after Bastien's review.
Created attachment 175375 [details] [review] Add GtkSwitch, a "light-switch" like widget / v3 Forgot the space key handling, and cleaned up the test.
Created attachment 175400 [details] Localized gtkSwitch mockups Hello. I must say this is an excellent idea. After looking in the source code for the way the widget works for localized versions, I tried to draw a test of the widget with this WHITE CIRCLE and MEDIUM VERTICAL BAR characters, and I found some issues. The characters don't have the same height and the same line width. They are also usually not vertically centralized. Instead of using unicode chars, maybe it can be a good idea to manually render the drawings for the line and circle. Thank you.
Additionally, a comment could be added to the translators telling that if there are no translations with 3 or less chars for ON and OFF, they can keep the English version if the people which speaks the translated language are ok with it. For instance, in [Brazilian] Portuguese there are the words "ligado" and "desligado" for on/off. However I'm sure it would be much better for Brazilians to have the words "ON" and "OFF" right in English than the I/O drawings.
Some initial impressions: - Dragging the slider reveals a blank space and the text only pops in when I've dragged all the way over. We should always draw both labels - The text should be rendered with a matching state, so that we get white-on-blue for selected, instead of black-on-blue - There's one place where you still get the translations without context - Since the widget is taking focus, it should show some focus indication - Should implement GtkActivatable I'll attach my modified gtkswitch.c, which implements these things. Some more notes: - Do we need a vertical variant ? Probably not... - Animation should probably wait until we land gtk-style-context - Is it really intuitive that clicking on the slider does nothing ?
Created attachment 175401 [details] updated gtkswitch.c
(In reply to comment #9) > Some initial impressions: > > - Dragging the slider reveals a blank space and the text only pops in when I've > dragged all the way over. We should always draw both labels my rationale for drawing only the "target" label was to avoid confusion, and to save a gtk_paint_layout(); I have no strong feelings about this, though. > Some more notes: > > - Do we need a vertical variant ? Probably not... I tend to agree. > - Is it really intuitive that clicking on the slider does nothing ? my initial version had the toggling on the whole area of the switch; after playing with it a bit more, it felt a bit odd in the case when you press-leave.
(In reply to comment #7) > After looking in the source code for the way the widget works for localized > versions, I tried to draw a test of the widget with this WHITE CIRCLE and > MEDIUM VERTICAL BAR characters, and I found some issues. > > The characters don't have the same height and the same line width. They are > also usually not vertically centralized. we paint the glyphs separately, so we can center them. > Instead of using unicode chars, maybe it can be a good idea to manually render > the drawings for the line and circle. we cannot separate the cases: if text is left untranslated, we default back to "ON"/"OFF".
Created attachment 175403 [details] localized GtkSwitch using MEDIUM CIRCLE (U+25CB)
Created attachment 175404 [details] localized GtkSwitch using LARGE CIRCLE (U+25EF)
A certain risk is that the font might not have these characters. Does e.g. Cantarell have them ? One thing we could do is if (strcmp (C_"switch", "ON"), "\342\227\213") == 0) /* draw circle */ but of course, then we have the problem of making the drawing code match font characteristics (font size, font weight, etc).
(In reply to comment #15) > A certain risk is that the font might not have these characters. Does e.g. > Cantarell have them ? I think most fonts have them, but you're right: there is such risk. > One thing we could do is > > if (strcmp (C_"switch", "ON"), "\342\227\213") == 0) > /* draw circle */ > > but of course, then we have the problem of making the drawing code match font > characteristics (font size, font weight, etc). I think MeeGo on netbooks, and looking at the MxToggle/MxGtkLightSwitch code, gave up on localization and just used ❙ and ○ in the background image for the widget. what I think will happen is that every i18n team will either use the on/off or the single-glyph pair - even for CJK and other non-latin scripts. the advantage of using a glyph is that we can effectively size the widget using the current font-size settings, and this would be an advantage in case of a11y themes with big fonts.
I agree. Lets just go with this for now, then. Should not block the merge, anyway. One thing I had idly wondered about is whether we need a way to force a minimum height for the slider as well (eg for touchscreens)
(In reply to comment #17) > One thing I had idly wondered about is whether we need a way to force a minimum > height for the slider as well (eg for touchscreens) good point; right now there's an hardcoded minimum height, but I can easily add a style property - similar to the :slider-width one.
Final few things on my checklist: - Flipping: do switches in RTL countries go the other way ? I don't know, so I guess we wait until someone tells us that our switches look wrong in Israel. - A11y: I think we should just add the accessible object inside gtk. Can probably just copy GailToggleButton 1-1. I can look at that tonight, maybe
Created attachment 175440 [details] [review] Add GtkSwitch, a "light-switch" like widget / v4
Created attachment 175441 [details] [review] switch: Add accessibility implementation Modelled on GailToggleButton. Untested - I need to enable A11Y, but it's getting late so it'll have to wait until tomorrow :-)
Created attachment 175459 [details] [review] Add GtkSwitch, a "light-switch" like widget / v5 The initial patch with the changes from Matthias.
Created attachment 175460 [details] [review] docs: Add GtkSwitch to the API reference
Created attachment 175461 [details] [review] switch: Add accessibility implementation Same as attachment 175441 [details] [review], but in order.
Attachment 175460 [details] pushed to master Attachment 175460 [details] pushed as 044040d - docs: Add GtkSwitch to the API reference Attachment 175461 [details] pushed as ae95cdf - switch: Add accessibility implementation