[gnome-builder] prefs: add GbPreferencesSwitch



commit 5290a8ab42bd37fff6079c14f41532adfb43ed0c
Author: Christian Hergert <christian hergert me>
Date:   Fri Apr 10 01:24:51 2015 -0700

    prefs: add GbPreferencesSwitch
    
    This widget simplifies the process of creating switches for the preferences
    pages. Additionally, it will allow us to hook up gsettings from the .ui
    file without intervention in C files.
    
    I think we can simplify this all a bunch more, but it is a step in the
    right direction.

 data/css/builder.Adwaita.css                       |    5 +
 data/ui/gb-preferences-page-editor.ui              |  677 ++++----------------
 data/ui/gb-preferences-page-experimental.ui        |   57 +--
 data/ui/gb-preferences-page-keybindings.ui         |  180 +-----
 data/ui/gb-preferences-switch.ui                   |   71 ++
 src/gnome-builder.mk                               |    2 +
 src/preferences/gb-preferences-page-editor.c       |  165 ++----
 src/preferences/gb-preferences-page-editor.h       |   28 +-
 src/preferences/gb-preferences-page-experimental.c |   22 +-
 src/preferences/gb-preferences-page-keybindings.c  |   58 +--
 src/preferences/gb-preferences-page.c              |    5 +-
 src/preferences/gb-preferences-page.h              |    2 +-
 src/preferences/gb-preferences-switch.c            |  396 ++++++++++++
 src/preferences/gb-preferences-switch.h            |   35 +
 src/resources/gnome-builder.gresource.xml          |    1 +
 15 files changed, 739 insertions(+), 965 deletions(-)
