mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07:35 +00:00
Maps: Add search panel
This commit is contained in:
parent
2c2a1da306
commit
deb7ecfbe9
9 changed files with 331 additions and 22 deletions
132
Userland/Applications/Maps/SearchPanel.cpp
Normal file
132
Userland/Applications/Maps/SearchPanel.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "SearchPanel.h"
|
||||
#include <AK/JsonParser.h>
|
||||
|
||||
namespace Maps {
|
||||
|
||||
ErrorOr<NonnullRefPtr<SearchPanel>> SearchPanel::create()
|
||||
{
|
||||
auto widget = TRY(try_create());
|
||||
TRY(widget->setup());
|
||||
return widget;
|
||||
}
|
||||
|
||||
ErrorOr<void> SearchPanel::setup()
|
||||
{
|
||||
m_request_client = TRY(Protocol::RequestClient::try_create());
|
||||
|
||||
m_search_textbox = *find_descendant_of_type_named<GUI::TextBox>("search_textbox");
|
||||
m_search_button = *find_descendant_of_type_named<GUI::Button>("search_button");
|
||||
m_start_container = *find_descendant_of_type_named<GUI::Frame>("start_container");
|
||||
m_empty_container = *find_descendant_of_type_named<GUI::Frame>("empty_container");
|
||||
m_places_list = *find_descendant_of_type_named<GUI::ListView>("places_list");
|
||||
|
||||
m_empty_container->set_visible(false);
|
||||
m_places_list->set_visible(false);
|
||||
|
||||
m_search_textbox->on_return_pressed = [this]() {
|
||||
search(MUST(String::from_deprecated_string(m_search_textbox->text())));
|
||||
};
|
||||
m_search_button->on_click = [this](unsigned) {
|
||||
search(MUST(String::from_deprecated_string(m_search_textbox->text())));
|
||||
};
|
||||
|
||||
m_places_list->set_item_height(m_places_list->font().preferred_line_height() * 2 + m_places_list->vertical_padding());
|
||||
m_places_list->on_selection_change = [this]() {
|
||||
auto const& index = m_places_list->selection().first();
|
||||
if (!index.is_valid())
|
||||
return;
|
||||
on_selected_place_change(m_places.at(index.row()));
|
||||
};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void SearchPanel::search(StringView query)
|
||||
{
|
||||
// Show start container when empty query
|
||||
if (query.is_empty()) {
|
||||
m_start_container->set_visible(true);
|
||||
m_empty_container->set_visible(false);
|
||||
m_places_list->set_visible(false);
|
||||
return;
|
||||
}
|
||||
m_start_container->set_visible(false);
|
||||
|
||||
// Start HTTP GET request to load people.json
|
||||
HashMap<DeprecatedString, DeprecatedString> headers;
|
||||
headers.set("User-Agent", "SerenityOS Maps");
|
||||
headers.set("Accept", "application/json");
|
||||
URL url(MUST(String::formatted("https://nominatim.openstreetmap.org/search?q={}&format=json", AK::URL::percent_encode(query, AK::URL::PercentEncodeSet::Query))));
|
||||
auto request = m_request_client->start_request("GET", url, headers, {});
|
||||
VERIFY(!request.is_null());
|
||||
m_request = request;
|
||||
request->on_buffered_request_finish = [this, request, url](bool success, auto, auto&, auto, ReadonlyBytes payload) {
|
||||
m_request.clear();
|
||||
if (!success) {
|
||||
dbgln("Maps: Can't load: {}", url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse JSON data
|
||||
JsonParser parser(payload);
|
||||
auto result = parser.parse();
|
||||
if (result.is_error()) {
|
||||
dbgln("Maps: Can't parse JSON: {}", url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show empty label when no places are found
|
||||
auto json_places = result.release_value().as_array();
|
||||
if (json_places.size() == 0) {
|
||||
m_empty_container->set_visible(true);
|
||||
m_places_list->set_visible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse places from JSON data
|
||||
m_places.clear();
|
||||
m_places_names.clear();
|
||||
for (size_t i = 0; i < json_places.size(); i++) {
|
||||
// FIXME: Handle JSON parsing errors
|
||||
auto const& json_place = json_places.at(i).as_object();
|
||||
|
||||
String name = MUST(String::from_deprecated_string(json_place.get_deprecated_string("display_name"sv).release_value()));
|
||||
MapWidget::LatLng latlng = { json_place.get_deprecated_string("lat"sv).release_value().to_double().release_value(),
|
||||
json_place.get_deprecated_string("lon"sv).release_value().to_double().release_value() };
|
||||
|
||||
// Calculate the right zoom level for bounding box
|
||||
auto const& json_boundingbox = json_place.get_array("boundingbox"sv);
|
||||
MapWidget::LatLngBounds bounds = {
|
||||
{ json_boundingbox->at(0).as_string().to_double().release_value(),
|
||||
json_boundingbox->at(2).as_string().to_double().release_value() },
|
||||
{ json_boundingbox->at(1).as_string().to_double().release_value(),
|
||||
json_boundingbox->at(3).as_string().to_double().release_value() }
|
||||
};
|
||||
|
||||
m_places.append({ name, latlng, bounds.get_zoom() });
|
||||
m_places_names.append(MUST(String::formatted("{}\n{:.5}, {:.5}", name, latlng.latitude, latlng.longitude)));
|
||||
}
|
||||
on_places_change(m_places);
|
||||
|
||||
// Update and show places list
|
||||
m_empty_container->set_visible(false);
|
||||
m_places_list->set_model(*GUI::ItemListModel<String>::create(m_places_names));
|
||||
m_places_list->set_visible(true);
|
||||
};
|
||||
request->set_should_buffer_all_input(true);
|
||||
request->on_certificate_requested = []() -> Protocol::Request::CertificateAndKey { return {}; };
|
||||
}
|
||||
|
||||
void SearchPanel::reset()
|
||||
{
|
||||
m_search_textbox->set_text(""sv);
|
||||
search(""sv);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue