Skip to content

Commit

Permalink
Form Validation Message (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
alainm23 authored Oct 17, 2024
1 parent c442bc1 commit 4b7ba83
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 32 deletions.
26 changes: 20 additions & 6 deletions data/Application.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
@keyframes fancy-turn {
0% { -gtk-icon-transform: rotate(0deg); }
25% { -gtk-icon-transform: rotate(-30deg); }
50% { -gtk-icon-transform: rotate(0deg); }
75% { -gtk-icon-transform: rotate(30deg); }
100% { -gtk-icon-transform: rotate(0deg); }
0% {
-gtk-icon-transform: rotate(0deg);
}

25% {
-gtk-icon-transform: rotate(-30deg);
}

50% {
-gtk-icon-transform: rotate(0deg);
}

75% {
-gtk-icon-transform: rotate(30deg);
}

100% {
-gtk-icon-transform: rotate(0deg);
}
}

.fancy-turn.animation {
Expand All @@ -18,4 +32,4 @@

.fw-500 {
font-weight: 500;
}
}
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ executable(
'src/Views/Developer.vala',
'src/Views/Form.vala',
'src/Views/Success.vala',
'src/Widgets/InvalidLabel.vala',
'src/Widgets/Stepper.vala',
dependencies: deps,
install: true
Expand Down
1 change: 1 addition & 0 deletions po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ src/MainWindow.vala
src/Views/Developer.vala
src/Views/Form.vala
src/Views/Success.vala
src/Widgets/InvalidLabel.vala
src/Widgets/Stepper.vala
9 changes: 8 additions & 1 deletion src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,15 @@ public class MainWindow : Gtk.ApplicationWindow {
});
main_box.append (form_box);

var scrolled_window = new Gtk.ScrolledWindow () {
child = main_box,
vscrollbar_policy = NEVER,
hscrollbar_policy = NEVER
};

var toolbar_view = new Adw.ToolbarView ();
toolbar_view.add_top_bar (headerbar);
toolbar_view.content = main_box;
toolbar_view.content = scrolled_window;

child = toolbar_view;

Expand Down Expand Up @@ -126,6 +132,7 @@ public class MainWindow : Gtk.ApplicationWindow {

form_view.developer_name = name;
form_view.developer_email = email;
form_view.focus_name ();
});

