Skip to content

Commit 5687699

Browse files
committed
Add map preview to Places sidebar card and update styles
- Show a non-interactive map with location pins in the Places card - Adjust CSS for new map preview layout - Update PlacesView and Sidebar to support map preview and pin updates - Fix TagRow count label margin when editing
1 parent 02f2995 commit 5687699

4 files changed

Lines changed: 168 additions & 23 deletions

File tree

data/style.css

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ window {
9898
margin: 18px;
9999
margin-bottom: 0;
100100
margin-top: 4px;
101-
background-image:
102-
linear-gradient(90deg, #aad3df 25%, transparent 100%),
103-
url("resource://io/github/lainsce/Notejot/map.png");
104-
background-size: cover;
105-
background-repeat: no-repeat;
106-
background-position: center;
101+
padding: 0;
102+
}
103+
.card.places-card overlay > box {
104+
margin: 0;
105+
padding: 15px;
106+
background-image: linear-gradient(90deg, #aad3df 33%, transparent 100%);
107107
}
108108

109109
.card {

src/views/PlacesView.vala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ namespace Notejot {
199199
}
200200

201201
total_locations_label.set_label (@"$(unique_locations.length()) Locations");
202+
map_widget.queue_draw ();
202203
}
203204

204205
private void show_location_notes (double lat, double lon) {

src/views/Sidebar.vala

Lines changed: 157 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ namespace Notejot {
2929

3030
private Gtk.Label places_subtitle_number;
3131

32+
// Places (sidebar card) map preview
33+
private Shumate.SimpleMap places_map_widget;
34+
private Shumate.MapSourceRegistry places_registry;
35+
private Shumate.MarkerLayer places_marker_layer;
36+
3237
public Sidebar (DataManager data_manager) {
3338
Object (
3439
orientation: Gtk.Orientation.VERTICAL,
@@ -100,13 +105,9 @@ namespace Notejot {
100105
this.insights_card_overlay.add_overlay (this.insights_switch);
101106
this.append (this.insights_card_overlay);
102107

103-
// Places card (keeps existing simple counter)
104-
this.places_button = create_sidebar_card (
105-
_("Places"),
106-
_("0"),
107-
"places-card",
108-
out this.places_subtitle_number
109-
);
108+
// Places card
109+
// Places card with non-interactive map preview
110+
this.places_button = create_places_card_with_map (out this.places_subtitle_number);
110111
places_button.set_size_request (-1, 148);
111112
places_button.clicked.connect (() => {
112113
if (editing_mode)return;
@@ -250,40 +251,117 @@ namespace Notejot {
250251
this.insights_words_number_label.set_label (@"$words_all_time");
251252
}
252253
this.places_subtitle_number.set_label (@"$location_count");
254+
this.refresh_places_preview_pins ();
255+
256+
this.places_map_widget.queue_draw ();
253257
}
254258

255-
private Gtk.Button create_sidebar_card (string title, string subtitle, string style_class, out Gtk.Label number_label_out) {
259+
private Gtk.Button create_places_card_with_map (out Gtk.Label number_label_out) {
256260
var button = new Gtk.Button ();
257261
button.add_css_class ("card");
258-
button.add_css_class (style_class);
262+
button.add_css_class ("places-card");
263+
button.set_overflow (Gtk.Overflow.HIDDEN);
264+
265+
// Overlay with map as background
266+
var overlay = new Gtk.Overlay ();
267+
overlay.set_vexpand_set (true);
268+
button.set_child (overlay);
269+
270+
this.places_map_widget = new Shumate.SimpleMap ();
271+
this.places_map_widget.set_hexpand (true);
272+
this.places_map_widget.show_zoom_buttons = false;
273+
this.places_map_widget.license.visible = false;
274+
this.places_map_widget.scale.visible = false;
275+
// Make the map non-interactive inside the sidebar card
276+
this.places_map_widget.set_can_target (false);
277+
this.places_map_widget.set_focusable (false);
278+
this.places_map_widget.realize.connect (() => {
279+
this.fit_places_preview_to_markers ();
280+
this.places_map_widget.queue_draw ();
281+
});
282+
283+
// Map source and layers
284+
setup_places_preview_map_source ();
285+
this.places_marker_layer = new Shumate.MarkerLayer (this.places_map_widget.get_viewport ());
286+
this.places_map_widget.add_overlay_layer (this.places_marker_layer);
287+
288+
overlay.set_child (this.places_map_widget);
259289

260-
var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { halign = Gtk.Align.START, hexpand = true };
261-
button.set_child (box);
290+
// Title and counter overlay at top-left
291+
var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { halign = Gtk.Align.START };
292+
box.set_size_request (165, -1);
262293

263294
var title_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
264295
box.append (title_box);
265296

266-
var title_label = new Gtk.Label (title) { hexpand = true };
297+
var title_label = new Gtk.Label (_("Places")) { hexpand = true };
267298
title_label.add_css_class ("title");
268299
title_label.set_xalign (0.0f);
269300
title_box.append (title_label);
270301

271302
var subtitle_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4);
272-
box.append (subtitle_box);
273-
274303
var image = new Gtk.Image.from_icon_name ("location-active-symbolic");
275304
image.pixel_size = 24;
276305
subtitle_box.append (image);
277306

278-
string number = subtitle;
279-
number_label_out = new Gtk.Label (number);
307+
number_label_out = new Gtk.Label ("0");
280308
number_label_out.add_css_class ("stat-value");
281309
number_label_out.set_xalign (0.0f);
282310
subtitle_box.append (number_label_out);
283311

312+
box.append (subtitle_box);
313+
overlay.add_overlay (box);
314+
315+
// Initial pins
316+
refresh_places_preview_pins ();
317+
284318
return button;
285319
}
286320

321+
private void setup_places_preview_map_source () {
322+
this.places_registry = new Shumate.MapSourceRegistry.with_defaults ();
323+
324+
if (Shumate.VectorRenderer.is_supported ()) {
325+
try {
326+
var style_json = new Notejot.MapStyle ("notejot-light").to_string ();
327+
var renderer = new Shumate.VectorRenderer ("notejot-light", style_json) {
328+
max_zoom_level = 14,
329+
min_zoom_level = 1
330+
};
331+
this.places_registry.add (renderer);
332+
} catch (Error e) {
333+
warning ("Places sidebar map: failed to create vector map style: %s", e.message);
334+
}
335+
}
336+
337+
NotejotApp.settings.bind_with_mapping (
338+
"map-source", this.places_map_widget, "map-source", GET,
339+
(SettingsBindGetMappingShared) PlacesView.map_source_get_mapping_cb,
340+
(SettingsBindSetMappingShared) null,
341+
this.places_registry, null
342+
);
343+
}
344+
345+
private void refresh_places_preview_pins () {
346+
if (this.places_marker_layer == null) {
347+
return;
348+
}
349+
this.places_marker_layer.remove_all ();
350+
351+
var unique_locations = this.data_manager.get_unique_locations ();
352+
foreach (var entry in unique_locations) {
353+
var marker = new Shumate.Marker ();
354+
var marker_image = new Gtk.Image.from_icon_name ("marker-pin") { pixel_size = 20 };
355+
marker.set_child (marker_image);
356+
marker.set_location (entry.latitude, entry.longitude);
357+
this.places_marker_layer.add_marker (marker);
358+
}
359+
if (this.places_map_widget != null) {
360+
this.fit_places_preview_to_markers ();
361+
this.places_map_widget.queue_draw ();
362+
}
363+
}
364+
287365
private Gtk.Button create_insights_card (out Gtk.Label year_number_out,
288366
out Gtk.Label days_number_out,
289367
out Gtk.Label words_number_out) {
@@ -368,6 +446,68 @@ namespace Notejot {
368446
return button;
369447
}
370448

449+
private void fit_places_preview_to_markers () {
450+
if (this.places_map_widget == null) {
451+
return;
452+
}
453+
454+
var viewport = this.places_map_widget.get_viewport ();
455+
var unique_locations = this.data_manager.get_unique_locations ();
456+
457+
int count = 0;
458+
double min_lat = 0.0;
459+
double max_lat = 0.0;
460+
double min_lon = 0.0;
461+
double max_lon = 0.0;
462+
463+
foreach (var e in unique_locations) {
464+
double lat = (double) e.latitude;
465+
double lon = (double) e.longitude;
466+
if (count == 0) {
467+
min_lat = lat;
468+
max_lat = lat;
469+
min_lon = lon;
470+
max_lon = lon;
471+
} else {
472+
if (lat < min_lat)min_lat = lat;
473+
if (lat > max_lat)max_lat = lat;
474+
if (lon < min_lon)min_lon = lon;
475+
if (lon > max_lon)max_lon = lon;
476+
}
477+
count++;
478+
}
479+
480+
if (count == 0) {
481+
viewport.zoom_level = 1.0;
482+
viewport.latitude = 0.0;
483+
viewport.longitude = 0.0;
484+
return;
485+
}
486+
487+
double center_lat = (min_lat + max_lat) / 2.0;
488+
double center_lon = (min_lon + max_lon) / 2.0;
489+
viewport.latitude = center_lat;
490+
viewport.longitude = center_lon;
491+
492+
if (count == 1) {
493+
viewport.zoom_level = 12.0;
494+
return;
495+
}
496+
497+
double span_lat = max_lat - min_lat;
498+
double span_lon = max_lon - min_lon;
499+
double span = (span_lat > span_lon) ? span_lat : span_lon;
500+
501+
double zoom = 3.0;
502+
if (span < 0.01)zoom = 13.0;
503+
else if (span < 0.05)zoom = 12.0;
504+
else if (span < 0.1)zoom = 11.0;
505+
else if (span < 0.5)zoom = 10.0;
506+
else if (span < 1.0)zoom = 9.0;
507+
508+
viewport.zoom_level = zoom;
509+
}
510+
371511
private GLib.List<Tag?> sort_tags (GLib.List<Tag?> tags, string[] order) {
372512
var by_uuid = new GLib.HashTable<string, Tag?> (GLib.str_hash, GLib.str_equal);
373513
foreach (var t in tags) {
@@ -376,6 +516,7 @@ namespace Notejot {
376516
}
377517
}
378518

519+
379520
var used = new GLib.HashTable<string, bool> (GLib.str_hash, GLib.str_equal);
380521
var result = new GLib.List<Tag?> ();
381522

src/widgets/TagRow.vala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Notejot {
1010
private He.Button? edit_button;
1111
private He.Button? delete_button;
1212
private Gtk.Image drag_handle;
13+
private Gtk.Label count_label;
1314

1415
public TagRow (string? color, string name, string count, string? icon_name, string? uuid) {
1516
this.tag_uuid = uuid;
@@ -98,7 +99,7 @@ namespace Notejot {
9899
label.margin_top = 3;
99100
label.set_xalign (0.0f);
100101

101-
var count_label = new Gtk.Label (count);
102+
count_label = new Gtk.Label (count);
102103
count_label.set_valign (Gtk.Align.CENTER);
103104
count_label.margin_top = 3;
104105
count_label.margin_end = 9;
@@ -222,6 +223,8 @@ namespace Notejot {
222223
this.delete_button.set_sensitive (!is_editing);
223224
}
224225
this.drag_handle.set_visible (is_editing);
226+
count_label.margin_end = is_editing ? 0 : 9;
227+
this.drag_handle.margin_end = 9;
225228
}
226229
}
227230

0 commit comments

Comments
 (0)