瀏覽代碼

Fin du tutoriel officiel

vetetix 1 月之前
父節點
當前提交
66af7e3187
共有 4 個文件被更改,包括 231 次插入76 次删除
  1. 12 0
      data/com.example.TextViewer.gschema.xml
  2. 84 3
      src/text-viewer-application.c
  3. 118 64
      src/text-viewer-window.c
  4. 17 9
      src/text-viewer-window.ui

+ 12 - 0
data/com.example.TextViewer.gschema.xml

@@ -1,5 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <schemalist gettext-domain="text-viewer">
 	<schema id="com.example.TextViewer" path="/com/example/TextViewer/">
+    <key name="window-width" type="i">
+      <default>600</default>
+    </key>
+    <key name="window-height" type="i">
+      <default>400</default>
+    </key>
+    <key name="window-maximized" type="b">
+      <default>false</default>
+    </key>
+    <key name="dark-mode" type="b">
+      <default>false</default>
+    </key>
 	</schema>
 </schemalist>

+ 84 - 3
src/text-viewer-application.c

@@ -26,7 +26,9 @@
 
 struct _TextViewerApplication
 {
-	AdwApplication parent_instance;
+  AdwApplication parent_instance;
+
+  GSettings *settings;
 };
 
 G_DEFINE_FINAL_TYPE (TextViewerApplication, text_viewer_application, ADW_TYPE_APPLICATION)
@@ -61,12 +63,73 @@ text_viewer_application_activate (GApplication *app)
 	gtk_window_present (window);
 }
 
+static void
+text_viewer_application_dispose (GObject *gobject)
+{
+  TextViewerApplication *self = TEXT_VIEWER_APPLICATION (gobject);
+
+  g_clear_object (&self->settings);
+
+  G_OBJECT_CLASS (text_viewer_application_parent_class)->dispose (gobject);
+}
+
+static void
+text_viewer_application_startup (GApplication *app)
+{
+  TextViewerApplication *self = TEXT_VIEWER_APPLICATION (app);
+
+  G_APPLICATION_CLASS (text_viewer_application_parent_class)->startup (app);
+
+  gboolean dark_mode = g_settings_get_boolean (self->settings, "dark-mode");
+  AdwStyleManager *style_manager = adw_style_manager_get_default ();
+  if (dark_mode)
+    adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_FORCE_DARK);
+  else
+    adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_DEFAULT);
+}
+
 static void
 text_viewer_application_class_init (TextViewerApplicationClass *klass)
 {
-	GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
+  GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->dispose = text_viewer_application_dispose;
 
-	app_class->activate = text_viewer_application_activate;
+  app_class->activate = text_viewer_application_activate;
+  app_class->startup = text_viewer_application_startup;
+}
+static void
+toggle_dark_mode (GSimpleAction *action,
+                  GVariant      *parameter G_GNUC_UNUSED,
+                  gpointer       user_data G_GNUC_UNUSED)
+{
+  GVariant *state = g_action_get_state (G_ACTION (action));
+  gboolean old_state = g_variant_get_boolean (state);
+  gboolean new_state = !old_state;
+
+  g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
+
+  g_variant_unref (state);
+}
+
+static void
+change_color_scheme (GSimpleAction         *action,
+                     GVariant              *new_state,
+                     TextViewerApplication *self)
+{
+  gboolean dark_mode = g_variant_get_boolean (new_state);
+
+  AdwStyleManager *style_manager = adw_style_manager_get_default ();
+
+  if (dark_mode)
+    adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_FORCE_DARK);
+  else
+    adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_DEFAULT);
+
+  g_simple_action_set_state (action, new_state);
+
+  g_settings_set_boolean (self->settings, "dark-mode", dark_mode);
 }
 
 static void
