Skip to content

Commit 706f2b5

Browse files
committed
feat: migrate entry editor from inline UI to dialog-based tag/image/location management
1 parent 7cb06d8 commit 706f2b5

5 files changed

Lines changed: 505 additions & 324 deletions

File tree

meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ sources = [
3131
'src/models/DataManager.vala',
3232
'src/models/DataModels.vala',
3333
'src/dialogs/AddTagDialog.vala',
34+
'src/dialogs/EntryTagsDialog.vala',
35+
'src/dialogs/EntryImagesDialog.vala',
36+
'src/dialogs/EntryLocationDialog.vala',
3437
'src/services/JsonObject.vala',
3538
'src/services/MapStyle.vala',
3639
'src/services/ReminderService.vala',

src/dialogs/EntryImagesDialog.vala

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
namespace Notejot {
2+
public class EntryImagesDialog : He.Window {
3+
public signal void response (int response_id);
4+
5+
private GLib.List<string> image_paths;
6+
private Gtk.FlowBox image_preview_box;
7+
private He.Button add_image_button;
8+
9+
public EntryImagesDialog (Gtk.Window parent, GLib.List<string> initial_paths) {
10+
Object (
11+
parent : parent,
12+
modal: true,
13+
default_width: 520,
14+
resizable: false
15+
);
16+
17+
this.add_css_class ("dialog-content");
18+
this.image_paths = new GLib.List<string> ();
19+
foreach (var p in initial_paths) { this.image_paths.append (p); }
20+
21+
// Header
22+
var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
23+
header_box.set_margin_top (12);
24+
header_box.set_margin_start (12);
25+
header_box.set_margin_end (12);
26+
header_box.set_margin_bottom (12);
27+
28+
var title_label = new Gtk.Label (_("Manage Images"));
29+
title_label.add_css_class ("title-3");
30+
header_box.append (title_label);
31+
header_box.append (new Gtk.Label ("") { hexpand = true });
32+
33+
var close_button = new He.Button ("window-close-symbolic", "");
34+
close_button.tooltip_text = _("Close");
35+
close_button.is_disclosure = true;
36+
close_button.clicked.connect (() => {
37+
response (Gtk.ResponseType.CANCEL);
38+
this.close ();
39+
});
40+
header_box.append (close_button);
41+
42+
var winhandle = new Gtk.WindowHandle ();
43+
winhandle.set_child (header_box);
44+
45+
// Main content
46+
var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 12);
47+
main_box.set_margin_top (6);
48+
main_box.set_margin_bottom (12);
49+
main_box.set_margin_start (12);
50+
main_box.set_margin_end (12);
51+
52+
// Toolbar with Add button
53+
var img_toolbar = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
54+
this.add_image_button = new He.Button ("", _("Add Images"));
55+
this.add_image_button.is_fill = true;
56+
this.add_image_button.clicked.connect (on_add_images_clicked);
57+
img_toolbar.append (this.add_image_button);
58+
main_box.append (img_toolbar);
59+
60+
// Previews
61+
var scrolled = new Gtk.ScrolledWindow ();
62+
scrolled.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
63+
this.image_preview_box = new Gtk.FlowBox () {
64+
min_children_per_line = 4,
65+
max_children_per_line = 4,
66+
margin_top = 8, margin_bottom = 8, margin_start = 8, margin_end = 8
67+
};
68+
scrolled.set_child (this.image_preview_box);
69+
main_box.append (scrolled);
70+
71+
// Populate previews
72+
foreach (var p in this.image_paths) {
73+
add_image_preview (p);
74+
}
75+
this.add_image_button.set_sensitive (this.image_paths.length () < 4);
76+
77+
// Bottom buttons
78+
var button_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12);
79+
button_box.set_halign (Gtk.Align.END);
80+
button_box.set_margin_top (12);
81+
button_box.set_margin_bottom (12);
82+
button_box.set_margin_start (12);
83+
button_box.set_margin_end (12);
84+
85+
var cancel_btn = new He.Button ("", _("Cancel"));
86+
cancel_btn.is_tint = true;
87+
cancel_btn.clicked.connect (() => {
88+
response (Gtk.ResponseType.CANCEL);
89+
this.close ();
90+
});
91+
button_box.append (cancel_btn);
92+
93+
var save_btn = new He.Button ("", _("Save"));
94+
save_btn.is_fill = true;
95+
save_btn.clicked.connect (() => {
96+
response (Gtk.ResponseType.ACCEPT);
97+
this.close ();
98+
});
99+
button_box.append (save_btn);
100+
101+
var container = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
102+
container.append (winhandle);
103+
container.append (main_box);
104+
container.append (button_box);
105+
this.set_child (container);
106+
}
107+
108+
private void add_image_preview (string path) {
109+
var overlay = new Gtk.Overlay ();
110+
overlay.set_size_request (72, 72);
111+
var picture = new Gtk.Picture.for_filename (path);
112+
picture.set_halign (Gtk.Align.CENTER);
113+
picture.set_can_shrink (true);
114+
overlay.set_child (picture);
115+
116+
var remove_btn = new He.Button ("window-close-symbolic", "");
117+
remove_btn.is_disclosure = true;
118+
remove_btn.set_halign (Gtk.Align.END);
119+
remove_btn.set_valign (Gtk.Align.START);
120+
remove_btn.clicked.connect (() => {
121+
this.image_paths.remove (path);
122+
this.image_preview_box.remove (overlay);
123+
this.add_image_button.set_sensitive (this.image_paths.length () < 4);
124+
});
125+
overlay.add_overlay (remove_btn);
126+
127+
this.image_preview_box.insert (overlay, -1);
128+
}
129+
130+
private void on_add_images_clicked () {
131+
if (this.image_paths.length () >= 4)return;
132+
133+
var file_dialog = new Gtk.FileDialog ();
134+
file_dialog.set_title (_("Select Images"));
135+
file_dialog.set_accept_label (_("Open"));
136+
137+
var filter = new Gtk.FileFilter ();
138+
filter.set_filter_name (_("Image Files"));
139+
filter.add_pixbuf_formats ();
140+
var list_store = new GLib.ListStore (typeof (Gtk.FileFilter));
141+
list_store.append (filter);
142+
file_dialog.set_filters (list_store);
143+
144+
file_dialog.open_multiple.begin ((Gtk.Window) this.get_root (), null, (obj, res) => {
145+
try {
146+
var files = file_dialog.open_multiple.end (res);
147+
for (int i = 0; i < files.get_n_items (); i++) {
148+
if (this.image_paths.length () >= 4)break;
149+
var f = files.get_item (i) as File;
150+
var p = f.get_path ();
151+
this.image_paths.append (p);
152+
add_image_preview (p);
153+
}
154+
if (this.image_paths.length () >= 4) {
155+
this.add_image_button.set_sensitive (false);
156+
}
157+
} catch (Error e) {
158+
}
159+
});
160+
}
161+
162+
public GLib.List<string> get_image_paths () {
163+
var out_list = new GLib.List<string> ();
164+
foreach (var p in this.image_paths) out_list.append (p);
165+
return out_list;
166+
}
167+
}
168+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
namespace Notejot {
2+
public class EntryLocationDialog : He.Window {
3+
public signal void response (int response_id);
4+
public He.TextField location_entry { get; private set; }
5+
6+
public EntryLocationDialog (Gtk.Window parent, string initial_text) {
7+
Object (
8+
parent : parent,
9+
modal: true,
10+
default_width: 440,
11+
resizable: false
12+
);
13+
14+
this.add_css_class ("dialog-content");
15+
16+
// Header
17+
var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
18+
header_box.set_margin_top (12);
19+
header_box.set_margin_start (12);
20+
header_box.set_margin_end (12);
21+
header_box.set_margin_bottom (12);
22+
23+
var title_label = new Gtk.Label (_("Set Location"));
24+
title_label.add_css_class ("title-3");
25+
header_box.append (title_label);
26+
header_box.append (new Gtk.Label ("") { hexpand = true });
27+
28+
var close_button = new He.Button ("window-close-symbolic", "");
29+
close_button.tooltip_text = _("Close");
30+
close_button.is_disclosure = true;
31+
close_button.clicked.connect (() => {
32+
response (Gtk.ResponseType.CANCEL);
33+
this.close ();
34+
});
35+
header_box.append (close_button);
36+
37+
var winhandle = new Gtk.WindowHandle ();
38+
winhandle.set_child (header_box);
39+
40+
// Main content
41+
var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 12);
42+
main_box.set_margin_top (6);
43+
main_box.set_margin_bottom (12);
44+
main_box.set_margin_start (12);
45+
main_box.set_margin_end (12);
46+
47+
this.location_entry = new He.TextField () { support_text = _("City, State, Country"), hexpand = true, is_outline = true };
48+
var internal = this.location_entry.get_internal_entry ();
49+
if (internal != null) internal.text = initial_text;
50+
main_box.append (this.location_entry);
51+
52+
// Bottom buttons
53+
var button_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12);
54+
button_box.set_halign (Gtk.Align.END);
55+
button_box.set_margin_top (12);
56+
button_box.set_margin_bottom (12);
57+
button_box.set_margin_start (12);
58+
button_box.set_margin_end (12);
59+
60+
var cancel_btn = new He.Button ("", _("Cancel"));
61+
cancel_btn.is_tint = true;
62+
cancel_btn.clicked.connect (() => {
63+
response (Gtk.ResponseType.CANCEL);
64+
this.close ();
65+
});
66+
button_box.append (cancel_btn);
67+
68+
var save_btn = new He.Button ("", _("Save"));
69+
save_btn.is_fill = true;
70+
save_btn.clicked.connect (() => {
71+
response (Gtk.ResponseType.ACCEPT);
72+
this.close ();
73+
});
74+
button_box.append (save_btn);
75+
76+
var container = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
77+
container.append (winhandle);
78+
container.append (main_box);
79+
container.append (button_box);
80+
this.set_child (container);
81+
}
82+
83+
public string get_location_text () {
84+
return this.location_entry.get_internal_entry ().text;
85+
}
86+
}
87+
}

