[geany/geany] ce66dc: Improve user experience when creating new projects

Jiří Techet git-noreply at geany.org
Fri May 13 00:19:38 UTC 2022


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Tue, 15 Mar 2022 19:44:59 UTC
Commit:      ce66dc92231cf7fb4aa47e1846f4ab127b8ee873
             https://github.com/geany/geany/commit/ce66dc92231cf7fb4aa47e1846f4ab127b8ee873

Log Message:
-----------
Improve user experience when creating new projects

At the moment, when creating a project, the user is greeted with the
dialog containing:
1. Name
2. Filename
3. Base path

The user is expected to type the name of the project into (1), and then,
Geany tries to guess the base path and file name. The guess simply takes
the project directory specified in settings and appends the name. When the
project is located anywhere else than in the projects directory,
the guessed values are wrong and have to be entered manually which is
quite annoying. In addition, the dialog doesn't make it clear that the
user should start with (1), when he starts with (2) or (3), he has to
fill in all 3 values manually.

This patch adds another method of project creation which is more
suitable for creating projects from existing directory with source
files. There's a new "Project->New from Folder..." option that in
the first step asks user to provide the base path and based on this
path fills in the entries in the New Project dialog.

With this approach, Project->New from Folder...:
a. First pops up a open directory dialog to specify base path
b. After that, opens the (currently used) New Project dialog which
is pre-filled in the following way:
  1. Name: The last directory in base_path
  2. Filename: depending on "store project file inside the project base
directory" settings either in base_path/(1).geany or projects_dir/(1).geany
  3. Base path: path specified in (a)

This way, in most cases, the user will only have to select the base
directory in the first step and use the pre-filled values without
any modification no matter whether the project is stored in the projects
directory or not.

After this patch, there will be 2 different ways to create projects:
1. Project->New - more suitable for creating empty projects from scratch
inside the "projects" directory
2. Project->New from Folder - more suitable for creating projects from
existing sources


Modified Paths:
--------------
    data/geany.glade
    src/callbacks.c
    src/callbacks.h
    src/project.c
    src/project.h
    src/ui_utils.c
    src/ui_utils.h

Modified: data/geany.glade
17 lines changed, 17 insertions(+), 0 deletions(-)
===================================================================
@@ -704,6 +704,12 @@
     <property name="stock">gtk-find</property>
     <property name="icon_size">1</property>
   </object>
+  <object class="GtkImage" id="image7">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-new</property>
+    <property name="icon-size">1</property>
+  </object>
   <object class="GtkListStore" id="indent_mode_list">
     <columns>
       <!-- column-name item -->
@@ -7897,6 +7903,17 @@
                             <signal name="activate" handler="on_project_new1_activate" swapped="no"/>
                           </object>
                         </child>
+                        <child>
+                          <object class="GtkImageMenuItem" id="project_new_from_folder1">
+                            <property name="label" translatable="yes">New from _Folder...</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="image">image7</property>
+                            <property name="use_stock">False</property>
+                            <signal name="activate" handler="on_project_new_from_folder1_activate" swapped="no"/>
+                          </object>
+                        </child>
                         <child>
                           <object class="GtkImageMenuItem" id="project_open1">
                             <property name="label" translatable="yes">_Open...</property>


Modified: src/callbacks.c
8 lines changed, 7 insertions(+), 1 deletions(-)
===================================================================
@@ -1387,7 +1387,13 @@ void on_previous_message1_activate(GtkMenuItem *menuitem, gpointer user_data)
 
 void on_project_new1_activate(GtkMenuItem *menuitem, gpointer user_data)
 {
-	project_new();
+	project_new(FALSE);
+}
+
+
+void on_project_new_from_folder1_activate(GtkMenuItem *menuitem, gpointer user_data)
+{
+	project_new(TRUE);
 }
 
 


Modified: src/callbacks.h
2 lines changed, 2 insertions(+), 0 deletions(-)
===================================================================
@@ -144,6 +144,8 @@ void on_next_message1_activate(GtkMenuItem *menuitem, gpointer user_data);
 
 void on_project_new1_activate(GtkMenuItem *menuitem, gpointer user_data);
 