---
diff --git a/data/css/builder.Adwaita.css b/data/css/builder.Adwaita.css
index afd21fc..742ccb6 100644
--- a/data/css/builder.Adwaita.css
+++ b/data/css/builder.Adwaita.css
@@ -137,3 +137,8 @@ GbEditorWorkspace > GtkPaned > GtkBox > GtkBox.header.notebook {
 GbNewProjectDialog GtkFileChooserButton.linked-on-right .button {
     border-radius: 3px 0 0 3px;
 }
+
+
+GbPreferencesSwitch:prelight {
+    background-color: shade(@theme_bg_color, 1.01);
+}
diff --git a/data/ui/gb-preferences-page-editor.ui b/data/ui/gb-preferences-page-editor.ui
index c5812ae..f1d2445 100644
--- a/data/ui/gb-preferences-page-editor.ui
+++ b/data/ui/gb-preferences-page-editor.ui
@@ -11,600 +11,154 @@
           <object class="GtkBox">
             <property name="visible">true</property>
             <property name="orientation">vertical</property>
-            <property name="spacing">12</property>
             <child>
-              <object class="GtkGrid" id="grid1">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="hexpand">True</property>
-                <property name="row_spacing">12</property>
-                <property name="column_spacing">12</property>
-                <child>
-                  <object class="GtkSwitch" id="restore_insert_mark_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="word_completion_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="show_diff_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="show_line_numbers_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="highlight_current_line_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">5</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="highlight_matching_brackets_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">6</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="smart_home_end_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">7</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="smart_backspace_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">8</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSwitch" id="show_grid_lines_switch">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="valign">center</property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">9</property>
-                  </packing>
-                </child>
+              <object class="GbPreferencesSwitch" id="restore_insert_mark_switch">
+                <property name="title" translatable="yes">Restore Cursor Position</property>
+                <property name="description" translatable="yes">Restore the insertion cursor to the last 
position when reopening a file.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">restore-insert-mark</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="word_completion_switch">
+                <property name="title" translatable="yes">Suggest Word Completion</property>
+                <property name="description" translatable="yes">Suggest completion of words found within the 
current document.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">word-completion</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="show_diff_switch">
+                <property name="title" translatable="yes">Display Document Changes</property>
+                <property name="description" translatable="yes">Display whether a line has been added or 
modified next to the line number.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">show-line-changes</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="show_line_numbers_switch">
+                <property name="title" translatable="yes">Show Line Numbers</property>
+                <property name="description" translatable="yes">Display line numbers in the 
document.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">show-line-numbers</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="highlight_current_line_switch">
+                <property name="title" translatable="yes">Highlight Current Line</property>
+                <property name="description" translatable="yes">Highlight the line containing the 
cursor.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">highlight-current-line</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="highlight_matching_brackets_switch">
+                <property name="title" translatable="yes">Highlight Matching Brackets</property>
+                <property name="description" translatable="yes">Whether or not matching brackets should be 
highlighted.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">highlight-matching-brackets</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="smart_home_end_switch">
+                <property name="title" translatable="yes">Smart Home End</property>
+                <property name="description" translatable="yes">Home moves to first non-space 
character.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">smart-home-end</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="smart_backspace_switch">
+                <property name="title" translatable="yes">Smart Backspace</property>
+                <property name="description" translatable="yes">Backspace will remove extra spaces to keep 
you aligned with your indentation size.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">smart-backspace</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GbPreferencesSwitch" id="show_grid_lines_switch">
+                <property name="title" translatable="yes">Show Grid Lines</property>
+                <property name="description" translatable="yes">Display a grid patten in the 
document.</property>
+                <property name="settings">editor_settings</property>
+                <property name="settings-schema-key">show-grid-lines</property>
+                <property name="size-group">control_group</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox" id="scroll_off_container">
+                <property name="orientation">horizontal</property>
+                <property name="margin">6</property>
+                <property name="spacing">12</property>
+                <property name="visible">true</property>
                 <child>
                   <object class="GtkSpinButton" id="scroll_off_spin">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
+                    <property name="visible">true</property>
                     <property name="valign">center</property>
+                    <property name="expand">false</property>
                     <property name="adjustment">scroll_off_adjustment</property>
                   </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">10</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="restore_insert_mark_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel" id="label9">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Restore Cursor Position</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label10">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Restore the insertion cursor to the last 
position when reopening a file.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="word_completion_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel" id="label11">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Suggest Word Completion</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label12">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Suggest completion of words found within 
the current document.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">2</property>
-                  </packing>
                 </child>
                 <child>
-                  <object class="GtkBox" id="show_diff_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
+                  <object class="GtkBox">
+                    <property name="visible">true</property>
+                    <property name="hexpand">true</property>
                     <property name="orientation">vertical</property>
                     <child>
                       <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Display Document Changes</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Display whether a line has been added or 
modified next to the line number.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">3</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="show_line_numbers_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Show Line Numbers</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Display line numbers in the 
document.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="highlight_current_line_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Highlight Current Line</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Highlight the line containing the 
cursor.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">5</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="highlight_matching_brackets_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Highlight Matching Brackets</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Whether or not matching brackets should be 
highlighted.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">6</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="smart_home_end_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Smart Home End</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Home moves to first non-space 
character.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">7</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="smart_backspace_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Smart Backspace</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Backspace will remove extra spaces to keep 
you aligned with your indentation size.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">8</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="show_grid_lines_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Show Grid Lines</property>
-                        <property name="xalign">0</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="label" translatable="yes">Display a grid patten in the 
document.</property>
-                        <property name="xalign">0</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">9</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkBox" id="scroll_off_container">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
+                        <property name="visible">true</property>
                         <property name="label" translatable="yes">Scroll Offset</property>
-                        <property name="xalign">0</property>
+                        <property name="xalign">0.0</property>
                         <attributes>
                           <attribute name="weight" value="bold"/>
                         </attributes>
                       </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
                     </child>
                     <child>
                       <object class="GtkLabel">
-                        <property name="visible">True</property>
+                        <property name="visible">true</property>
                         <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
+                        <property name="hexpand">true</property>
                         <property name="label" translatable="yes">Minimal number of lines to keep above and 
below the cursor.</property>
                         <property name="xalign">0</property>
+                        <attributes>
+                          <attribute name="scale" value="0.833333"/>
+                        </attributes>
                         <style>
                           <class name="dim-label"/>
                         </style>
                       </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
                     </child>
                   </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">10</property>
-                  </packing>
                 </child>
               </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
-              </packing>
             </child>
             <child>
               <object class="GtkFontButton" id="font_button">
-                <property name="visible">True</property>
+                <property name="margin">6</property>
+                <property name="visible">true</property>
               </object>
             </child>
             <child>
               <object class="GtkSourceStyleSchemeChooserButton" id="style_scheme_button">
-                <property name="visible">True</property>
+                <property name="margin">6</property>
+                <property name="visible">true</property>
               </object>
             </child>
           </object>
@@ -619,4 +173,13 @@
     <property name="page_increment">1</property>
     <property name="value">0</property>
   </object>
+  <object class="GSettings" id="editor_settings">
+    <property name="schema-id">org.gnome.builder.editor</property>
+  </object>
+  <object class="GtkSizeGroup" id="control_group">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="scroll_off_spin"/>
+    </widgets>
+  </object>
 </interface>
diff --git a/data/ui/gb-preferences-page-experimental.ui b/data/ui/gb-preferences-page-experimental.ui
index 7cae022..e03dfa5 100644
--- a/data/ui/gb-preferences-page-experimental.ui
+++ b/data/ui/gb-preferences-page-experimental.ui
@@ -7,59 +7,18 @@
         <property name="visible">true</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="GtkBox" id="all_container">
+          <object class="GbPreferencesSwitch" id="clang_autocompletion">
+            <property name="settings">experimental_settings</property>
+            <property name="settings-schema-key">clang-autocompletion</property>
+            <property name="title" translatable="yes">Clang Auto-Completion</property>
+            <property name="description" translatable="yes">Use Clang for autocompletion in the C and C++ 
languages.</property>
             <property name="visible">true</property>
-            <property name="orientation">vertical</property>
-            <child>
-              <object class="GtkBox" id="clang_autocompletion_container">
-                <property name="orientation">horizontal</property>
-                <property name="spacing">6</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkSwitch" id="clang_autocompletion_switch">
-                    <property name="action-name">settings.clang-autocompletion</property>
-                    <property name="hexpand">false</property>
-                    <property name="vexpand">false</property>
-                    <property name="valign">center</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkBox">
-                    <property name="visible">true</property>
-                    <property name="hexpand">true</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Clang Auto-Completion</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-top">6</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Use Clang for autocompletion in the C and 
C++ languages.</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-bottom">6</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
           </object>
         </child>
       </object>
     </child>
   </template>
+  <object class="GSettings" id="experimental_settings">
+    <property name="schema-id">org.gnome.builder.experimental</property>
+  </object>
 </interface>
diff --git a/data/ui/gb-preferences-page-keybindings.ui b/data/ui/gb-preferences-page-keybindings.ui
index c9ce822..330e4ed 100644
--- a/data/ui/gb-preferences-page-keybindings.ui
+++ b/data/ui/gb-preferences-page-keybindings.ui
@@ -4,159 +4,45 @@
   <template class="GbPreferencesPageKeybindings" parent="GbPreferencesPage">
     <child>
       <object class="GtkBox">
-        <property name="visible">true</property>
         <property name="orientation">vertical</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GbPreferencesSwitch" id="default_switch">
+            <property name="settings">editor_settings</property>
+            <property name="settings-schema-key">keybindings</property>
+            <property name="settings-schema-value">'default'</property>
+            <property name="is-radio">true</property>
+            <property name="title" translatable="yes">Builder</property>
+            <property name="description" translatable="yes">The default keybindings for Builder.</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+        <child>
+          <object class="GbPreferencesSwitch" id="emacs_switch">
+            <property name="settings">editor_settings</property>
+            <property name="settings-schema-key">keybindings</property>
+            <property name="settings-schema-value">'emacs'</property>
+            <property name="is-radio">true</property>
+            <property name="title" translatable="yes">Emacs Emulation</property>
+            <property name="description" translatable="yes">Emulates familiar keybindings from the Emacs 
text editor.</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
         <child>
-          <object class="GtkBox" id="all_container">
+          <object class="GbPreferencesSwitch" id="vim_switch">
+            <property name="settings">editor_settings</property>
+            <property name="settings-schema-key">keybindings</property>
+            <property name="settings-schema-value">'vim'</property>
+            <property name="is-radio">true</property>
+            <property name="title" translatable="yes">Vim Emulation</property>
+            <property name="description" translatable="yes">Emulates familiar keybindings from the Vim text 
editor.</property>
             <property name="visible">true</property>
-            <property name="orientation">vertical</property>
-            <child>
-              <object class="GtkBox" id="default_container">
-                <property name="orientation">horizontal</property>
-                <property name="spacing">6</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkSwitch" id="default_switch">
-                    <property name="action-name">settings.keybindings</property>
-                    <property name="action-target">'default'</property>
-                    <property name="hexpand">false</property>
-                    <property name="vexpand">false</property>
-                    <property name="valign">center</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkBox" id="box3">
-                    <property name="visible">true</property>
-                    <property name="hexpand">true</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel" id="label1">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Builder</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-top">6</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkLabel" id="label2">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">The default keybindings for 
Builder.</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-bottom">6</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkBox" id="emacs_container">
-                <property name="orientation">horizontal</property>
-                <property name="spacing">6</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkSwitch" id="emacs_switch">
-                    <property name="action-name">settings.keybindings</property>
-                    <property name="action-target">'emacs'</property>
-                    <property name="hexpand">false</property>
-                    <property name="vexpand">false</property>
-                    <property name="valign">center</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkBox">
-                    <property name="visible">true</property>
-                    <property name="hexpand">true</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Emacs Emulation</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-top">6</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Emulates familiar keybindings from the 
Emacs text editor.</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-bottom">6</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkBox" id="vim_container">
-                <property name="orientation">horizontal</property>
-                <property name="spacing">6</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkSwitch" id="vim_switch">
-                    <property name="action-name">settings.keybindings</property>
-                    <property name="action-target">'vim'</property>
-                    <property name="hexpand">false</property>
-                    <property name="vexpand">false</property>
-                    <property name="valign">center</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkBox">
-                    <property name="visible">true</property>
-                    <property name="hexpand">true</property>
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Vim Emulation</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-top">6</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkLabel">
-                        <property name="visible">true</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Emulates familiar keybindings from the Vim 
text editor.</property>
-                        <property name="margin-start">6</property>
-                        <property name="margin-bottom">6</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </child>
           </object>
         </child>
       </object>
     </child>
   </template>
+  <object class="GSettings" id="editor_settings">
+    <property name="schema-id">org.gnome.builder.editor</property>
+  </object>
 </interface>
diff --git a/data/ui/gb-preferences-switch.ui b/data/ui/gb-preferences-switch.ui
new file mode 100644
index 0000000..19e2fef
--- /dev/null
+++ b/data/ui/gb-preferences-switch.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.16 -->
+  <template class="GbPreferencesSwitch" parent="GtkEventBox">
+    <child>
+      <object class="GtkBox">
+        <property name="margin">6</property>
+        <property name="orientation">horizontal</property>
+        <property name="spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkBox" id="controls_box">
+            <property name="halign">end</property>
+            <property name="hexpand">false</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkSwitch" id="settings_switch">
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="valign">center</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="settings_radio">
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="valign">center</property>
+                <property name="visible">false</property>
+                <style>
+                  <class name="radio"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">vertical</property>
+            <property name="valign">center</property>
+            <property name="vexpand">false</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkLabel" id="title_label">
+                <property name="mnemonic-widget">settings_switch</property>
+                <property name="visible">true</property>
+                <property name="xalign">0.0</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="description_label">
+                <property name="mnemonic-widget">settings_switch</property>
+                <property name="visible">true</property>
+                <property name="xalign">0.0</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+                <attributes>
+                  <attribute name="scale" value="0.833333"/>
+                </attributes>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index e4f994c..ae22244 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -90,6 +90,8 @@ libgnome_builder_la_SOURCES = \
        src/preferences/gb-preferences-page-language.h \
        src/preferences/gb-preferences-page.c \
        src/preferences/gb-preferences-page.h \
+       src/preferences/gb-preferences-switch.c \
+       src/preferences/gb-preferences-switch.h \
        src/preferences/gb-preferences-window.c \
        src/preferences/gb-preferences-window.h \
        src/project-tree/gb-project-tree.c \
diff --git a/src/preferences/gb-preferences-page-editor.c b/src/preferences/gb-preferences-page-editor.c
index 526cbda..dafd6f0 100644
--- a/src/preferences/gb-preferences-page-editor.c
+++ b/src/preferences/gb-preferences-page-editor.c
@@ -24,11 +24,11 @@
 #include "gb-preferences-page-editor.h"
 #include "gb-widget.h"
 
-struct _GbPreferencesPageEditorPrivate
+struct _GbPreferencesPageEditor
 {
-  GSettings                         *settings;
+  GbPreferencesPage                  parent_instance;
 
-  /* Widgets owned by Template */
+  GSettings                         *editor_settings;
   GtkSwitch                         *restore_insert_mark_switch;
   GtkSwitch                         *show_diff_switch;
   GtkSwitch                         *word_completion_switch;
@@ -42,22 +42,10 @@ struct _GbPreferencesPageEditorPrivate
   GtkFontButton                     *font_button;
   GtkSourceStyleSchemeChooserButton *style_scheme_button;
   GtkAdjustment                     *scroll_off_adjustment;
-
-  /* Template widgets used for filtering */
-  GtkWidget                         *restore_insert_mark_container;
-  GtkWidget                         *word_completion_container;
-  GtkWidget                         *show_diff_container;
-  GtkWidget                         *show_line_numbers_container;
-  GtkWidget                         *highlight_current_line_container;
-  GtkWidget                         *highlight_matching_brackets_container;
-  GtkWidget                         *smart_backspace_container;
-  GtkWidget                         *smart_home_end_container;
-  GtkWidget                         *show_grid_lines_container;
-  GtkWidget                         *scroll_off_container;
+  GtkBox                            *scroll_off_container;
 };
 
-G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesPageEditor, gb_preferences_page_editor,
-                            GB_TYPE_PREFERENCES_PAGE)
+G_DEFINE_TYPE (GbPreferencesPageEditor, gb_preferences_page_editor, GB_TYPE_PREFERENCES_PAGE)
 
 static void
 gb_preferences_page_editor_style_scheme_changed (GtkSourceStyleSchemeChooser *chooser,
@@ -82,76 +70,35 @@ gb_preferences_page_editor_style_scheme_changed (GtkSourceStyleSchemeChooser *ch
 static void
 gb_preferences_page_editor_constructed (GObject *object)
 {
-  GbPreferencesPageEditorPrivate *priv;
-  GbPreferencesPageEditor *editor = (GbPreferencesPageEditor *)object;
+  GbPreferencesPageEditor *self = (GbPreferencesPageEditor *)object;
   GtkSourceStyleSchemeManager *manager;
   GtkSourceStyleScheme *scheme;
   gchar *scheme_id;
 
-  g_return_if_fail (GB_IS_PREFERENCES_PAGE_EDITOR (editor));
-
-  priv = editor->priv;
+  g_assert (GB_IS_PREFERENCES_PAGE_EDITOR (self));
 
-  priv->settings = g_settings_new ("org.gnome.builder.editor");
+  G_OBJECT_CLASS (gb_preferences_page_editor_parent_class)->constructed (object);
 
-  g_settings_bind (priv->settings, "restore-insert-mark",
-                   priv->restore_insert_mark_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "show-line-changes",
-                   priv->show_diff_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "word-completion",
-                   priv->word_completion_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "show-line-numbers",
-                   priv->show_line_numbers_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "highlight-current-line",
-                   priv->highlight_current_line_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "highlight-matching-brackets",
-                   priv->highlight_matching_brackets_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "smart-home-end",
-                   priv->smart_home_end_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "smart-backspace",
-                   priv->smart_backspace_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "show-grid-lines",
-                   priv->show_grid_lines_switch, "active",
-                   G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "scroll-offset",
-                   priv->scroll_off_adjustment, "value",
+  g_settings_bind (self->editor_settings, "scroll-offset",
+                   self->scroll_off_adjustment, "value",
                    G_SETTINGS_BIND_DEFAULT);
-  g_settings_bind (priv->settings, "font-name",
-                   priv->font_button, "font-name",
+  g_settings_bind (self->editor_settings, "font-name",
+                   self->font_button, "font-name",
                    G_SETTINGS_BIND_DEFAULT);
 
-  scheme_id = g_settings_get_string (priv->settings, "style-scheme-name");
+  scheme_id = g_settings_get_string (self->editor_settings, "style-scheme-name");
   manager = gtk_source_style_scheme_manager_get_default ();
   scheme = gtk_source_style_scheme_manager_get_scheme (manager, scheme_id);
   g_free (scheme_id);
+
   gtk_source_style_scheme_chooser_set_style_scheme (
-      GTK_SOURCE_STYLE_SCHEME_CHOOSER (priv->style_scheme_button),
+      GTK_SOURCE_STYLE_SCHEME_CHOOSER (self->style_scheme_button),
       scheme);
-  g_signal_connect_object (priv->style_scheme_button,
+  g_signal_connect_object (self->style_scheme_button,
                            "notify::style-scheme",
                            G_CALLBACK (gb_preferences_page_editor_style_scheme_changed),
-                           priv->settings,
+                           self->editor_settings,
                            0);
-
-  G_OBJECT_CLASS (gb_preferences_page_editor_parent_class)->constructed (object);
-}
-
-static void
-gb_preferences_page_editor_finalize (GObject *object)
-{
-  GbPreferencesPageEditorPrivate *priv = GB_PREFERENCES_PAGE_EDITOR (object)->priv;
-
-  g_clear_object (&priv->settings);
-
-  G_OBJECT_CLASS (gb_preferences_page_editor_parent_class)->finalize (object);
 }
 
 static void
@@ -161,110 +108,90 @@ gb_preferences_page_editor_class_init (GbPreferencesPageEditorClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->constructed = gb_preferences_page_editor_constructed;
-  object_class->finalize = gb_preferences_page_editor_finalize;
 
   GB_WIDGET_CLASS_TEMPLATE (widget_class, "gb-preferences-page-editor.ui");
 
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, font_button);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, restore_insert_mark_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, show_diff_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, style_scheme_button);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, word_completion_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, show_line_numbers_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, highlight_current_line_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, highlight_matching_brackets_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, smart_home_end_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, smart_backspace_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, show_grid_lines_switch);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, scroll_off_spin);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, scroll_off_adjustment);
-
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, restore_insert_mark_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, word_completion_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, show_diff_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, show_line_numbers_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, highlight_current_line_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, 
highlight_matching_brackets_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, smart_home_end_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, smart_backspace_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, show_grid_lines_container);
-  GB_WIDGET_CLASS_BIND_PRIVATE (widget_class, GbPreferencesPageEditor, scroll_off_container);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, editor_settings);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, font_button);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, restore_insert_mark_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, show_diff_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, style_scheme_button);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, word_completion_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, show_line_numbers_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, highlight_current_line_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, highlight_matching_brackets_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, smart_home_end_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, smart_backspace_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, show_grid_lines_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, scroll_off_spin);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, scroll_off_adjustment);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageEditor, scroll_off_container);
 }
 
 static void
 gb_preferences_page_editor_init (GbPreferencesPageEditor *self)
 {
-  self->priv = gb_preferences_page_editor_get_instance_private (self);
-
   gtk_widget_init_template (GTK_WIDGET (self));
+
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("restore insert cursor mark"),
-                                               self->priv->restore_insert_mark_container,
-                                               self->priv->restore_insert_mark_switch,
+                                               self->restore_insert_mark_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("word words auto completion suggest found document"),
-                                               self->priv->word_completion_container,
-                                               self->priv->word_completion_switch,
+                                               self->word_completion_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("diff renderer gutter changes git vcs"),
-                                               self->priv->show_diff_container,
-                                               self->priv->show_diff_switch,
+                                               self->show_diff_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("line numbers"),
-                                               self->priv->show_line_numbers_container,
-                                               self->priv->show_line_numbers_switch,
+                                               self->show_line_numbers_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("line lines highlight current"),
-                                               self->priv->highlight_current_line_container,
-                                               self->priv->highlight_current_line_switch,
+                                               self->highlight_current_line_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("bracket brackets highlight matching"),
-                                               self->priv->highlight_matching_brackets_container,
-                                               self->priv->highlight_matching_brackets_switch,
+                                               self->highlight_matching_brackets_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("smart home end"),
-                                               self->priv->smart_home_end_container,
-                                               self->priv->smart_home_end_switch,
+                                               self->smart_home_end_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("smart back backspace indent align"),
-                                               self->priv->smart_backspace_container,
-                                               self->priv->smart_backspace_switch,
+                                               self->smart_backspace_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("show grid lines"),
-                                               self->priv->show_grid_lines_container,
-                                               self->priv->show_grid_lines_switch,
+                                               self->show_grid_lines_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("lines margin scrolloff scroll off"),
-                                               self->priv->scroll_off_container,
-                                               self->priv->scroll_off_spin,
+                                               self->scroll_off_container,
+                                               self->scroll_off_spin,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("font document editor monospace"),
-                                               GTK_WIDGET (self->priv->font_button),
+                                               self->font_button,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("source style scheme source tango solarized builder"),
-                                               GTK_WIDGET (self->priv->style_scheme_button),
+                                               self->style_scheme_button,
                                                NULL);
 }