@@ -113,6 +176,24 @@ static const GActionEntry app_actions[] = {
 static void
 text_viewer_application_init (TextViewerApplication *self)
 {
+  //gtk_init();
+  self->settings = g_settings_new ("com.example.TextViewer");
+
+  gboolean dark_mode = g_settings_get_boolean (self->settings, "dark-mode");
+
+  /*AdwStyleManager *style_manager = adw_style_manager_get_default ();
+  if (dark_mode)
+    adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_FORCE_DARK);
+  else
+    adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_DEFAULT);
+   */
+
+  g_autoptr (GSimpleAction) dark_action =
+    g_simple_action_new_stateful ("dark-mode", NULL, g_variant_new_boolean (dark_mode));
+  g_signal_connect (dark_action, "activate", G_CALLBACK (toggle_dark_mode), self);
+  g_signal_connect (dark_action, "change-state", G_CALLBACK (change_color_scheme), self);
+  g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (dark_action));
+
   g_action_map_add_action_entries (G_ACTION_MAP (self),
                                    app_actions,
                                    G_N_ELEMENTS (app_actions),

+ 118 - 64
src/text-viewer-window.c

@@ -26,23 +26,39 @@ struct _TextViewerWindow
 {
   AdwApplicationWindow  parent_instance;
 
+  GSettings     *settings;
+
   /* Template widgets */
-  AdwHeaderBar  *header_bar;
-  GtkTextView   *main_text_view;
-  GtkButton     *open_button;
-  GtkLabel      *cursor_pos;
+  AdwHeaderBar    *header_bar;
+  AdwToastOverlay *toast_overlay;
+  GtkTextView     *main_text_view;
+  GtkButton       *open_button;
+  GtkLabel        *cursor_pos;
 };
 
 G_DEFINE_FINAL_TYPE (TextViewerWindow, text_viewer_window, ADW_TYPE_APPLICATION_WINDOW)
 
+static void
+text_viewer_window_finalize (GObject *gobject)
+{
+  TextViewerWindow *self = TEXT_VIEWER_WINDOW (gobject);
+
+  g_clear_object (&self->settings);
+
+  G_OBJECT_CLASS (text_viewer_window_parent_class)->finalize (gobject);
+}
+
 static void
 text_viewer_window_class_init (TextViewerWindowClass *klass)
 {
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = text_viewer_window_finalize;
 
   gtk_widget_class_set_template_from_resource (
-        widget_class,
-        "/com/example/TextViewer/text-viewer-window.ui");
+                              widget_class,
+                              "/com/example/TextViewer/text-viewer-window.ui");
   gtk_widget_class_bind_template_child (widget_class,
                                         TextViewerWindow,
                                         header_bar);
@@ -55,6 +71,9 @@ text_viewer_window_class_init (TextViewerWindowClass *klass)
   gtk_widget_class_bind_template_child (widget_class,
                                         TextViewerWindow,
                                         cursor_pos);
+  gtk_widget_class_bind_template_child (widget_class,
+                                        TextViewerWindow,
+                                        toast_overlay);
 }
 
 static void
@@ -63,16 +82,17 @@ text_viewer_window__update_cursor_position (GtkTextBuffer    *buffer,
                                             TextViewerWindow *self)
 {
   int cursor_pos = 0;
+  GtkTextIter iter;
+  g_autofree char *cursor_str;
 
   // Retrieve the value of the "cursor-position" property
   g_object_get (buffer, "cursor-position", &cursor_pos, NULL);
 
   // Construct the text iterator for the position of the cursor
-  GtkTextIter iter;
   gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor_pos);
 
   // Set the new contents of the label
-  g_autofree char *cursor_str =
+  cursor_str =
     g_strdup_printf ("Ln %d, Col %d",
                      gtk_text_iter_get_line (&iter) + 1,
                      gtk_text_iter_get_line_offset (&iter) + 1);
@@ -86,11 +106,14 @@ open_file_complete (GObject          *source_object,
                     TextViewerWindow *self)
 {
   GFile *file = G_FILE (source_object);
-
   g_autofree char *contents = NULL;
   gsize length = 0;
-
   g_autoptr (GError) error = NULL;
+  g_autofree char *display_name = NULL;
+  g_autoptr (GFileInfo) info;
+  GtkTextBuffer *buffer;
+  GtkTextIter start;
+  g_autofree char *success_msg;
 
   // Complete the asynchronous operation; this function will either
   // give you the contents of the file as a byte array, or will
@@ -103,13 +126,11 @@ open_file_complete (GObject          *source_object,
                                &error);
 
   // Query the display name for the file
-  g_autofree char *display_name = NULL;
-  g_autoptr (GFileInfo) info =
-    g_file_query_info (file,
-                       "standard::display-name",
-                       G_FILE_QUERY_INFO_NONE,
-                       NULL,
-                       NULL);
+  info = g_file_query_info (file,
+                             "standard::display-name",
+                             G_FILE_QUERY_INFO_NONE,
+                             NULL,
+                             NULL);
   if (info != NULL)
     {
       display_name =
@@ -123,35 +144,42 @@ open_file_complete (GObject          *source_object,
   // In case of error, print a warning to the standard error output
   if (error != NULL)
     {
-      g_printerr ("Unable to open “%s”: %s\n",
-                  g_file_peek_path (file),
-                  error->message);
+      g_autofree char *msg =
+        g_strdup_printf ("Unable to open “%s”", display_name);
+
+      adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (msg));
       return;
     }
 
   // Ensure that the file is encoded with UTF-8
   if (!g_utf8_validate (contents, length, NULL))
     {
-      g_printerr ("Unable to load the contents of “%s”: "
-                  "the file is not encoded with UTF-8\n",
-                  g_file_peek_path (file));
+      g_autofree char *msg =
+        g_strdup_printf ("Invalid text encoding for “%s”", display_name);
+
+      adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (msg));
       return;
     }
 
   // Retrieve the GtkTextBuffer instance that stores the
   // text displayed by the GtkTextView widget
-  GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
+  buffer = gtk_text_view_get_buffer (self->main_text_view);
 
   // Set the text using the contents of the file
   gtk_text_buffer_set_text (buffer, contents, length);
 
   // Reposition the cursor so it's at the start of the text
-  GtkTextIter start;
   gtk_text_buffer_get_start_iter (buffer, &start);
   gtk_text_buffer_place_cursor (buffer, &start);
 
   // Set the title using the display name
   gtk_window_set_title (GTK_WINDOW (self), display_name);
+
+  // Show a toast for the successful loading
+  success_msg =
+    g_strdup_printf ("Opened “%s”", display_name);
+
+  adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (success_msg));
 }
 
 static void