+void on_project_new_from_folder1_activate(GtkMenuItem *menuitem, gpointer user_data);
+
 void on_project_open1_activate(GtkMenuItem *menuitem, gpointer user_data);
 
 void on_project_close1_activate(GtkMenuItem *menuitem, gpointer user_data);


Modified: src/project.c
75 lines changed, 58 insertions(+), 17 deletions(-)
===================================================================
@@ -83,6 +83,8 @@ static gboolean update_config(const PropertyDialogElements *e, gboolean new_proj
 static void on_file_save_button_clicked(GtkButton *button, PropertyDialogElements *e);
 static gboolean load_config(const gchar *filename);
 static gboolean write_config(void);
+static void update_new_project_dlg(GtkEditable *editable, PropertyDialogElements *e,
+	const gchar *base_p);
 static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements *e);
 static void on_entries_changed(GtkEditable *editable, PropertyDialogElements *e);
 static void on_radio_long_line_custom_toggled(GtkToggleButton *radio, GtkWidget *spin_long_line);
@@ -141,7 +143,7 @@ static gboolean handle_current_session(void)
 /* TODO: this should be ported to Glade like the project preferences dialog,
  * then we can get rid of the PropertyDialogElements struct altogether as
  * widgets pointers can be accessed through ui_lookup_widget(). */
-void project_new(void)
+void project_new(gboolean from_folder)
 {
 	GtkWidget *vbox;
 	GtkWidget *table;
@@ -150,8 +152,16 @@ void project_new(void)
 	GtkWidget *bbox;
 	GtkWidget *label;
 	gchar *tooltip;
+	gchar *base_path = NULL;
 	PropertyDialogElements e = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, FALSE };
 
