diff --git a/docs/reference/xapp-docs.xml b/docs/reference/xapp-docs.xml
index daa1627..d1f65a8 100644
--- a/docs/reference/xapp-docs.xml
+++ b/docs/reference/xapp-docs.xml
@@ -19,6 +19,7 @@
+
diff --git a/libxapp/Makefile.am b/libxapp/Makefile.am
index f2bd420..a088f09 100644
--- a/libxapp/Makefile.am
+++ b/libxapp/Makefile.am
@@ -18,6 +18,7 @@
xapp-monitor-blanker.c \
xapp-kbd-layout-controller.c \
xapp-gtk-window.c \
+ xapp-preferences-window.c \
xapp-glade-catalog.c
libxapp_la_SOURCES = \
@@ -44,7 +45,8 @@
libxapp_HEADERS = \
xapp-monitor-blanker.h \
xapp-kbd-layout-controller.h \
- xapp-gtk-window.h
+ xapp-gtk-window.h \
+ xapp-preferences-window.h
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS =
diff --git a/libxapp/xapp-preferences-window.c b/libxapp/xapp-preferences-window.c
new file mode 100644
index 0000000..b8eb973
--- /dev/null
+++ b/libxapp/xapp-preferences-window.c
@@ -0,0 +1,188 @@
+#include
+#include "xapp-preferences-window.h"
+
+/**
+ * SECTION:xapp-preferences-window
+ * @Short_description: A base preferences window
+ * @Title: XAppPreferencesWindow
+ *
+ * The XAppPreferencesWindow sets up a simple dialog
+ * window with a GtkStack, GtkSidebarSwitcher, and
+ * GtkActionBar. The stack switcher and action bar only
+ * show when needed.
+ */
+
+typedef struct
+{
+ GtkWidget *stack;
+ GtkWidget *side_switcher;
+ GtkWidget *button_area;
+ GtkSizeGroup *button_size_group;
+
+ gint num_pages;
+} XAppPreferencesWindowPrivate;
+
+enum
+{
+ CLOSE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (XAppPreferencesWindow, xapp_preferences_window, GTK_TYPE_WINDOW)
+
+static void
+xapp_preferences_window_init (XAppPreferencesWindow *window)
+{
+ XAppPreferencesWindowPrivate *priv = xapp_preferences_window_get_instance_private (window);
+ GtkWidget *main_box;
+ GtkWidget *secondary_box;
+ GtkWidget *separator;
+
+ gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
+ gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
+ gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), main_box);
+
+ secondary_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (main_box), secondary_box, TRUE, TRUE, 0);
+
+ priv->side_switcher = gtk_stack_sidebar_new ();
+ gtk_widget_set_size_request (priv->side_switcher, 100, -1);
+ gtk_box_pack_start (GTK_BOX (secondary_box), priv->side_switcher, FALSE, FALSE, 0);
+ gtk_widget_set_no_show_all (priv->side_switcher, TRUE);
+
+ separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+ gtk_box_pack_start (GTK_BOX (secondary_box), separator, FALSE, FALSE, 0);
+
+ priv->stack = gtk_stack_new ();
+ gtk_stack_set_transition_type (GTK_STACK (priv->stack), GTK_STACK_TRANSITION_TYPE_CROSSFADE);
+ gtk_box_pack_start (GTK_BOX (secondary_box), priv->stack, TRUE, TRUE, 0);
+ gtk_stack_sidebar_set_stack (GTK_STACK_SIDEBAR (priv->side_switcher), GTK_STACK (priv->stack));
+
+ priv->button_area = gtk_action_bar_new ();
+ gtk_box_pack_start (GTK_BOX (main_box), priv->button_area, FALSE, FALSE, 0);
+ gtk_widget_set_no_show_all (priv->button_area, TRUE);
+
+ priv->button_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* Keep track of the number of pages so we can hide the stack switcher with < 2 */
+ priv->num_pages = 0;
+}
+
+static void
+xapp_preferences_window_close (XAppPreferencesWindow *window)
+{
+ gtk_window_close (GTK_WINDOW (window));
+}
+
+static void
+xapp_preferences_window_class_init (XAppPreferencesWindowClass *klass)
+{
+ GtkBindingSet *binding_set;
+
+ klass->close = xapp_preferences_window_close;
+
+ signals[CLOSE] =
+ g_signal_new ("close",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (XAppPreferencesWindowClass, close),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
+}
+
+/**
+ * xapp_preferences_window_new:
+ *
+ * Creates a new #XAppPreferencesWindow.
+ *
+ * Returns: a newly created #XAppPreferencesWindow
+ */
+XAppPreferencesWindow *
+xapp_preferences_window_new (void)
+{
+ return g_object_new (XAPP_TYPE_PREFERENCES_WINDOW, NULL);
+}
+
+
+/**
+ * xapp_preferences_window_add_page:
+ * @window: a #XAppPreferencesWindow
+ * @widget: a #GtkWidget to add
+ * @name: the name for the page
+ * @title: a human-readable title for the page
+ *
+ * Adds a page to the window. The page is identified by name. The
+ * title will be used in the sidebar so should be short. The sidebar
+ * will show automatically once at least two pages are added.
+ */
+void
+xapp_preferences_window_add_page (XAppPreferencesWindow *window,
+ GtkWidget *widget,
+ const gchar *name,
+ const gchar *title)
+{
+ XAppPreferencesWindowPrivate *priv = xapp_preferences_window_get_instance_private (window);
+
+ g_return_if_fail (XAPP_IS_PREFERENCES_WINDOW (window));
+
+ gtk_stack_add_titled (GTK_STACK (priv->stack), widget, name, title);
+
+ priv->num_pages++;
+
+ if (priv->num_pages > 1)
+ {
+ gtk_widget_set_no_show_all (priv->side_switcher, FALSE);
+ }
+}
+
+/**
+ * xapp_preferences_window_add_button:
+ * @window: a #XAppPreferencesWindow
+ * @button: a #GtkWidget to add
+ * @pack_type: a #GtkPackType to use
+ *
+ * Adds a button to the bottom action bar of the window. Where
+ * the button is place will be determined by the #GtkPackType. The
+ * action bar will show automatically once at least one button is
+ * added.
+ */
+void
+xapp_preferences_window_add_button (XAppPreferencesWindow *window,
+ GtkWidget *button,
+ GtkPackType pack_type)
+{
+ XAppPreferencesWindowPrivate *priv = xapp_preferences_window_get_instance_private (window);
+ GtkStyleContext *style_context;
+
+ g_return_if_fail (XAPP_IS_PREFERENCES_WINDOW (window));
+ g_return_if_fail (GTK_IS_WIDGET (button));
+
+ if (pack_type == GTK_PACK_START)
+ {
+ gtk_action_bar_pack_start (GTK_ACTION_BAR (priv->button_area), button);
+ gtk_widget_set_margin_start (button, 6);
+ }
+ else if (pack_type == GTK_PACK_END)
+ {
+ gtk_action_bar_pack_end (GTK_ACTION_BAR (priv->button_area), button);
+ gtk_widget_set_margin_end (button, 6);
+ }
+ else
+ {
+ return;
+ }
+
+ style_context = gtk_widget_get_style_context (button);
+ gtk_style_context_add_class (style_context, "text-button");
+
+ gtk_size_group_add_widget (priv->button_size_group, button);
+ gtk_widget_set_no_show_all (priv->button_area, FALSE);
+}
\ No newline at end of file
diff --git a/libxapp/xapp-preferences-window.h b/libxapp/xapp-preferences-window.h
new file mode 100644
index 0000000..4dfddea
--- /dev/null
+++ b/libxapp/xapp-preferences-window.h
@@ -0,0 +1,34 @@
+#ifndef _XAPP_PREFERENCES_WINDOW_H_
+#define _XAPP_PREFERENCES_WINDOW_H_
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define XAPP_TYPE_PREFERENCES_WINDOW (xapp_preferences_window_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (XAppPreferencesWindow, xapp_preferences_window, XAPP, PREFERENCES_WINDOW, GtkWindow)
+
+struct _XAppPreferencesWindowClass
+{
+ GtkWindowClass parent_class;
+
+ /* Keybinding signals */
+ void (* close) (XAppPreferencesWindow *window);
+};
+
+XAppPreferencesWindow *xapp_preferences_window_new (void);
+
+void xapp_preferences_window_add_page (XAppPreferencesWindow *window,
+ GtkWidget *widget,
+ const gchar *name,
+ const gchar *title);
+
+void xapp_preferences_window_add_button (XAppPreferencesWindow *window,
+ GtkWidget *button,
+ GtkPackType pack_type);
+
+G_END_DECLS
+
+#endif /* _XAPP_PREFERENCES_WINDOW_H_ */
\ No newline at end of file