@@ -200,18 +228,21 @@ save_file_complete (GObject      *source_object,
                     gpointer      user_data)
 {
   GFile *file = G_FILE (source_object);
-
+  TextViewerWindow *self = user_data;
   g_autoptr (GError) error =  NULL;
+  g_autofree char *display_name = NULL;
+  g_autoptr (GFileInfo) info;
+  g_autofree char *msg = NULL;
+
   g_file_replace_contents_finish (file, result, NULL, &error);
 
   // Query the display name for the file
-  g_autofree char *display_name = NULL;
-  g_autoptr (GFileInfo) info =
-  g_file_query_info (file,
-                     "standard::display-name",
-                     G_FILE_QUERY_INFO_NONE,
-                     NULL,
-                     NULL);
+  info = g_file_query_info (file,
+                             "standard::display-name",
+                             G_FILE_QUERY_INFO_NONE,
+                             NULL,
+                             NULL);
+
   if (info != NULL)
     {
       display_name =
@@ -228,40 +259,49 @@ save_file_complete (GObject      *source_object,
                   display_name,
                   error->message);
     }
+
+  if (error != NULL)
+    msg = g_strdup_printf ("Unable to save as “%s”", display_name);
+  else
+    msg = g_strdup_printf ("Saved as “%s”", display_name);
+
+  adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (msg));
 }
 
 static void
 save_file (TextViewerWindow *self,
             GFile            *file)
 {
-   GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
-
-   // Retrieve the iterator at the start of the buffer
-   GtkTextIter start;
-   gtk_text_buffer_get_start_iter (buffer, &start);
-
-   // Retrieve the iterator at the end of the buffer
-   GtkTextIter end;
-   gtk_text_buffer_get_end_iter (buffer, &end);
-
-   // Retrieve all the visible text between the two bounds
-   char *text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
-
-   // If there is nothing to save, return early
-   if (text == NULL)
-     return;
-
-   g_autoptr(GBytes) bytes = g_bytes_new_take (text, strlen (text));
-
-   // Start the asynchronous operation to save the data into the file
-   g_file_replace_contents_bytes_async (file,
-                                        bytes,
-                                        NULL,
-                                        FALSE,
-                                        G_FILE_CREATE_NONE,
-                                        NULL,
-                                        save_file_complete,
-                                        self);
+  GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
+  GtkTextIter end;
+  char *text;
+  g_autoptr(GBytes) bytes;
+
+  // Retrieve the iterator at the start of the buffer
+  GtkTextIter start;
+  gtk_text_buffer_get_start_iter (buffer, &start);
+
+  // Retrieve the iterator at the end of the buffer
+  gtk_text_buffer_get_end_iter (buffer, &end);
+
+  // Retrieve all the visible text between the two bounds
+  text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+  // If there is nothing to save, return early
+  if (text == NULL)
+   return;
+
+  bytes = g_bytes_new_take (text, strlen (text));
+
+  // Start the asynchronous operation to save the data into the file
+  g_file_replace_contents_bytes_async (file,
+                                      bytes,
+                                      NULL,
+                                      FALSE,
+                                      G_FILE_CREATE_NONE,
+                                      NULL,
+                                      save_file_complete,
+                                      self);
 }
 
 static void