+	if (from_folder)
+	{
+		base_path = ui_get_project_directory(local_prefs.project_file_path);
+		if (!base_path)
+			return;
+	}
+
 	e.dialog = gtk_dialog_new_with_buttons(_("New Project"), GTK_WINDOW(main_widgets.window),
 										 GTK_DIALOG_DESTROY_WITH_PARENT,
 										 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
@@ -218,13 +228,20 @@ void project_new(void)
 
 	gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
 
-	/* signals */
-	g_signal_connect(e.name, "changed", G_CALLBACK(on_name_entry_changed), &e);
-	/* run the callback manually to initialise the base_path and file_name fields */
-	on_name_entry_changed(GTK_EDITABLE(e.name), &e);
+	if (base_path)
+	{
+		update_new_project_dlg(GTK_EDITABLE(e.name), &e, base_path);
+		g_free(base_path);
+	}
+	else
+	{
+		/* signals */
+		g_signal_connect(e.name, "changed", G_CALLBACK(on_name_entry_changed), &e);
+		g_signal_connect(e.file_name, "changed", G_CALLBACK(on_entries_changed), &e);
+		g_signal_connect(e.base_path, "changed", G_CALLBACK(on_entries_changed), &e);
 
-	g_signal_connect(e.file_name, "changed", G_CALLBACK(on_entries_changed), &e);
-	g_signal_connect(e.base_path, "changed", G_CALLBACK(on_entries_changed), &e);
+		update_new_project_dlg(GTK_EDITABLE(e.name), &e, NULL);
+	}
 
 	gtk_widget_show_all(e.dialog);
 	run_new_dialog(&e);
@@ -950,8 +967,9 @@ static void on_file_save_button_clicked(GtkButton *button, PropertyDialogElement
 }
 
 
-/* sets the project base path and the project file name according to the project name */
-static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements *e)
+/* sets the New Project dialog entries according to the base path or project name */
+static void update_new_project_dlg(GtkEditable *editable, PropertyDialogElements *e,
+	const gchar *base_p)
 {
 	gchar *base_path;
 	gchar *file_name;
@@ -961,24 +979,41 @@ static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements
 	if (e->entries_modified)
 		return;
 
-	name = gtk_editable_get_chars(editable, 0, -1);
-	if (!EMPTY(name))
+	if (!EMPTY(base_p))
 	{
-		base_path = g_strconcat(project_dir, G_DIR_SEPARATOR_S,
-			name, G_DIR_SEPARATOR_S, NULL);
+		gchar *name = g_path_get_basename(base_p);
+
+		base_path = g_strdup(base_p);
+		gtk_entry_set_text(GTK_ENTRY(e->name), name);
 		if (project_prefs.project_file_in_basedir)
-			file_name = g_strconcat(project_dir, G_DIR_SEPARATOR_S, name, G_DIR_SEPARATOR_S,
+			file_name = g_strconcat(base_path, G_DIR_SEPARATOR_S,
 				name, "." GEANY_PROJECT_EXT, NULL);
 		else
 			file_name = g_strconcat(project_dir, G_DIR_SEPARATOR_S,
 				name, "." GEANY_PROJECT_EXT, NULL);
+		g_free(name);
 	}
 	else
 	{
-		base_path = g_strconcat(project_dir, G_DIR_SEPARATOR_S, NULL);
-		file_name = g_strconcat(project_dir, G_DIR_SEPARATOR_S, NULL);
+		gchar *name = gtk_editable_get_chars(editable, 0, -1);
+		if (!EMPTY(name))
+		{
+			base_path = g_strconcat(project_dir, G_DIR_SEPARATOR_S,
+				name, G_DIR_SEPARATOR_S, NULL);
+			if (project_prefs.project_file_in_basedir)
+				file_name = g_strconcat(project_dir, G_DIR_SEPARATOR_S, name, G_DIR_SEPARATOR_S,
+					name, "." GEANY_PROJECT_EXT, NULL);
+			else
+				file_name = g_strconcat(project_dir, G_DIR_SEPARATOR_S,
+					name, "." GEANY_PROJECT_EXT, NULL);
+		}
+		else
+		{
+			base_path = g_strconcat(project_dir, G_DIR_SEPARATOR_S, NULL);
+			file_name = g_strconcat(project_dir, G_DIR_SEPARATOR_S, NULL);
+		}
+		g_free(name);
 	}
-	g_free(name);
 
 	gtk_entry_set_text(GTK_ENTRY(e->base_path), base_path);
 	gtk_entry_set_text(GTK_ENTRY(e->file_name), file_name);
@@ -990,6 +1025,12 @@ static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements
 }
 
 
+static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements *e)
+{
+	update_new_project_dlg(editable, e, NULL);
+}
+
+
 static void on_entries_changed(GtkEditable *editable, PropertyDialogElements *e)
 {
 	e->entries_modified = TRUE;


Modified: src/project.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -66,7 +66,7 @@ void project_init(void);
 void project_finalize(void);
 
 
-void project_new(void);
+void project_new(gboolean from_folder);
 
 void project_open(void);
 


Modified: src/ui_utils.c
15 lines changed, 15 insertions(+), 0 deletions(-)
===================================================================
@@ -1999,6 +1999,21 @@ static gchar *run_file_chooser(const gchar *title, GtkFileChooserAction action,
 #endif
 
 
+gchar *ui_get_project_directory(const gchar *path)
+{
+	gchar *utf8_path;
+	const gchar *title = _("Select Project Base Path");
+
+#ifdef G_OS_WIN32
+	utf8_path = win32_show_folder_dialog(ui_widgets.prefs_dialog, title, path);
+#else
+	utf8_path = run_file_chooser(title, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, path);
+#endif
+
+	return utf8_path;
+}
+
+
 static void ui_path_box_open_clicked(GtkButton *button, gpointer user_data)
 {
 	GtkFileChooserAction action = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "action"));


Modified: src/ui_utils.h
2 lines changed, 2 insertions(+), 0 deletions(-)
===================================================================
@@ -365,6 +365,8 @@ gint ui_encodings_combo_box_get_active_encoding(GtkComboBox *combo);
 
 gboolean ui_encodings_combo_box_set_active_encoding(GtkComboBox *combo, gint enc);
 
+gchar *ui_get_project_directory(const gchar *path);
+
 #endif /* GEANY_PRIVATE */
 
 G_END_DECLS



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list