diff --git a/src/preferences/gb-preferences-page-editor.h b/src/preferences/gb-preferences-page-editor.h
index 9b40f4a..f52e2f1 100644
--- a/src/preferences/gb-preferences-page-editor.h
+++ b/src/preferences/gb-preferences-page-editor.h
@@ -23,32 +23,10 @@
 
 G_BEGIN_DECLS
 
-#define GB_TYPE_PREFERENCES_PAGE_EDITOR            (gb_preferences_page_editor_get_type())
-#define GB_PREFERENCES_PAGE_EDITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GB_TYPE_PREFERENCES_PAGE_EDITOR, GbPreferencesPageEditor))
-#define GB_PREFERENCES_PAGE_EDITOR_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GB_TYPE_PREFERENCES_PAGE_EDITOR, GbPreferencesPageEditor const))
-#define GB_PREFERENCES_PAGE_EDITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
GB_TYPE_PREFERENCES_PAGE_EDITOR, GbPreferencesPageEditorClass))
-#define GB_IS_PREFERENCES_PAGE_EDITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GB_TYPE_PREFERENCES_PAGE_EDITOR))
-#define GB_IS_PREFERENCES_PAGE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
GB_TYPE_PREFERENCES_PAGE_EDITOR))
-#define GB_PREFERENCES_PAGE_EDITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
GB_TYPE_PREFERENCES_PAGE_EDITOR, GbPreferencesPageEditorClass))
+#define GB_TYPE_PREFERENCES_PAGE_EDITOR (gb_preferences_page_editor_get_type())
 