@@ -297,9 +337,12 @@ text_viewer_window__save_file_dialog (GAction *action,
 static void
 text_viewer_window_init (TextViewerWindow *self)
 {
+  g_autoptr (GSimpleAction) open_action = g_simple_action_new ("open", NULL);
+  g_autoptr (GSimpleAction) save_action = g_simple_action_new ("save-as", NULL);
+  GtkTextBuffer *buffer;
+
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  g_autoptr (GSimpleAction) open_action = g_simple_action_new ("open", NULL);
   g_signal_connect (open_action,
                     "activate",
                     G_CALLBACK (text_viewer_window__open_file_dialog),
@@ -307,13 +350,24 @@ text_viewer_window_init (TextViewerWindow *self)
   g_action_map_add_action (G_ACTION_MAP (self),
                            G_ACTION (open_action));
 
-  g_autoptr (GSimpleAction) save_action = g_simple_action_new ("save-as", NULL);
   g_signal_connect (save_action, "activate", G_CALLBACK (text_viewer_window__save_file_dialog), self);
   g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (save_action));
 
-  GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
+  buffer = gtk_text_view_get_buffer (self->main_text_view);
   g_signal_connect (buffer,
                     "notify::cursor-position",
                     G_CALLBACK (text_viewer_window__update_cursor_position),
                     self);
+
+  self->settings = g_settings_new ("com.example.TextViewer");
+
+  g_settings_bind (self->settings, "window-width",
+                   G_OBJECT (self), "default-width",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind (self->settings, "window-height",
+                   G_OBJECT (self), "default-height",
+                   G_SETTINGS_BIND_DEFAULT);
+  g_settings_bind (self->settings, "window-maximized",
+                   G_OBJECT (self), "maximized",
+                   G_SETTINGS_BIND_DEFAULT);
 }

+ 17 - 9
src/text-viewer-window.ui

@@ -39,16 +39,20 @@
           </object>
         </child>
         <property name="content">
-          <object class="GtkScrolledWindow">
-            <property name="hexpand">true</property>
-            <property name="vexpand">true</property>
-            <property name="margin-top">6</property>
-            <property name="margin-bottom">6</property>
-            <property name="margin-start">6</property>
-            <property name="margin-end">6</property>
+          <object class="AdwToastOverlay" id="toast_overlay">
             <property name="child">
-              <object class="GtkTextView" id="main_text_view">
-                <property name="monospace">true</property>
+              <object class="GtkScrolledWindow">
+                <property name="hexpand">true</property>
+                <property name="vexpand">true</property>
+                <property name="margin-top">6</property>
+                <property name="margin-bottom">6</property>
+                <property name="margin-start">6</property>
+                <property name="margin-end">6</property>
+                <property name="child">
+                  <object class="GtkTextView" id="main_text_view">
+                    <property name="monospace">true</property>
+                  </object>
+                </property>
               </object>
             </property>
           </object>
@@ -66,6 +70,10 @@
         <attribute name="label" translatable="yes">_Save as...</attribute>
         <attribute name="action">win.save-as</attribute>
       </item>
+      <item>
+        <attribute name="label" translatable="yes">_Dark mode</attribute>
+        <attribute name="action">app.dark-mode</attribute>
+      </item>
       <item>
         <attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
         <attribute name="action">app.shortcuts</attribute>