• 一个偏好对话框

    一个偏好对话框

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

    我们将用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