一个偏好对话框

一个典型的应用程序应该有一些偏好设置,在每次打开时都能被记住。即使是为这个小范例程序,我们也将想改变正文的字体。

我们将用GSettings 来保存偏好设置,GSettings 需要一个描述我们设置的模式。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <schemalist>
  3. <schema path="/org/gtk/exampleapp/" id="org.gtk.exampleapp">
  4. <key name="font" type="s">
  5. <default>'Monospace 12'</default>
  6. <summary>Font</summary>
  7. <description>The font to be used for content.</description>
  8. </key>
  9. <key name="transition" type="s">
  10. <choices>
  11. <choice value='none'/>
  12. <choice value='crossfade'/>
  13. <choice value='slide-left-right'/>
  14. </choices>
  15. <default>'none'</default>
  16. <summary>Transition</summary>
  17. <description>The transition to use when switching tabs.</description>
  18. </key>
  19. </schema>
  20. </schemalist>

当我们在应用程序中使用这个模式之前,我们需要从GSettings 中将这编译进二进制文件。GIO 提供macros来在工程中做这件事。

接着,我们需要连接settings 和我们的目标部件。一个简便的方法是用GSettings bind 函数绑定设定关键词和目标属性,就像我们这里为转换设置做的。

  1. ...
  2. static void
  3. example_app_window_init (ExampleAppWindow *win)
  4. {
  5. ExampleAppWindowPrivate *priv;
  6. priv = example_app_window_get_instance_private (win);
  7. gtk_widget_init_template (GTK_WIDGET (win));
  8. priv->settings = g_settings_new ("org.gtk.exampleapp");
  9. g_settings_bind (priv->settings, "transition",
  10. priv->stack, "transition-type",
  11. G_SETTINGS_BIND_DEFAULT);
  12. }
  13. ...

(full source)

这个连接字体设置的代码有点儿复杂,因为我们没有对应的简单的目标属性,我们本没打算这么做。

至此,如果我们改变一个设置,程序将会有反应,比如用gsettings 命令行工具。当然,我们希望应用程序提供一个偏好对话框。所以干吧,我们的偏好对话框是GtkDialog 的子类,我们将使用我们已经用过的技术:templates,private structs, settingbindings。

让我们从模板开始。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <interface>
  3. <!-- interface-requires gtk+ 3.8 -->
  4. <template class="ExampleAppPrefs" parent="GtkDialog">
  5. <property name="title" translatable="yes">Preferences</property>
  6. <property name="resizable">False</property>
  7. <property name="modal">True</property>
  8. <child internal-child="vbox">
  9. <object class="GtkBox" id="vbox">
  10. <child>
  11. <object class="GtkGrid" id="grid">
  12. <property name="visible">True</property>
  13. <property name="margin">6</property>
  14. <property name="row-spacing">12</property>
  15. <property name="column-spacing">6</property>
  16. <child>
  17. <object class="GtkLabel" id="fontlabel">
  18. <property name="visible">True</property>
  19. <property name="label">_Font:</property>
  20. <property name="use-underline">True</property>
  21. <property name="mnemonic-widget">font</property>
  22. <property name="xalign">1</property>
  23. </object>
  24. <packing>
  25. <property name="left-attach">0</property>
  26. <property name="top-attach">0</property>
  27. </packing>
  28. </child>
  29. <child>
  30. <object class="GtkFontButton" id="font">
  31. <property name="visible">True</property>
  32. </object>
  33. <packing>
  34. <property name="left-attach">1</property>
  35. <property name="top-attach">0</property>
  36. </packing>
  37. </child>
  38. <child>
  39. <object class="GtkLabel" id="transitionlabel">
  40. <property name="visible">True</property>
  41. <property name="label">_Transition:</property>
  42. <property name="use-underline">True</property>
  43. <property name="mnemonic-widget">transition</property>
  44. <property name="xalign">1</property>
  45. </object>
  46. <packing>
  47. <property name="left-attach">0</property>
  48. <property name="top-attach">1</property>
  49. </packing>
  50. </child>
  51. <child>
  52. <object class="GtkComboBoxText" id="transition">
  53. <property name="visible">True</property>
  54. <items>
  55. <item translatable="yes" id="none">None</item>
  56. <item translatable="yes" id="crossfade">Fade</item>
  57. <item translatable="yes" id="slide-left-right">Slide</item>
  58. </items>
  59. </object>
  60. <packing>
  61. <property name="left-attach">1</property>
  62. <property name="top-attach">1</property>
  63. </packing>
  64. </child>
  65. </object>
  66. </child>
  67. </object>
  68. </child>
  69. </template>
  70. </interface>