-typedef struct _GbPreferencesPageEditor        GbPreferencesPageEditor;
-typedef struct _GbPreferencesPageEditorClass   GbPreferencesPageEditorClass;
-typedef struct _GbPreferencesPageEditorPrivate GbPreferencesPageEditorPrivate;
-
-struct _GbPreferencesPageEditor
-{
-  GbPreferencesPage parent;
-
-  /*< private >*/
-  GbPreferencesPageEditorPrivate *priv;
-};
-
-struct _GbPreferencesPageEditorClass
-{
-  GbPreferencesPageClass parent;
-};
-
-GType gb_preferences_page_editor_get_type (void);
+G_DECLARE_FINAL_TYPE (GbPreferencesPageEditor, gb_preferences_page_editor,
+                      GB, PREFERENCES_PAGE_EDITOR, GbPreferencesPage)
 
 G_END_DECLS
 
diff --git a/src/preferences/gb-preferences-page-experimental.c 
b/src/preferences/gb-preferences-page-experimental.c
index 12bbccc..19a1057 100644
--- a/src/preferences/gb-preferences-page-experimental.c
+++ b/src/preferences/gb-preferences-page-experimental.c
@@ -25,8 +25,7 @@ struct _GbPreferencesPageExperimental
 {
   GbPreferencesPage  parent_instance;
 
-  GtkWidget         *clang_autocompletion_container;
-  GtkWidget         *clang_autocompletion_switch;
+  GtkWidget         *clang_autocompletion;
 };
 
 G_DEFINE_TYPE (GbPreferencesPageExperimental,
@@ -37,32 +36,17 @@ static void
 gb_preferences_page_experimental_class_init (GbPreferencesPageExperimentalClass *klass)
 {
   GB_WIDGET_CLASS_TEMPLATE (klass, "gb-preferences-page-experimental.ui");
-  GB_WIDGET_CLASS_BIND (klass, GbPreferencesPageExperimental, clang_autocompletion_container);
-  GB_WIDGET_CLASS_BIND (klass, GbPreferencesPageExperimental, clang_autocompletion_switch);
+  GB_WIDGET_CLASS_BIND (klass, GbPreferencesPageExperimental, clang_autocompletion);
 }
 
 static void
 gb_preferences_page_experimental_init (GbPreferencesPageExperimental *self)
 {
-  g_autoptr(GSettings) settings = NULL;
-  GAction *action;
-  GSimpleActionGroup *group;
-
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  settings = g_settings_new ("org.gnome.builder.experimental");
-
-  group = g_simple_action_group_new ();
-
-  action = g_settings_create_action (settings, "clang-autocompletion");
-  g_action_map_add_action (G_ACTION_MAP (group), action);
-  g_clear_object (&action);
-
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "settings", G_ACTION_GROUP (group));
-
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("experimental clang autocompletion auto complete"),
-                                               self->clang_autocompletion_container,
+                                               self->clang_autocompletion,
                                                NULL);
 }
