mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:22:45 +00:00 
			
		
		
		
	Maps: Add search panel
This commit is contained in:
		
							parent
							
								
									2c2a1da306
								
							
						
					
					
						commit
						deb7ecfbe9
					
				
					 9 changed files with 331 additions and 22 deletions
				
			
		|  | @ -4,9 +4,13 @@ serenity_component( | |||
|     TARGETS Maps | ||||
| ) | ||||
| 
 | ||||
| compile_gml(SearchPanel.gml SearchPanelGML.cpp) | ||||
| 
 | ||||
| set(SOURCES | ||||
|     main.cpp | ||||
|     MapWidget.cpp | ||||
|     SearchPanelGML.cpp | ||||
|     SearchPanel.cpp | ||||
|     UsersMapWidget.cpp | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ | |||
| #include <LibGfx/ImageFormats/ImageDecoder.h> | ||||
| #include <LibProtocol/Request.h> | ||||
| 
 | ||||
| namespace Maps { | ||||
| 
 | ||||
| // Math helpers
 | ||||
| // https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Pseudo-code
 | ||||
| static double longitude_to_tile_x(double longitude, int zoom) | ||||
|  | @ -531,3 +533,5 @@ void MapWidget::paint_event(GUI::PaintEvent& event) | |||
|         paint_scale(painter); | ||||
|     paint_panels(painter); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -15,6 +15,8 @@ | |||
| #include <LibProtocol/Request.h> | ||||
| #include <LibProtocol/RequestClient.h> | ||||
| 
 | ||||
| namespace Maps { | ||||
| 
 | ||||
| class MapWidget : public GUI::Frame | ||||
|     , public Config::Listener { | ||||
|     C_OBJECT(MapWidget); | ||||
|  | @ -63,15 +65,16 @@ public: | |||
|         LatLng latlng; | ||||
|         Optional<String> tooltip {}; | ||||
|         RefPtr<Gfx::Bitmap> image { nullptr }; | ||||
|         Optional<String> name {}; | ||||
|     }; | ||||
|     void add_marker(Marker const& marker) | ||||
|     { | ||||
|         m_markers.append(marker); | ||||
|         update(); | ||||
|     } | ||||
|     void clear_markers() | ||||
|     void remove_markers_with_name(StringView name) | ||||
|     { | ||||
|         m_markers.clear(); | ||||
|         m_markers.remove_all_matching([name](auto const& marker) { return marker.name == name; }); | ||||
|         update(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -185,7 +188,9 @@ private: | |||
|     Vector<Panel> m_panels; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| struct AK::Traits<MapWidget::TileKey> : public GenericTraits<MapWidget::TileKey> { | ||||
|     static unsigned hash(MapWidget::TileKey const& t) { return t.hash(); } | ||||
| struct AK::Traits<Maps::MapWidget::TileKey> : public GenericTraits<Maps::MapWidget::TileKey> { | ||||
|     static unsigned hash(Maps::MapWidget::TileKey const& t) { return t.hash(); } | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										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); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										63
									
								
								Userland/Applications/Maps/SearchPanel.gml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Userland/Applications/Maps/SearchPanel.gml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| @Maps::SearchPanel { | ||||
|     min_width: 100 | ||||
|     preferred_width: 200 | ||||
|     max_width: 350 | ||||
|     layout: @GUI::VerticalBoxLayout { | ||||
|         spacing: 2 | ||||
|     } | ||||
| 
 | ||||
|     @GUI::Frame { | ||||
|         frame_style: "SunkenPanel" | ||||
|         fixed_height: 28 | ||||
|         layout: @GUI::HorizontalBoxLayout { | ||||
|             margins: [2] | ||||
|             spacing: 2 | ||||
|         } | ||||
| 
 | ||||
|         @GUI::TextBox { | ||||
|             name: "search_textbox" | ||||
|             placeholder: "Search a place..." | ||||
|         } | ||||
| 
 | ||||
|         @GUI::Button { | ||||
|             name: "search_button" | ||||
|             icon_from_path: "/res/icons/16x16/find.png" | ||||
|             fixed_width: 24 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //  Start, empty and places are toggled in visibility | ||||
|     @GUI::Frame { | ||||
|         name: "start_container" | ||||
|         frame_style: "SunkenPanel" | ||||
|         layout: @GUI::VerticalBoxLayout { | ||||
|             margins: [4] | ||||
|         } | ||||
| 
 | ||||
|         @GUI::Label { | ||||
|             text: "Enter a search query to search for places..." | ||||
|             text_alignment: "CenterLeft" | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @GUI::Frame { | ||||
|         name: "empty_container" | ||||
|         frame_style: "SunkenPanel" | ||||
|         layout: @GUI::VerticalBoxLayout { | ||||
|             margins: [4] | ||||
|         } | ||||
| 
 | ||||
|         @GUI::Label { | ||||
|             text: "Can't find any places with the search query" | ||||
|             text_alignment: "CenterLeft" | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @GUI::ListView { | ||||
|         name: "places_list" | ||||
|         horizontal_padding: 6 | ||||
|         vertical_padding: 4 | ||||
|         should_hide_unnecessary_scrollbars: true | ||||
|         alternating_row_colors: false | ||||
|     } | ||||
| } | ||||
							
								
								
									
										55
									
								
								Userland/Applications/Maps/SearchPanel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Userland/Applications/Maps/SearchPanel.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "MapWidget.h" | ||||
| #include <LibGUI/Button.h> | ||||
| #include <LibGUI/Frame.h> | ||||
| #include <LibGUI/ItemListModel.h> | ||||
| #include <LibGUI/ListView.h> | ||||
| #include <LibGUI/TextBox.h> | ||||
| #include <LibProtocol/Request.h> | ||||
| #include <LibProtocol/RequestClient.h> | ||||
| 
 | ||||
| namespace Maps { | ||||
| 
 | ||||
| class SearchPanel final : public GUI::Widget { | ||||
|     C_OBJECT(SearchPanel) | ||||
| 
 | ||||
| public: | ||||
|     static ErrorOr<NonnullRefPtr<SearchPanel>> create(); | ||||
| 
 | ||||
|     void search(StringView query); | ||||
|     void reset(); | ||||
| 
 | ||||
|     struct Place { | ||||
|         String name; | ||||
|         MapWidget::LatLng latlng; | ||||
|         int zoom; | ||||
|     }; | ||||
|     Function<void(Vector<Place> const&)> on_places_change; | ||||
|     Function<void(Place const&)> on_selected_place_change; | ||||
| 
 | ||||
| private: | ||||
|     SearchPanel() = default; | ||||
|     static ErrorOr<NonnullRefPtr<SearchPanel>> try_create(); | ||||
| 
 | ||||
|     ErrorOr<void> setup(); | ||||
| 
 | ||||
|     RefPtr<Protocol::RequestClient> m_request_client; | ||||
|     RefPtr<Protocol::Request> m_request; | ||||
|     RefPtr<GUI::TextBox> m_search_textbox; | ||||
|     RefPtr<GUI::Button> m_search_button; | ||||
|     RefPtr<GUI::Frame> m_start_container; | ||||
|     RefPtr<GUI::Frame> m_empty_container; | ||||
|     RefPtr<GUI::ListView> m_places_list; | ||||
|     RefPtr<GUI::ItemListModel<String>> m_places_names_model; | ||||
|     Vector<Place> m_places; | ||||
|     Vector<String> m_places_names; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -8,6 +8,8 @@ | |||
| #include <AK/JsonParser.h> | ||||
| #include <LibDesktop/Launcher.h> | ||||
| 
 | ||||
| namespace Maps { | ||||
| 
 | ||||
| UsersMapWidget::UsersMapWidget(Options const& options) | ||||
|     : MapWidget::MapWidget(options) | ||||
| { | ||||
|  | @ -65,7 +67,7 @@ void UsersMapWidget::add_users_to_map() | |||
|         return; | ||||
| 
 | ||||
|     for (auto const& user : m_users.value()) { | ||||
|         MapWidget::Marker marker = { user.coordinates, user.nick }; | ||||
|         MapWidget::Marker marker = { user.coordinates, user.nick, {}, "users"_string }; | ||||
|         if (!user.contributor) | ||||
|             marker.image = m_marker_gray_image; | ||||
|         add_marker(marker); | ||||
|  | @ -76,3 +78,5 @@ void UsersMapWidget::add_users_to_map() | |||
|         { { "https://github.com/SerenityOS/user-map" } }, | ||||
|         "users"_string }); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ | |||
| 
 | ||||
| #include "MapWidget.h" | ||||
| 
 | ||||
| namespace Maps { | ||||
| 
 | ||||
| class UsersMapWidget final : public MapWidget { | ||||
|     C_OBJECT(UsersMapWidget); | ||||
| 
 | ||||
|  | @ -23,7 +25,7 @@ public: | |||
|                 add_users_to_map(); | ||||
|             } | ||||
|         } else { | ||||
|             clear_markers(); | ||||
|             remove_markers_with_name("users"sv); | ||||
|             remove_panels_with_name("users"sv); | ||||
|         } | ||||
|     } | ||||
|  | @ -46,3 +48,5 @@ private: | |||
|     }; | ||||
|     Optional<Vector<User>> m_users; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include "SearchPanel.h" | ||||
| #include "UsersMapWidget.h" | ||||
| #include <LibConfig/Client.h> | ||||
| #include <LibCore/System.h> | ||||
|  | @ -13,6 +14,7 @@ | |||
| #include <LibGUI/Icon.h> | ||||
| #include <LibGUI/Menu.h> | ||||
| #include <LibGUI/Process.h> | ||||
| #include <LibGUI/Splitter.h> | ||||
| #include <LibGUI/ToolbarContainer.h> | ||||
| #include <LibGUI/Window.h> | ||||
| 
 | ||||
|  | @ -33,6 +35,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | |||
| 
 | ||||
|     Config::monitor_domain("Maps"); | ||||
| 
 | ||||
|     // Window
 | ||||
|     auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-maps"sv)); | ||||
|     auto window = GUI::Window::construct(); | ||||
|     window->set_title("Maps"); | ||||
|  | @ -49,14 +52,32 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | |||
|     auto toolbar_container = TRY(root_widget->try_add<GUI::ToolbarContainer>()); | ||||
|     auto toolbar = TRY(toolbar_container->try_add<GUI::Toolbar>()); | ||||
| 
 | ||||
|     // Main Widget
 | ||||
|     auto main_widget = TRY(root_widget->try_add<GUI::HorizontalSplitter>()); | ||||
| 
 | ||||
|     // Map widget
 | ||||
|     UsersMapWidget::Options options {}; | ||||
|     Maps::UsersMapWidget::Options options {}; | ||||
|     options.center.latitude = Config::read_string("Maps"sv, "MapView"sv, "CenterLatitude"sv, "30"sv).to_double().value_or(30.0); | ||||
|     options.center.longitude = Config::read_string("Maps"sv, "MapView"sv, "CenterLongitude"sv, "0"sv).to_double().value_or(0.0); | ||||
|     options.zoom = Config::read_i32("Maps"sv, "MapView"sv, "Zoom"sv, MAP_ZOOM_DEFAULT); | ||||
|     auto maps = TRY(root_widget->try_add<UsersMapWidget>(options)); | ||||
|     maps->set_frame_style(Gfx::FrameStyle::SunkenContainer); | ||||
|     maps->set_show_users(Config::read_bool("Maps"sv, "MapView"sv, "ShowUsers"sv, false)); | ||||
|     auto map_widget = TRY(main_widget->try_add<Maps::UsersMapWidget>(options)); | ||||
|     map_widget->set_frame_style(Gfx::FrameStyle::SunkenContainer); | ||||
|     map_widget->set_show_users(Config::read_bool("Maps"sv, "MapView"sv, "ShowUsers"sv, false)); | ||||
| 
 | ||||
|     // Search panel
 | ||||
|     auto search_panel = TRY(Maps::SearchPanel::create()); | ||||
|     search_panel->on_places_change = [map_widget](auto) { map_widget->remove_markers_with_name("search"sv); }; | ||||
|     search_panel->on_selected_place_change = [map_widget](auto const& place) { | ||||
|         // Remove old search markers
 | ||||
|         map_widget->remove_markers_with_name("search"sv); | ||||
| 
 | ||||
|         // Add new marker and zoom into it
 | ||||
|         map_widget->add_marker({ place.latlng, place.name, {}, "search"_string }); | ||||
|         map_widget->set_center(place.latlng); | ||||
|         map_widget->set_zoom(place.zoom); | ||||
|     }; | ||||
|     if (Config::read_bool("Maps"sv, "SearchPanel"sv, "Show"sv, false)) | ||||
|         main_widget->insert_child_before(search_panel, map_widget); | ||||
| 
 | ||||
|     // Main menu actions
 | ||||
|     auto file_menu = window->add_menu("&File"_string); | ||||
|  | @ -68,18 +89,32 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | |||
|     file_menu->add_action(GUI::CommonActions::make_quit_action([](auto&) { GUI::Application::the()->quit(); })); | ||||
| 
 | ||||
|     auto view_menu = window->add_menu("&View"_string); | ||||
|     auto show_search_panel_action = GUI::Action::create_checkable( | ||||
|         "Show search panel", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/find.png"sv)), [main_widget, search_panel, map_widget](auto& action) { | ||||
|             if (action.is_checked()) { | ||||
|                 main_widget->insert_child_before(search_panel, map_widget); | ||||
|             } else { | ||||
|                 map_widget->remove_markers_with_name("search"sv); | ||||
|                 search_panel->reset(); | ||||
|                 main_widget->remove_child(search_panel); | ||||
|             } | ||||
|         }, | ||||
|         window); | ||||
|     show_search_panel_action->set_checked(Config::read_bool("Maps"sv, "SearchPanel"sv, "Show"sv, false)); | ||||
|     auto show_users_action = GUI::Action::create_checkable( | ||||
|         "Show SerenityOS users", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/ladyball.png"sv)), [maps](auto& action) { maps->set_show_users(action.is_checked()); }, window); | ||||
|     show_users_action->set_checked(maps->show_users()); | ||||
|     auto zoom_in_action = GUI::CommonActions::make_zoom_in_action([maps](auto&) { maps->set_zoom(maps->zoom() + 1); }, window); | ||||
|     auto zoom_out_action = GUI::CommonActions::make_zoom_out_action([maps](auto&) { maps->set_zoom(maps->zoom() - 1); }, window); | ||||
|     auto reset_zoom_action = GUI::CommonActions::make_reset_zoom_action([maps](auto&) { maps->set_zoom(MAP_ZOOM_DEFAULT); }, window); | ||||
|     auto fullscreen_action = GUI::CommonActions::make_fullscreen_action([window, toolbar_container, maps](auto&) { | ||||
|         "Show SerenityOS users", TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/ladyball.png"sv)), [map_widget](auto& action) { map_widget->set_show_users(action.is_checked()); }, window); | ||||
|     show_users_action->set_checked(map_widget->show_users()); | ||||
|     auto zoom_in_action = GUI::CommonActions::make_zoom_in_action([map_widget](auto&) { map_widget->set_zoom(map_widget->zoom() + 1); }, window); | ||||
|     auto zoom_out_action = GUI::CommonActions::make_zoom_out_action([map_widget](auto&) { map_widget->set_zoom(map_widget->zoom() - 1); }, window); | ||||
|     auto reset_zoom_action = GUI::CommonActions::make_reset_zoom_action([map_widget](auto&) { map_widget->set_zoom(MAP_ZOOM_DEFAULT); }, window); | ||||
|     auto fullscreen_action = GUI::CommonActions::make_fullscreen_action([window, toolbar_container, map_widget](auto&) { | ||||
|         window->set_fullscreen(!window->is_fullscreen()); | ||||
|         toolbar_container->set_visible(!window->is_fullscreen()); | ||||
|         maps->set_frame_style(window->is_fullscreen() ? Gfx::FrameStyle::NoFrame : Gfx::FrameStyle::SunkenContainer); | ||||
|         map_widget->set_frame_style(window->is_fullscreen() ? Gfx::FrameStyle::NoFrame : Gfx::FrameStyle::SunkenContainer); | ||||
|     }, | ||||
|         window); | ||||
|     view_menu->add_action(show_search_panel_action); | ||||
|     view_menu->add_separator(); | ||||
|     view_menu->add_action(show_users_action); | ||||
|     view_menu->add_separator(); | ||||
|     view_menu->add_action(zoom_in_action); | ||||
|  | @ -93,6 +128,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | |||
|     help_menu->add_action(GUI::CommonActions::make_about_action("Maps"_string, app_icon, window)); | ||||
| 
 | ||||
|     // Main toolbar actions
 | ||||
|     toolbar->add_action(show_search_panel_action); | ||||
|     toolbar->add_separator(); | ||||
|     toolbar->add_action(show_users_action); | ||||
|     toolbar->add_separator(); | ||||
|     toolbar->add_action(zoom_in_action); | ||||
|  | @ -103,11 +140,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | |||
| 
 | ||||
|     window->show(); | ||||
| 
 | ||||
|     // Remember last map position
 | ||||
|     // Remember last window state
 | ||||
|     int exec = app->exec(); | ||||
|     Config::write_string("Maps"sv, "MapView"sv, "CenterLatitude"sv, TRY(String::number(maps->center().latitude))); | ||||
|     Config::write_string("Maps"sv, "MapView"sv, "CenterLongitude"sv, TRY(String::number(maps->center().longitude))); | ||||
|     Config::write_i32("Maps"sv, "MapView"sv, "Zoom"sv, maps->zoom()); | ||||
|     Config::write_bool("Maps"sv, "MapView"sv, "ShowUsers"sv, maps->show_users()); | ||||
|     Config::write_bool("Maps"sv, "SearchPanel"sv, "Show"sv, show_search_panel_action->is_checked()); | ||||
|     Config::write_string("Maps"sv, "MapView"sv, "CenterLatitude"sv, TRY(String::number(map_widget->center().latitude))); | ||||
|     Config::write_string("Maps"sv, "MapView"sv, "CenterLongitude"sv, TRY(String::number(map_widget->center().longitude))); | ||||
|     Config::write_i32("Maps"sv, "MapView"sv, "Zoom"sv, map_widget->zoom()); | ||||
|     Config::write_bool("Maps"sv, "MapView"sv, "ShowUsers"sv, map_widget->show_users()); | ||||
|     return exec; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Bastiaan van der Plaat
						Bastiaan van der Plaat