接下来是对话框子类。

  1. #include <gtk/gtk.h>
  2. #include "exampleapp.h"
  3. #include "exampleappwin.h"
  4. #include "exampleappprefs.h"
  5. struct _ExampleAppPrefs
  6. {
  7. GtkDialog parent;
  8. };
  9. struct _ExampleAppPrefsClass
  10. {
  11. GtkDialogClass parent_class;
  12. };
  13. typedef struct _ExampleAppPrefsPrivate ExampleAppPrefsPrivate;
  14. struct _ExampleAppPrefsPrivate
  15. {
  16. GSettings *settings;
  17. GtkWidget *font;
  18. GtkWidget *transition;
  19. };
  20. G_DEFINE_TYPE_WITH_PRIVATE(ExampleAppPrefs, example_app_prefs, GTK_TYPE_DIALOG)
  21. static void
  22. example_app_prefs_init (ExampleAppPrefs *prefs)
  23. {
  24. ExampleAppPrefsPrivate *priv;
  25. priv = example_app_prefs_get_instance_private (prefs);
  26. gtk_widget_init_template (GTK_WIDGET (prefs));
  27. priv->settings = g_settings_new ("org.gtk.exampleapp");
  28. g_settings_bind (priv->settings, "font",
  29. priv->font, "font",
  30. G_SETTINGS_BIND_DEFAULT);
  31. g_settings_bind (priv->settings, "transition",
  32. priv->transition, "active-id",
  33. G_SETTINGS_BIND_DEFAULT);
  34. }
  35. static void
  36. example_app_prefs_dispose (GObject *object)
  37. {
  38. ExampleAppPrefsPrivate *priv;
  39. priv = example_app_prefs_get_instance_private (EXAMPLE_APP_PREFS (object));
  40. g_clear_object (&priv->settings);
  41. G_OBJECT_CLASS (example_app_prefs_parent_class)->dispose (object);
  42. }
  43. static void
  44. example_app_prefs_class_init (ExampleAppPrefsClass *class)
  45. {
  46. G_OBJECT_CLASS (class)->dispose = example_app_prefs_dispose;
  47. gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
  48. "/org/gtk/exampleapp/prefs.ui");
  49. gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppPrefs, font);
  50. gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppPrefs, transition);
  51. }
  52. ExampleAppPrefs *
  53. example_app_prefs_new (ExampleAppWindow *win)
  54. {
  55. return g_object_new (EXAMPLE_APP_PREFS_TYPE, "transient-for", win, "use-header-bar", TRUE, NULL);
  56. }

现在我们再看preferences_activated()函数,使它打开一个偏好对话框。

  1. ...
  2. static void
  3. preferences_activated (GSimpleAction *action,
  4. GVariant *parameter,
  5. gpointer app)
  6. {
  7. ExampleAppPrefs *prefs;
  8. GtkWindow *win;
  9. win = gtk_application_get_active_window (GTK_APPLICATION (app));
  10. prefs = example_app_prefs_new (EXAMPLE_APP_WINDOW (win));
  11. gtk_window_present (GTK_WINDOW (prefs));
  12. }
  13. ...

(full source)

完成所有这些工作后,我们的应用程序现在可以像这样显示一个偏好对话框:

getting-started-app6.png