diff --git a/src/preferences/gb-preferences-page-keybindings.c 
b/src/preferences/gb-preferences-page-keybindings.c
index 01d1bda..84fceb0 100644
--- a/src/preferences/gb-preferences-page-keybindings.c
+++ b/src/preferences/gb-preferences-page-keybindings.c
@@ -28,12 +28,10 @@ struct _GbPreferencesPageKeybindings
 {
   GbPreferencesPage      parent_instance;
 
-  GSettings             *editor_settings;
-
   /* Template widgets used for filtering */
-  GtkWidget             *default_container;
-  GtkWidget             *emacs_container;
-  GtkWidget             *vim_container;
+  GtkWidget             *default_switch;
+  GtkWidget             *emacs_switch;
+  GtkWidget             *vim_switch;
 };
 
 G_DEFINE_TYPE (GbPreferencesPageKeybindings,
@@ -41,49 +39,15 @@ G_DEFINE_TYPE (GbPreferencesPageKeybindings,
                GB_TYPE_PREFERENCES_PAGE)
 
 static void
-gb_preferences_page_keybindings_constructed (GObject *object)
-{
-  GbPreferencesPageKeybindings *self = (GbPreferencesPageKeybindings *)object;
-  GSimpleActionGroup *group;
-  GAction *action;
-
-  g_return_if_fail (GB_IS_PREFERENCES_PAGE_KEYBINDINGS (self));
-
-  self->editor_settings = g_settings_new ("org.gnome.builder.editor");
-
-  group = g_simple_action_group_new ();
-
-  action = g_settings_create_action (self->editor_settings, "keybindings");
-  g_action_map_add_action (G_ACTION_MAP (group), action);
-  g_clear_object (&action);
-
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "settings", G_ACTION_GROUP (group));
-  g_clear_object (&group);
-}
-
-static void
-gb_preferences_page_keybindings_finalize (GObject *object)
-{
-  GbPreferencesPageKeybindings *self = (GbPreferencesPageKeybindings *)object;
-
-  g_clear_object (&self->editor_settings);
-
-  G_OBJECT_CLASS (gb_preferences_page_keybindings_parent_class)->finalize (object);
-}
-
-static void
 gb_preferences_page_keybindings_class_init (GbPreferencesPageKeybindingsClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  object_class->finalize = gb_preferences_page_keybindings_finalize;
-  object_class->constructed = gb_preferences_page_keybindings_constructed;
-
   GB_WIDGET_CLASS_TEMPLATE (widget_class, "gb-preferences-page-keybindings.ui");
-  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageKeybindings, vim_container);
-  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageKeybindings, emacs_container);
-  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageKeybindings, default_container);
+
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageKeybindings, vim_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageKeybindings, emacs_switch);
+  GB_WIDGET_CLASS_BIND (widget_class, GbPreferencesPageKeybindings, default_switch);
 }
 
 static void