success_view.back.connect (() => {
Expand Down
39 changes: 34 additions & 5 deletions src/Views/Developer.vala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ public class Views.Developer : Adw.Bin {

public signal void next (string name, string email);

public bool is_valid {
get {
return name_entry.is_valid && email_entry.is_valid;
}
}

construct {
Regex? email_regex = null;
try {
Expand All @@ -23,11 +29,19 @@ public class Views.Developer : Adw.Bin {
text = GLib.Environment.get_real_name ()
};

var name_invalid = new Widgets.InvalidLabel () {
text = _("This field is required")
};

email_entry = new Granite.ValidatedEntry () {
regex = email_regex,
margin_top = 6
};

var email_invalid = new Widgets.InvalidLabel () {
text = _("The email is invalid")
};

next_button = new Gtk.Button.with_label (_("Next")) {
margin_bottom = 32,
sensitive = false,
Expand All @@ -43,8 +57,10 @@ public class Views.Developer : Adw.Bin {
});
form_box.append (new Granite.HeaderLabel (_("Name:")));
form_box.append (name_entry);
form_box.append (name_invalid);
form_box.append (new Granite.HeaderLabel (_("Email:")));
form_box.append (email_entry);
form_box.append (email_invalid);
form_box.append (next_button);

var content_box = new Adw.Bin () {
Expand All @@ -56,16 +72,29 @@ public class Views.Developer : Adw.Bin {

child = content_box;

name_entry.changed.connect (check_valid);
email_entry.changed.connect (check_valid);
name_entry.changed.connect (() => {
check_valid ();
name_invalid.reveal_child = !name_entry.is_valid;
});

next_button.clicked.connect (() => {
next (name_entry.text, email_entry.text);
email_entry.changed.connect (() => {
check_valid ();
email_invalid.reveal_child = !email_entry.is_valid;
});

name_entry.activate.connect (go_next);
email_entry.activate.connect (go_next);
next_button.clicked.connect (go_next);
}

private void go_next () {
if (is_valid) {
next (name_entry.text, email_entry.text);
}
}

private void check_valid () {
next_button.sensitive = name_entry.is_valid && email_entry.is_valid;
next_button.sensitive = is_valid;
}

public void reset_form () {
Expand Down
103 changes: 83 additions & 20 deletions src/Views/Form.vala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public class Views.Form : Adw.Bin {
public string developer_name { get; set; }
public string developer_email { get; set; }

public bool is_valid {
get {
return project_name_entry.is_valid && identifier_entry.is_valid && location_entry.text.length > 0;
}
}

construct {
Regex? project_name_regex = null;
Regex? identifier_regex = null;
Expand All @@ -26,31 +32,63 @@ public class Views.Form : Adw.Bin {
critical (e.message);
}

var project_name_header = new Granite.HeaderLabel (_("Project Name:")) {
valign = CENTER
};

var project_name_info = new Gtk.MenuButton () {
can_focus = false,
hexpand = true,
halign = END,
icon_name = "dialog-information-symbolic",
popover = build_info_popover (_("A unique name that is used for the project folder and other resources. The name should be in lower case without spaces and should not start with a number"))
};
project_name_info.add_css_class (Granite.STYLE_CLASS_DIM_LABEL);
project_name_info.add_css_class (Granite.STYLE_CLASS_FLAT);

var project_name_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
margin_top = 12
};
project_name_box.append (project_name_header);
project_name_box.append (project_name_info);

project_name_entry = new Granite.ValidatedEntry () {
regex = project_name_regex,
margin_top = 6
};

var project_name_description = new Gtk.Label (_("A unique name that is used for the project folder and other resources. The name should be in lower case without spaces and should not start with a number.")) {
wrap = true,
xalign = 0,
margin_top = 3
var project_name_invalid = new Widgets.InvalidLabel () {
text = _("Project name must start with a lowercase letter and contain only letters and numbers")
};
project_name_description.add_css_class (Granite.STYLE_CLASS_DIM_LABEL);
project_name_description.add_css_class (Granite.STYLE_CLASS_SMALL_LABEL);

var identifier_header = new Granite.HeaderLabel (_("Organization Identifier:")) {
valign = CENTER
};

var identifier_info = new Gtk.MenuButton () {
can_focus = false,
hexpand = true,
halign = END,
icon_name = "dialog-information-symbolic",
popover = build_info_popover (_("A reverse domain-name identifier used to identify the application, such as 'io.github.username'. It may not contain dashes"))
};
identifier_info.add_css_class (Granite.STYLE_CLASS_DIM_LABEL);
identifier_info.add_css_class (Granite.STYLE_CLASS_FLAT);

var identifier_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
margin_top = 12
};
identifier_box.append (identifier_header);
identifier_box.append (identifier_info);

identifier_entry = new Granite.ValidatedEntry () {
regex = identifier_regex,
margin_top = 6
};

var identifier_description = new Gtk.Label (_("A reverse domain-name identifier used to identify the application, such as 'io.github.username'. It may not contain dashes.")) {
wrap = true,
xalign = 0,
margin_top = 3
var identifier_invalid = new Widgets.InvalidLabel () {
text = _("App ID must start with a lowercase letter, use dots to separate parts, contain only letters and numbers, and replace hyphens (-) with underscores (_)")
};
identifier_description.add_css_class (Granite.STYLE_CLASS_DIM_LABEL);
identifier_description.add_css_class (Granite.STYLE_CLASS_SMALL_LABEL);

application_id_entry = new Gtk.Entry () {
margin_top = 6,
Expand Down Expand Up @@ -92,6 +130,7 @@ public class Views.Form : Adw.Bin {
vexpand = true,
valign = END,
margin_bottom = 32,
margin_top = 12
};
buttons_box.append (back_button);
buttons_box.append (create_button);
Expand All @@ -101,12 +140,12 @@ public class Views.Form : Adw.Bin {
halign = START,
css_classes = { Granite.STYLE_CLASS_H1_LABEL }
});
form_box.append (new Granite.HeaderLabel (_("Project Name:")));
form_box.append (project_name_box);
form_box.append (project_name_entry);
// form_box.append (project_name_description);
form_box.append (new Granite.HeaderLabel (_("Organization Identifier:")));
form_box.append (project_name_invalid);
form_box.append (identifier_box);
form_box.append (identifier_entry);
// form_box.append (identifier_description);
form_box.append (identifier_invalid);
form_box.append (new Granite.HeaderLabel (_("Application ID:")));
form_box.append (application_id_entry);
form_box.append (new Granite.HeaderLabel (_("Location:")));
Expand All @@ -133,16 +172,18 @@ public class Views.Form : Adw.Bin {

project_name_entry.changed.connect (() => {
application_id_entry.text = identifier_entry.text + "." + project_name_entry.text;
create_button.sensitive = project_name_entry.is_valid && identifier_entry.is_valid && location_entry.text.length > 0;
create_button.sensitive = is_valid;
project_name_invalid.reveal_child = !project_name_entry.is_valid;
});

identifier_entry.changed.connect (() => {
application_id_entry.text = identifier_entry.text + "." + project_name_entry.text;
create_button.sensitive = project_name_entry.is_valid && identifier_entry.is_valid && location_entry.text.length > 0;
create_button.sensitive = is_valid;
identifier_invalid.reveal_child = !identifier_entry.is_valid;
});

location_entry.changed.connect (() => {
create_button.sensitive = project_name_entry.is_valid && identifier_entry.is_valid && location_entry.text.length > 0;
create_button.sensitive = is_valid;
});

location_entry.icon_release.connect ((icon_pos) => {
Expand Down Expand Up @@ -285,7 +326,7 @@ public class Views.Form : Adw.Bin {
}
}

void rename_file (string old_name, string new_name) {
private void rename_file (string old_name, string new_name) {
try {
GLib.File old_file = GLib.File.new_for_path (old_name);
GLib.File new_file = GLib.File.new_for_path (new_name);
Expand All @@ -294,4 +335,26 @@ public class Views.Form : Adw.Bin {
debug (e.message);
}
}

private Gtk.Popover build_info_popover (string text) {
var label = new Gtk.Label (text) {
wrap = true,
margin_top = 6,
margin_bottom = 6,
margin_start = 6,
margin_end = 6,
max_width_chars = 24,
justify = CENTER
};

var popover = new Gtk.Popover () {
child = label
};

return popover;
}

public void focus_name () {
project_name_entry.grab_focus ();
}
}
45 changes: 45 additions & 0 deletions src/Widgets/InvalidLabel.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2024 Alain <[email protected]>
*/

public class Widgets.InvalidLabel : Gtk.Grid {
private Gtk.Label text_label;
private Gtk.Revealer label_revealer;

public string text {
set {
text_label.label = value;
}

get {
return text_label.label;
}
}

public bool reveal_child {
set {
label_revealer.reveal_child = value;
}

get {
return label_revealer.reveal_child;
}
}

construct {
text_label = new Gtk.Label (null) {
xalign = 0,
margin_top = 6,
wrap = true
};
text_label.add_css_class ("error");
text_label.add_css_class (Granite.STYLE_CLASS_SMALL_LABEL);

label_revealer = new Gtk.Revealer () {
child = text_label
};

attach (label_revealer, 0, 0, 1, 1);
}
}

0 comments on commit 4b7ba83

Please sign in to comment.