src/dialogs/EntryTagsDialog.vala

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
namespace Notejot {
2+
public class EntryTagsDialog : He.Window {
3+
public signal void response (int response_id);
4+
5+
private DataManager data_manager;
6+
private Gtk.FlowBox tags_flowbox;
7+
private GLib.List<Gtk.CheckButton> tag_check_buttons = new GLib.List<Gtk.CheckButton> ();
8+
private GLib.List<string> tag_uuids = new GLib.List<string> ();
9+
10+
public EntryTagsDialog (Gtk.Window parent, DataManager data_manager, GLib.List<string> initially_selected) {
11+
Object (
12+
parent : parent,
13+
modal: true,
14+
default_width: 440,
15+
resizable: false
16+
);
17+
18+
this.data_manager = data_manager;
19+
this.add_css_class ("dialog-content");
20+
21+
// Header
22+
var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
23+
header_box.set_margin_top (12);
24+
header_box.set_margin_start (12);
25+
header_box.set_margin_end (12);
26+
header_box.set_margin_bottom (12);
27+
28+
var title_label = new Gtk.Label (_("Select Tags"));
29+
title_label.add_css_class ("title-3");
30+
header_box.append (title_label);
31+
header_box.append (new Gtk.Label ("") { hexpand = true });
32+
33+
var close_button = new He.Button ("window-close-symbolic", "");
34+
close_button.tooltip_text = _("Close");
35+
close_button.is_disclosure = true;
36+
close_button.clicked.connect (() => {
37+
response (Gtk.ResponseType.CANCEL);
38+
this.close ();
39+
});
40+
header_box.append (close_button);
41+
42+
var winhandle = new Gtk.WindowHandle ();
43+
winhandle.set_child (header_box);
44+
45+
// Main content
46+
var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 12);
47+
main_box.set_margin_top (6);
48+
main_box.set_margin_bottom (12);
49+
main_box.set_margin_start (12);
50+
main_box.set_margin_end (12);
51+
52+
var scrolled = new Gtk.ScrolledWindow ();
53+
scrolled.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
54+
scrolled.set_min_content_height (160);
55+
this.tags_flowbox = new Gtk.FlowBox () {
56+
selection_mode = Gtk.SelectionMode.NONE,
57+
valign = Gtk.Align.START,
58+
max_children_per_line = 3,
59+
min_children_per_line = 1,
60+
margin_top = 4, margin_bottom = 4, margin_start = 4, margin_end = 4
61+
};
62+
scrolled.set_child (this.tags_flowbox);
63+
main_box.append (scrolled);
64+
65+
// Populate tags
66+
foreach (var tag in this.data_manager.get_tags ()) {
67+
var check = new Gtk.CheckButton.with_label (tag.name);
68+
bool preselected = false;
69+
for (int i = 0; i < initially_selected.length (); i++) {
70+
if (initially_selected.nth_data (i) == tag.uuid) {
71+
preselected = true;
72+
break;
73+
}
74+
}
75+
check.set_active (preselected);
76+
this.tag_check_buttons.append (check);
77+
this.tag_uuids.append (tag.uuid);
78+
this.tags_flowbox.insert (check, -1);
79+
}
80+
81+
// Bottom buttons
82+
var button_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12);
83+
button_box.set_halign (Gtk.Align.END);
84+
button_box.set_margin_top (12);
85+
button_box.set_margin_bottom (12);
86+
button_box.set_margin_start (12);
87+
button_box.set_margin_end (12);
88+
89+
var cancel_btn = new He.Button ("", _("Cancel"));
90+
cancel_btn.is_tint = true;
91+
cancel_btn.clicked.connect (() => {
92+
response (Gtk.ResponseType.CANCEL);
93+
this.close ();
94+
});
95+
button_box.append (cancel_btn);
96+
97+
var save_btn = new He.Button ("", _("Save"));
98+
save_btn.is_fill = true;
99+
save_btn.clicked.connect (() => {
100+
response (Gtk.ResponseType.ACCEPT);
101+
this.close ();
102+
});
103+
button_box.append (save_btn);
104+
105+
var container = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
106+
container.append (winhandle);
107+
container.append (main_box);
108+
container.append (button_box);
109+
this.set_child (container);
110+
}
111+
112+
public GLib.List<string> get_selected_tag_uuids () {
113+
var selected = new GLib.List<string> ();
114+
for (int i = 0; i < this.tag_check_buttons.length (); i++) {
115+
if (this.tag_check_buttons.nth_data (i).get_active ()) {
116+
selected.append (this.tag_uuids.nth_data (i));
117+
}
118+
}
119+
return selected;
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)