@@ -93,17 +57,17 @@ gb_preferences_page_keybindings_init (GbPreferencesPageKeybindings *self)
 
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
-                                               _("default keybindings"),
-                                               self->default_container,
+                                               _("default builder keybindings"),
+                                               self->default_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("emacs keybindings modal"),
-                                               self->emacs_container,
+                                               self->emacs_switch,
                                                NULL);
   gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
   /* To translators: This is a list of keywords for the preferences page */
                                                _("vim keybindings modal"),
-                                               self->vim_container,
+                                               self->vim_switch,
                                                NULL);
 }
diff --git a/src/preferences/gb-preferences-page.c b/src/preferences/gb-preferences-page.c
index d6de247..cf6a554 100644
--- a/src/preferences/gb-preferences-page.c
+++ b/src/preferences/gb-preferences-page.c
@@ -22,6 +22,7 @@
 #include <ide.h>
 
 #include "gb-preferences-page.h"
+#include "gb-preferences-switch.h"
 #include "gb-string.h"
 
 struct _GbPreferencesPagePrivate
@@ -106,7 +107,7 @@ gb_preferences_page_set_keywords (GbPreferencesPage   *page,
 void
 gb_preferences_page_set_keywords_for_widget (GbPreferencesPage *page,
                                              const gchar       *keywords,
-                                             GtkWidget         *first_widget,
+                                             gpointer           first_widget,
                                              ...)
 {
   GtkWidget *widget = first_widget;
@@ -218,6 +219,8 @@ gb_preferences_page_class_init (GbPreferencesPageClass *klass)
                           G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (object_class, PROP_TITLE,
                                    gParamSpecs [PROP_TITLE]);
+
+  g_type_ensure (GB_TYPE_PREFERENCES_SWITCH);
 }
 
 static void
diff --git a/src/preferences/gb-preferences-page.h b/src/preferences/gb-preferences-page.h
index f51404c..51b231c 100644
--- a/src/preferences/gb-preferences-page.h
+++ b/src/preferences/gb-preferences-page.h
@@ -55,7 +55,7 @@ guint    gb_preferences_page_set_keywords            (GbPreferencesPage   *page,
                                                       const gchar * const *keywords);
 void     gb_preferences_page_set_keywords_for_widget (GbPreferencesPage   *page,
                                                       const gchar         *keywords,
-                                                      GtkWidget           *first_widget,
+                                                      gpointer             first_widget,
                                                       ...) G_GNUC_NULL_TERMINATED;
 
 G_END_DECLS
diff --git a/src/preferences/gb-preferences-switch.c b/src/preferences/gb-preferences-switch.c
new file mode 100644
index 0000000..37bafc0
--- /dev/null
+++ b/src/preferences/gb-preferences-switch.c
@@ -0,0 +1,396 @@
+/* gb-preferences-switch.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gb-preferences-switch.h"
+#include "gb-widget.h"
+
+struct _GbPreferencesSwitch
+{
+  GtkEventBox      parent_instance;
+
+  GtkBox          *controls_box;
+  GtkLabel        *description_label;
+  GtkCheckButton  *settings_radio;
+  GtkSwitch       *settings_switch;
+  GtkLabel        *title_label;
+
+  GSettings       *settings;
+  gchar           *settings_schema_key;
+  GVariant        *settings_schema_value;
+
+  guint            in_widget : 1;
+  guint            is_radio : 1;
+};
+
+G_DEFINE_TYPE (GbPreferencesSwitch, gb_preferences_switch, GTK_TYPE_EVENT_BOX)
+
+enum {
+  PROP_0,
+  PROP_TITLE,
+  PROP_DESCRIPTION,
+  PROP_DRAW_INDICATOR,
+  PROP_IS_RADIO,
+  PROP_SETTINGS,
+  PROP_SETTINGS_SCHEMA_KEY,
+  PROP_SETTINGS_SCHEMA_VALUE,
+  PROP_SIZE_GROUP,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+GtkWidget *
+gb_preferences_switch_new (void)
+{
+  return g_object_new (GB_TYPE_PREFERENCES_SWITCH, NULL);
+}
+
+static void
+gb_preferences_switch_update_settings (GbPreferencesSwitch *self)
+{
+  g_assert (GB_IS_PREFERENCES_SWITCH (self));
+
+  if ((self->settings != NULL) && (self->settings_schema_key != NULL))
+    {
+      GSimpleActionGroup *group;
+      GAction *action;
+      gchar *name;
+
+      action = g_settings_create_action (self->settings, self->settings_schema_key);
+      group = g_simple_action_group_new ();
+      g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
+      gtk_widget_insert_action_group (GTK_WIDGET (self), "settings", G_ACTION_GROUP (group));
+      g_object_unref (action);
+
+      name = g_strdup_printf ("settings.%s", self->settings_schema_key);
+
+      if (self->is_radio)
+        {
+          gtk_actionable_set_action_name (GTK_ACTIONABLE (self->settings_radio), name);
+          gtk_actionable_set_action_name (GTK_ACTIONABLE (self->settings_switch), NULL);
+        }
+      else
+        {
+          gtk_actionable_set_action_name (GTK_ACTIONABLE (self->settings_radio), NULL);
+          gtk_actionable_set_action_name (GTK_ACTIONABLE (self->settings_switch), name);
+        }
+
+      g_free (name);
+    }
+}
+
+static void
+gb_preferences_switch_set_is_radio (GbPreferencesSwitch *self,
+                                    gboolean             is_radio)
+{
+  g_assert (GB_IS_PREFERENCES_SWITCH (self));
+
+  self->is_radio = !!is_radio;
+
+  gtk_widget_set_visible (GTK_WIDGET (self->settings_radio), is_radio);
+  gtk_widget_set_visible (GTK_WIDGET (self->settings_switch), !is_radio);
+
+  gb_preferences_switch_update_settings (self);
+}
+
+static void
+gb_preferences_switch_set_settings_schema_value (GbPreferencesSwitch *self,
+                                                 GVariant            *variant)
+{
+  g_return_if_fail (GB_IS_PREFERENCES_SWITCH (self));
+
+  gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->settings_switch), variant);
+  gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->settings_radio), variant);
+}
+
+static void
+gb_preferences_switch_set_settings (GbPreferencesSwitch *self,
+                                    GSettings           *settings)
+{
+  g_return_if_fail (GB_IS_PREFERENCES_SWITCH (self));
+  g_return_if_fail (G_IS_SETTINGS (settings));
+
+  if (g_set_object (&self->settings, settings))
+    gb_preferences_switch_update_settings (self);
+}
+
+static void
+gb_preferences_switch_set_settings_schema_key (GbPreferencesSwitch *self,
+                                               const gchar         *settings_schema_key)
+{
+  g_return_if_fail (GB_IS_PREFERENCES_SWITCH (self));
+
+  if (self->settings_schema_key != settings_schema_key)
+    {
+      g_free (self->settings_schema_key);
+      self->settings_schema_key = g_strdup (settings_schema_key);
+      gb_preferences_switch_update_settings (self);
+    }
+}
+
+static gboolean
+gb_preferences_switch_draw (GtkWidget *widget,
+                            cairo_t   *cr)
+{
+
+  GbPreferencesSwitch *self = (GbPreferencesSwitch *)widget;
+  GtkStyleContext *style_context;
+  GtkStateFlags flags;
+  gboolean ret = FALSE;
+
+  g_assert (GB_IS_PREFERENCES_SWITCH (self));
+
+  style_context = gtk_widget_get_style_context (widget);
+
+  gtk_style_context_save (style_context);
+
+  if (self->in_widget)
+    {
+      flags = gtk_style_context_get_state (style_context);
+      gtk_style_context_set_state (style_context, flags | GTK_STATE_FLAG_PRELIGHT);
+    }
+
+  ret = GTK_WIDGET_CLASS (gb_preferences_switch_parent_class)->draw (widget, cr);
+
+  gtk_style_context_restore (style_context);
+
+  return ret;
+}
+
+static gboolean
+gb_preferences_switch_enter_notify_event (GtkWidget        *widget,
+                                          GdkEventCrossing *crossing)
+{
+  GbPreferencesSwitch *self = (GbPreferencesSwitch *)widget;
+  gboolean ret = FALSE;
+
+  g_assert (GB_IS_PREFERENCES_SWITCH (self));
+
+  self->in_widget = TRUE;
+  gtk_widget_queue_draw (widget);
+
+  return ret;
+}
+
+static gboolean
+gb_preferences_switch_leave_notify_event (GtkWidget        *widget,
+                                          GdkEventCrossing *crossing)
+{
+  GbPreferencesSwitch *self = (GbPreferencesSwitch *)widget;
+  gboolean ret = FALSE;
+
+  g_assert (GB_IS_PREFERENCES_SWITCH (self));
+
+  self->in_widget = FALSE;
+  gtk_widget_queue_draw (widget);
+
+  return ret;
+}
+
+static gboolean
+gb_preferences_switch_button_release_event (GtkWidget      *widget,
+                                            GdkEventButton *button)
+{
+  GbPreferencesSwitch *self = (GbPreferencesSwitch *)widget;
+  gboolean ret;
+
+  g_assert (GB_IS_PREFERENCES_SWITCH (self));
+
+  ret = GTK_WIDGET_CLASS (gb_preferences_switch_parent_class)->button_release_event (widget, button);
+
+  if ((ret == FALSE) && (self->in_widget == TRUE))
+    {
+      if (button->button == GDK_BUTTON_PRIMARY)
+        {
+          if (self->is_radio)
+            g_signal_emit_by_name (self->settings_radio, "activate");
+          else
+            g_signal_emit_by_name (self->settings_switch, "activate");
+
+          ret = TRUE;
+        }
+    }
+
+  return ret;
+}
+
+static void
+gb_preferences_switch_set_size_group (GbPreferencesSwitch *self,
+                                      GtkSizeGroup        *group)
+{
+  g_return_if_fail (GB_IS_PREFERENCES_SWITCH (self));
+  g_return_if_fail (!group || GTK_IS_SIZE_GROUP (group));
+
+  if (group != NULL)
+    gtk_size_group_add_widget (group, GTK_WIDGET (self->controls_box));
+}
+
+static void
+gb_preferences_switch_finalize (GObject *object)
+{
+  GbPreferencesSwitch *self = (GbPreferencesSwitch *)object;
+
+  g_clear_pointer (&self->settings_schema_key, g_free);
+  g_clear_object (&self->settings);
+
+  G_OBJECT_CLASS (gb_preferences_switch_parent_class)->finalize (object);
+}
+
+static void
+gb_preferences_switch_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GbPreferencesSwitch *self = GB_PREFERENCES_SWITCH (object);
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      gtk_label_set_label (self->title_label, g_value_get_string (value));
+      break;
+
+    case PROP_DESCRIPTION:
+      gtk_label_set_label (self->description_label, g_value_get_string (value));
+      break;
+
+    case PROP_IS_RADIO:
+      gb_preferences_switch_set_is_radio (self, g_value_get_boolean (value));
+      break;
+
+    case PROP_SETTINGS:
+      gb_preferences_switch_set_settings (self, g_value_get_object (value));
+      break;
+
+    case PROP_SETTINGS_SCHEMA_KEY:
+      gb_preferences_switch_set_settings_schema_key (self, g_value_get_string (value));
+      break;
+
+    case PROP_SETTINGS_SCHEMA_VALUE:
+      gb_preferences_switch_set_settings_schema_value (self, g_value_get_variant (value));
+      break;
+
+    case PROP_SIZE_GROUP:
+      gb_preferences_switch_set_size_group (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gb_preferences_switch_class_init (GbPreferencesSwitchClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gb_preferences_switch_finalize;
+  object_class->set_property = gb_preferences_switch_set_property;
+
+  widget_class->button_release_event = gb_preferences_switch_button_release_event;
+  widget_class->draw = gb_preferences_switch_draw;
+  widget_class->enter_notify_event = gb_preferences_switch_enter_notify_event;
+  widget_class->leave_notify_event = gb_preferences_switch_leave_notify_event;
+
+  gParamSpecs [PROP_TITLE] =
+    g_param_spec_string ("title",
+                         _("Title"),
+                         _("The title of the switch."),
+                         NULL,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_TITLE,
+                                   gParamSpecs [PROP_TITLE]);
+
+  gParamSpecs [PROP_DESCRIPTION] =
+    g_param_spec_string ("description",
+                         _("Description"),
+                         _("The description for the switch."),
+                         NULL,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_DESCRIPTION,
+                                   gParamSpecs [PROP_DESCRIPTION]);
+
+  gParamSpecs [PROP_IS_RADIO] =
+    g_param_spec_boolean ("is-radio",
+                          _("Is Radio"),
+                          _("If a radio button should be used."),
+                          FALSE,
+                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_IS_RADIO,
+                                   gParamSpecs [PROP_IS_RADIO]);
+
+  gParamSpecs [PROP_SETTINGS] =
+    g_param_spec_object ("settings",
+                         _("Settings"),
+                         _("The GSettings for the setting."),
+                         G_TYPE_SETTINGS,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SETTINGS,
+                                   gParamSpecs [PROP_SETTINGS]);
+
+  gParamSpecs [PROP_SETTINGS_SCHEMA_KEY] =
+    g_param_spec_string ("settings-schema-key",
+                         _("Settings Schema Key"),
+                         _("The settings schema key."),
+                         NULL,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SETTINGS_SCHEMA_KEY,
+                                   gParamSpecs [PROP_SETTINGS_SCHEMA_KEY]);
+
+  gParamSpecs [PROP_SETTINGS_SCHEMA_VALUE] =
+    g_param_spec_variant ("settings-schema-value",
+                          _("Settings Schema Value"),
+                          _("An action-target for the settings action."),
+                          G_VARIANT_TYPE_ANY,
+                          NULL,
+                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SETTINGS_SCHEMA_VALUE,
+                                   gParamSpecs [PROP_SETTINGS_SCHEMA_VALUE]);
+
+  gParamSpecs [PROP_SIZE_GROUP] =
+    g_param_spec_object ("size-group",
+                         _("Size Group"),
+                         _("The sizing group for the control."),
+                         GTK_TYPE_SIZE_GROUP,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_SIZE_GROUP,
+                                   gParamSpecs [PROP_SIZE_GROUP]);
+
+  GB_WIDGET_CLASS_TEMPLATE (klass, "gb-preferences-switch.ui");
+
+  GB_WIDGET_CLASS_BIND (klass, GbPreferencesSwitch, controls_box);
+  GB_WIDGET_CLASS_BIND (klass, GbPreferencesSwitch, description_label);
+  GB_WIDGET_CLASS_BIND (klass, GbPreferencesSwitch, settings_radio);
+  GB_WIDGET_CLASS_BIND (klass, GbPreferencesSwitch, settings_switch);
+  GB_WIDGET_CLASS_BIND (klass, GbPreferencesSwitch, title_label);
+}
+
+static void
+gb_preferences_switch_init (GbPreferencesSwitch *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_widget_add_events (GTK_WIDGET (self),
+                         (GDK_BUTTON_PRESS_MASK |
+                          GDK_BUTTON_RELEASE_MASK |
+                          GDK_ENTER_NOTIFY_MASK |
+                          GDK_LEAVE_NOTIFY_MASK));
+}
diff --git a/src/preferences/gb-preferences-switch.h b/src/preferences/gb-preferences-switch.h
new file mode 100644
index 0000000..5cb855d
--- /dev/null
+++ b/src/preferences/gb-preferences-switch.h
@@ -0,0 +1,35 @@
+/* gb-preferences-switch.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_PREFERENCES_SWITCH_H
+#define GB_PREFERENCES_SWITCH_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_PREFERENCES_SWITCH (gb_preferences_switch_get_type())
+
+G_DECLARE_FINAL_TYPE (GbPreferencesSwitch, gb_preferences_switch,
+                      GB, PREFERENCES_SWITCH, GtkEventBox)
+
+GtkWidget *gb_preferences_switch_new (void);
+
+G_END_DECLS
+
+#endif /* GB_PREFERENCES_SWITCH_H */
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index 7b2f90f..8a30e3f 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -47,6 +47,7 @@
     <file alias="ui/gb-preferences-page-git.ui">../../data/ui/gb-preferences-page-git.ui</file>
     <file 
alias="ui/gb-preferences-page-keybindings.ui">../../data/ui/gb-preferences-page-keybindings.ui</file>
     <file alias="ui/gb-preferences-page-language.ui">../../data/ui/gb-preferences-page-language.ui</file>
+    <file alias="ui/gb-preferences-switch.ui">../../data/ui/gb-preferences-switch.ui</file>
     <file alias="ui/gb-preferences-window.ui">../../data/ui/gb-preferences-window.ui</file>
     <file alias="ui/gb-projects-dialog.ui">../../data/ui/gb-projects-dialog.ui</file>
     <file alias="ui/gb-recent-project-row.ui">../../data/ui/gb-recent-project-row.ui</file>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]