From 5515152d5e7a6f21356afacd2032d81c4d78612a Mon Sep 17 00:00:00 2001 From: Matthias Meulien Date: Mon, 9 Oct 2023 01:33:51 +0200 Subject: [PATCH] Display location list when multiple locations are returned by Geocoding API Refs: #56 --- NEWS.md | 3 + icons/icon_radio_button_checked.bmp | Bin 0 -> 11162 bytes icons/icon_radio_button_unchecked.bmp | Bin 0 -> 11162 bytes icons/meson.build | 2 + src/app.cc | 31 ++++-- src/app.h | 4 - src/events.cc | 5 + src/events.h | 1 + src/hourlyforecastbox.h | 2 +- src/icons.h | 8 ++ src/locationbox.h | 2 +- src/locationlist.cc | 152 ++++++++++++++++++++++++++ src/locationlist.h | 51 +++++++++ src/menu.h | 9 +- src/meson.build | 1 + src/model.h | 4 +- src/service.h | 5 - src/ui.cc | 17 +++ src/ui.h | 12 ++ src/units.h | 2 +- 20 files changed, 284 insertions(+), 27 deletions(-) create mode 100644 icons/icon_radio_button_checked.bmp create mode 100644 icons/icon_radio_button_unchecked.bmp create mode 100644 src/locationlist.cc create mode 100644 src/locationlist.h diff --git a/NEWS.md b/NEWS.md index 345c317..748f8d2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added +- Display location list when multiple locations are returned by + Geocoding API [#56](https://github.com/orontee/taranis/issues/56) + - Compile with O3 optimization and strip executable [#25](https://github.com/orontee/taranis/issues/25) diff --git a/icons/icon_radio_button_checked.bmp b/icons/icon_radio_button_checked.bmp new file mode 100644 index 0000000000000000000000000000000000000000..284193dd8913ca72b6fd7736bd8c3ed501bef36e GIT binary patch literal 11162 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 S@mmA!=>NF?Jn;YOf&T#o(GSr8 literal 0 HcmV?d00001 diff --git a/icons/icon_radio_button_unchecked.bmp b/icons/icon_radio_button_unchecked.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ede510f0852eff289b701191452f4654e6b37c09 GIT binary patch literal 11162 zcmeHNbzBrp7=G-;Zn3Zn6-BWX0~N8mTSP?#1F$GtOhiyr5DPFsF~L9#1O+ToxHB;k z5#6R8jY~bEeQBmm_xQup+YEJxG;(oDT1O!i=tSuVklm`I7*Z# zfs!RlqEx9;C|$ZV%9JUCvSrIcOG^vo%9TU;^5s#XLIqT;SP_*fRYK*;l~JWi6;!QS z71gR$L-p#_QKLo;)T~((wQAKu?b@}Wt*wnZb?TsQ-MXk(uO8~xua5=|8bC)!2MrrG zM59KH(7179G-=WVO`A4Fvu4fEym@o9Xwd>KTed{2R;|#wb!+JA>Y`1XHfY&>cI=2wojSq5zyO^)cSe^kUC^~_S9I&v4c)tUM~@yo(6eVx z^y<|My?ghDp`juA^y!1Xefy$czkcZ7zdwwOj4)up01O;B5Q7E{g0ZnN1`i$#6B82* z88QSzhYrQCVZ$(d_;8FEF#;n;j>M=@qcD2(Xp9*%24lyL#kg_fFn;`aOqehM6DLl@ zq)C%7dGcgTnKA`ar%uJRY11%$`gF{gF#|Ja&cv))voL%1Y|NQ62Xp7ng{i42=FOW2 zGcz;HpFbZ97A(NRg$rSBZjMEZ7Gd$?#aOar2`nruU}ym>RWY}taXTerf=$qC!GZNv8M+p%NE4(!~y6T5cp!tUL>;q2^; zJ$v@R#l;1C_wI$Ot1H~x+^}!oKJ4GWAMWn%IB?(q4jw#+Lx&E*!@~ofo}M^-_%OV@ zyl~{m5ga{w6vvJo!|~(CapJ@Yczb)p$Hxc0zP>nl@+3~3I)&4xPvgv)Gw}2CgTKE& z&YnGsbLY+>ARqwe&!5MI3m0(l;zeA#bP1O)U&fUyR}dH&h@hY#T)lb~*REZ|_3PJh z8 ziHS%`N`govf>8QReEAZuUcJKW*RS#B%^M^qC*$qgw|MvN9p1lxj}IR{ z;N!=S`1I)$Qc_az`SWL_rl#V{moG?5OT*W%Uy+`kj&I+-;rsXR$jHcmL?XeDA3yN( z=TBs2W+E#q3%`E-f>bI6MNv7Qf6qS;{PRG*Js?d@3=KN(SXs?VN%mZZjGwcr{C z?mq;gB+pTRFmetS%lIQr9PHdrLB#Z}AdmzDIelYqF~_u)icc8Ji5Vma0EyC()m$Wi(Ordq!x>4vBo(3x>?C##Q{Mh>Mg*gppjPDI#WL|Cxc z;NqQ$js)XcUUl^-LBxU`kGzNonDNG09!-Xv$plCdLz64G6O0%+vY<*dv2lP?O+uV$ zt~8i)>M}&>5*kF0b8{C)UP1kZhB3_+B8U~v3Fbg^O*HO}9Hgv)v>8Vhmnlg|DDY)S z8WoPy7}^}+H;y#&g+`_sC1!NSGbE<)?&6HBRzFZYaa(iJ&E>uJPNqM~Lf%`s492(OcP z5vr&fNMl$iGKAL`)24zHRnO532StYPx*csQKv9*fliL&-!s}aUj64CulV-RIx2dSH zmo}AW=THzB1>viL=<|XYyLai1>Z;g-!t1bobS0cs3VsjZDJQBI&bS9sw9j#1`ixxc_N#!PBy56OTR@-4u1ipA_Gf%CpR}IdrSFd{swVZJuhSd za?5}J&MO - #include "config.h" #include "errors.h" #include "events.h" @@ -21,12 +19,16 @@ int App::process_event(int event_type, int param_one, int param_two) { return 1; } - if (event_type == EVT_SHOW) { + if (event_type == EVT_SHOW or event_type == EVT_FOREGROUND) { this->show(); return 1; } - if ((event_type == EVT_EXIT) || (event_type == EVT_HIDE)) { + if (event_type == EVT_HIDE or event_type == EVT_BACKGROUND) { + return 1; + } + + if (event_type == EVT_EXIT) { this->exit(); return 1; } @@ -176,7 +178,7 @@ int App::handle_custom_event(int param_one, int param_two) { return 1; } else if (param_one == CustomEvent::select_location_from_history) { const size_t history_index = param_two; - auto location = this->history->get_location(history_index); + const auto location = this->history->get_location(history_index); if (location) { this->set_model_location(*location); } else { @@ -184,6 +186,19 @@ int App::handle_custom_event(int param_one, int param_two) { GetLangText("Ok"), nullptr, nullptr); } return 1; + } else if (param_one == CustomEvent::select_location_from_list) { + const size_t list_index = param_two; + if (this->ui) { + const auto location = + this->ui->get_location_from_location_list(list_index); + if (location) { + this->set_model_location(*location); + } else { + DialogSynchro(ICON_WARNING, "Title", "Location not found!", + GetLangText("Ok"), nullptr, nullptr); + } + return 1; + } } else if (param_one == CustomEvent::show_about_dialog) { this->open_about_dialog(); return 1; @@ -301,7 +316,9 @@ void App::search_location(const std::string &location_description) { if (found_locations.size() == 1) { this->set_model_location(found_locations[0]); } else if (found_locations.size() > 1) { - // TODO Open location list + if (this->ui) { + this->ui->open_location_list(found_locations); + } } } catch (const InvalidLocation &error) { const auto event_handler = GetEventHandler(); @@ -336,7 +353,7 @@ void App::set_model_location(const Location &location) const { BOOST_LOG_TRIVIAL(debug) << "Updating model location"; if (location == this->model->location) { - BOOST_LOG_TRIVIAL(debug) << "No need to update configured location"; + BOOST_LOG_TRIVIAL(debug) << "No need to update model location"; return; } diff --git a/src/app.h b/src/app.h index f342698..b6673c4 100644 --- a/src/app.h +++ b/src/app.h @@ -21,10 +21,6 @@ using namespace std::placeholders; using namespace std::string_literals; -namespace std { -template using optional = std::experimental::optional; -} - namespace taranis { std::string get_about_content(); diff --git a/src/events.cc b/src/events.cc index bab6918..60a08fc 100644 --- a/src/events.cc +++ b/src/events.cc @@ -19,6 +19,9 @@ std::string taranis::format_custom_event_param(int param) { if (param == CustomEvent::select_location_from_history) { return "select_location_from_history"; } + if (param == CustomEvent::select_location_from_list) { + return "select_location_from_list"; + } if (param == CustomEvent::show_about_dialog) { return "show_about_dialog"; } @@ -39,6 +42,8 @@ std::string taranis::format_custom_event_param(int param) { } std::string taranis::format_event_type(int event_type) { + // Note that char *iv_evttype(int event_type) is not complete... + if (event_type == EVT_INIT) { return "EVT_INIT"; } diff --git a/src/events.h b/src/events.h index 053ae67..3e76607 100644 --- a/src/events.h +++ b/src/events.h @@ -13,6 +13,7 @@ enum CustomEvent { display_alert, refresh_data, select_location_from_history, + select_location_from_list, show_about_dialog, toggle_current_location_favorite, diff --git a/src/hourlyforecastbox.h b/src/hourlyforecastbox.h index f671029..10b4b14 100644 --- a/src/hourlyforecastbox.h +++ b/src/hourlyforecastbox.h @@ -229,7 +229,7 @@ class HourlyForecastBox : public Widget { for (int x_screen = this->bounding_box.x; x_screen < width; ++x_screen) { const double x = this->forecast_offset * step + (x_screen - this->bounding_box.x); - if (x < xa.front() || x > xa.back()) { + if (x < xa.front() or x > xa.back()) { continue; } double y; diff --git a/src/icons.h b/src/icons.h index 1abdd25..ee466ad 100644 --- a/src/icons.h +++ b/src/icons.h @@ -15,6 +15,8 @@ extern const ibitmap icon_13n_2x; extern const ibitmap icon_50n_2x; extern const ibitmap icon_favorite; extern const ibitmap icon_menu; +extern const ibitmap icon_radio_button_checked; +extern const ibitmap icon_radio_button_unchecked; extern const ibitmap icon_warning; ; @@ -55,6 +57,12 @@ class Icons { if (name == "menu") { return const_cast(&icon_menu); } + if (name == "radio-button-checked") { + return const_cast(&icon_radio_button_checked); + } + if (name == "radio-button-unchecked") { + return const_cast(&icon_radio_button_unchecked); + } if (name == "warning") { return const_cast(&icon_warning); } diff --git a/src/locationbox.h b/src/locationbox.h index 0ad225c..7404a1f 100644 --- a/src/locationbox.h +++ b/src/locationbox.h @@ -65,7 +65,7 @@ class LocationBox : public Widget { std::string elide_maybe(const std::string &text) const { SetFont(this->font.get(), BLACK); const auto text_width = StringWidth(text.c_str()); - if (text_width <= this->bounding_box.w || text.size() <= 2) { + if (text_width <= this->bounding_box.w or text.size() <= 2) { // the second case should not happen unless the default font is // huge... return text; diff --git a/src/locationlist.cc b/src/locationlist.cc new file mode 100644 index 0000000..4f19dec --- /dev/null +++ b/src/locationlist.cc @@ -0,0 +1,152 @@ +#include "locationlist.h" + +#include +#include +#include + +#include "events.h" +#include "util.h" + +namespace { +taranis::LocationList *that; + +iv_handler application_event_handler; + +int last_selected_item_index; +} // namespace + +namespace taranis { + +LocationList::LocationList(const int icon_size, std::shared_ptr fonts, + std::shared_ptr icons) + : font{fonts->get_normal_font()}, + radio_button_unchecked{BitmapStretchProportionally( + icons->get("radio-button-unchecked"), icon_size, icon_size)}, + radio_button_checked{BitmapStretchProportionally( + icons->get("radio-button-checked"), icon_size, icon_size)} {} + +std::optional LocationList::get_location(size_t index) const { + if (index < this->locations.size()) { + return this->locations.at(index); + } + return std::experimental::nullopt; +} + +void LocationList::set_locations(const std::vector &locations) { + this->locations = locations; + + this->item_names.clear(); + this->item_names.reserve(this->locations.size()); + + for (const auto &location : this->locations) { + this->item_names.push_back(format_location(location, true)); + } +} + +void LocationList::show() { + if (this->locations.size() == 0) { + return; + } + that = this; + application_event_handler = GetEventHandler(); + + OpenList(GetLangText("Locations"), nullptr, this->get_item_width(), + this->get_item_height(), this->locations.size(), 0, + &handle_list_action); +} + +int LocationList::get_item_width() const { + return ScreenWidth() - 2 * LocationList::horizontal_padding; +} + +int LocationList::get_item_height() const { + return std::max(static_cast(this->radio_button_unchecked->height), + this->font->height) + + LocationList::vertical_padding; +} + +int LocationList::get_icon_vertical_offset() const { + return (get_item_height() - this->radio_button_checked->height) / 2; +} + +int LocationList::handle_list_action(int action, int x, int y, int item_index, + int state) { + if (that == nullptr) { + BOOST_LOG_TRIVIAL(debug) + << "Skipping action received by uninitialized list " << action; + return 1; + } + if (action == LIST_BEGINPAINT) { + BOOST_LOG_TRIVIAL(debug) << "Starting to paint location list"; + return 1; + } else if (action == LIST_PAINT) { + if (0 <= item_index and item_index < that->item_names.size()) { + BOOST_LOG_TRIVIAL(debug) + << "Drawing list item with index " << item_index << " and state " + << state << " at " << x << ", " << y; + + const ibitmap *icon = that->radio_button_unchecked; + if (state) { + if (last_selected_item_index != -1) { + that->item_names[last_selected_item_index] = + format_location(that->locations[last_selected_item_index], true); + } + last_selected_item_index = item_index; + icon = that->radio_button_checked; + + DrawSelection( + x, y, ScreenWidth() - 2 * LocationList::horizontal_padding, + that->font->height + +LocationList::vertical_padding, BLACK); + } + DrawBitmap(x + LocationList::horizontal_padding / 2, + y + that->get_icon_vertical_offset(), icon); + + DrawTextRect( + x + 2 * LocationList::horizontal_padding + icon->width, y, + ScreenWidth() - 4 * LocationList::horizontal_padding - icon->width, + that->get_item_height(), that->item_names.at(item_index).c_str(), + ALIGN_LEFT | VALIGN_MIDDLE); + return 1; + } + } else if (action == LIST_ENDPAINT) { + BOOST_LOG_TRIVIAL(debug) << "Finished to paint location list"; + return 1; + } else if (action == LIST_OPEN) { + if (0 <= item_index and item_index < that->item_names.size()) { + BOOST_LOG_TRIVIAL(debug) + << "Will select location from item with index " << item_index; + + SetEventHandler(application_event_handler); + + SendEvent(application_event_handler, EVT_CUSTOM, + CustomEvent::select_location_from_list, + static_cast(last_selected_item_index)); + + return 1; + } + } else if (action == LIST_MENU) { + BOOST_LOG_TRIVIAL(debug) << "Received list menu action"; + } else if (action == LIST_DELETE) { + BOOST_LOG_TRIVIAL(debug) << "Received list delete action"; + } else if (action == LIST_EXIT) { + BOOST_LOG_TRIVIAL(debug) << "Received list exit action"; + + SetEventHandler(application_event_handler); + + that = nullptr; + return 1; + } else if (action == LIST_ORIENTATION) { + BOOST_LOG_TRIVIAL(debug) << "Changing list orientation is not supported"; + } else if (action == LIST_POINTER) { + BOOST_LOG_TRIVIAL(debug) << "Received list pointer action"; + } else if (action == LIST_INFO) { + BOOST_LOG_TRIVIAL(debug) << "Received list info action"; + last_selected_item_index = -1; + } else if (action == LIST_SCROLL) { + BOOST_LOG_TRIVIAL(debug) << "Received list scroll action"; + } else { + BOOST_LOG_TRIVIAL(warning) << "Unexpected list action " << action; + } + return 0; +} +} // namespace taranis diff --git a/src/locationlist.h b/src/locationlist.h new file mode 100644 index 0000000..ec01e3e --- /dev/null +++ b/src/locationlist.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "fonts.h" +#include "icons.h" +#include "model.h" + +namespace std { +template using optional = std::experimental::optional; +} + +namespace taranis { + +class LocationList { +public: + LocationList(int icon_size, std::shared_ptr fonts, + std::shared_ptr icons); + + std::optional get_location(size_t index) const; + + void set_locations(const std::vector &locations); + + void show(); + +private: + static constexpr int horizontal_padding{25}; + static constexpr int vertical_padding{25}; + + std::vector locations; + + std::vector item_names; + + std::shared_ptr font; + const ibitmap *const radio_button_unchecked; + const ibitmap *const radio_button_checked; + + int get_item_width() const; + + int get_item_height() const; + + int get_icon_vertical_offset() const; + + static int handle_list_action(int action, int x, int y, int item_index, + int state); +}; +} // namespace taranis diff --git a/src/menu.h b/src/menu.h index df0e906..034e213 100644 --- a/src/menu.h +++ b/src/menu.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -16,9 +15,6 @@ #include "model.h" #include "util.h" -namespace std { -template using optional = std::experimental::optional; -} namespace taranis { enum menu_item_index { @@ -71,7 +67,8 @@ class MenuButton : public Button { nullptr, nullptr}, imenuex{0, 0, nullptr, nullptr, nullptr, nullptr, nullptr}}, model{model}, font{fonts->get_normal_font()}, - history{std::make_unique(this->model)} { + history{std::make_unique(this->model)}, + menu_handler{nullptr} { this->initialize_favorite_location_icon(); this->update_location_history_items(); } @@ -110,7 +107,7 @@ class MenuButton : public Button { const ibitmap *favorite_location_icon; - std::optional menu_handler; + iv_menuhandler menu_handler; std::pair get_menu_position() const { SetFont(this->font.get(), BLACK); diff --git a/src/meson.build b/src/meson.build index 492ba13..d9589ea 100644 --- a/src/meson.build +++ b/src/meson.build @@ -26,6 +26,7 @@ sources = [about_cc] + files( 'events.cc', 'http.cc', 'locationbox.cc', + 'locationlist.cc', 'logging.cc', 'main.cc', 'service.cc', diff --git a/src/model.h b/src/model.h index 0585080..dfbab13 100644 --- a/src/model.h +++ b/src/model.h @@ -36,8 +36,8 @@ struct Location { std::string state; bool operator==(const Location &other) const { - return this->longitude == other.longitude and - this->latitude == other.latitude; + return this->name == other.name and this->country == other.country and + this->state == other.state; } }; diff --git a/src/service.h b/src/service.h index 1e5eb59..1a5a332 100644 --- a/src/service.h +++ b/src/service.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -16,10 +15,6 @@ using namespace std::string_literals; -namespace std { -template using optional = std::experimental::optional; -} - namespace taranis { class Service { diff --git a/src/ui.cc b/src/ui.cc index 7444d7a..7749c27 100644 --- a/src/ui.cc +++ b/src/ui.cc @@ -2,12 +2,14 @@ #include #include +#include #include "currentconditionbox.h" #include "dailyforecastbox.h" #include "events.h" #include "history.h" #include "locationbox.h" +#include "locationlist.h" #include "menu.h" #include "model.h" #include "statusbar.h" @@ -61,6 +63,9 @@ Ui::Ui(std::shared_ptr model) this->alerts_button->get_height() / 2 - CurrentConditionBox::bottom_padding); + this->location_list = + std::make_shared(50, this->fonts, this->icons); + this->children.push_back(location_box); this->children.push_back(menu_button); this->children.push_back(current_condition_box); @@ -120,6 +125,16 @@ void Ui::switch_forecast_widget() { } } +void Ui::open_location_list(const std::vector &locations) { + this->location_list->set_locations(locations); + this->location_list->show(); +} + +std::optional +Ui::get_location_from_location_list(size_t index) const { + return this->location_list->get_location(index); +} + bool Ui::is_on_widget(int pointer_pos_x, int pointer_pos_y, std::shared_ptr widget) { if (not widget) @@ -203,6 +218,8 @@ void Ui::handle_menu_item_selected(int item_index) { CustomEvent::select_location_from_history, history_index); } else if (item_index == MENU_ITEM_QUIT) { SendEvent(event_handler, EVT_EXIT, 0, 0); + } else if (item_index == -1) { + BOOST_LOG_TRIVIAL(debug) << "Menu to be closed"; } else { BOOST_LOG_TRIVIAL(error) << "Unexpected item index " << item_index; } diff --git a/src/ui.h b/src/ui.h index 0d6b9ac..31bb22d 100644 --- a/src/ui.h +++ b/src/ui.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,9 +9,14 @@ #include "fonts.h" #include "hourlyforecastbox.h" #include "icons.h" +#include "locationlist.h" #include "model.h" #include "swipe.h" +namespace std { +template using optional = std::experimental::optional; +} + namespace taranis { class Ui { @@ -31,6 +37,10 @@ class Ui { void switch_forecast_widget(); + void open_location_list(const std::vector &locations); + + std::optional get_location_from_location_list(size_t index) const; + private: std::shared_ptr model; std::shared_ptr icons; @@ -43,6 +53,8 @@ class Ui { std::shared_ptr hourly_forecast_box; std::shared_ptr daily_forecast_box; + std::shared_ptr location_list; + std::vector> children; const int alert_icon_size = 150; diff --git a/src/units.h b/src/units.h index 2101596..50d70ed 100644 --- a/src/units.h +++ b/src/units.h @@ -50,7 +50,7 @@ class Units { } std::string format_speed(double speed) const { - if (this->model->unit_system == UnitSystem::standard || + if (this->model->unit_system == UnitSystem::standard or this->model->unit_system == UnitSystem::metric) { return std::to_string(static_cast(speed)) + "m/s"; } else if (this->model->unit_system == UnitSystem::imperial) {