From eb9d36a3b21143d64917610031695d352596b917 Mon Sep 17 00:00:00 2001 From: Matthias Meulien Date: Tue, 14 Nov 2023 22:17:30 +0100 Subject: [PATCH] Daily forecast can be used as power off logo Refs: #28 --- NEWS.md | 3 ++ icons/icon_wallpaper.bmp | Bin 0 -> 11162 bytes icons/meson.build | 1 + src/alerts.h | 1 - src/app.cc | 6 +++- src/button.h | 2 +- src/config.cc | 12 +++++-- src/currentconditionbox.h | 1 - src/dailyforecastbox.h | 1 - src/hourlyforecastbox.h | 1 - src/icons.h | 4 +++ src/locationbox.h | 1 - src/logo.cc | 74 ++++++++++++++++++++++++++++++++++++++ src/logo.h | 46 ++++++++++++++++++++++++ src/meson.build | 1 + src/statusbar.h | 11 +++--- src/ui.cc | 7 ++++ src/ui.h | 6 ++-- src/widget.h | 4 +-- 19 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 icons/icon_wallpaper.bmp create mode 100644 src/logo.cc create mode 100644 src/logo.h diff --git a/NEWS.md b/NEWS.md index 536b864..602afa2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added +- Daily forecast can be used as power off logo + [#28](https://github.com/orontee/taranis/issues/28) + - Menu item to edit location [#68](https://github.com/orontee/taranis/issues/68) diff --git a/icons/icon_wallpaper.bmp b/icons/icon_wallpaper.bmp new file mode 100644 index 0000000000000000000000000000000000000000..56f5b48996bbf645574854f0287a7801aad16785 GIT binary patch literal 11162 zcmeI1by!qQ6vltpg<@f0H!8MbD~gDSo!H$7c3?MHn23dejV&r5cCLVeiWr0Ej0UNP5EU{{lLoU~;4F1{) z6)jN|Ul9}^xsH)Z*GckDI=(WcpsS^&MW#%d$ecMdS+ZmyYu2n}%a)Do*|U=)M-Fo4 z%t@|XxyYS6H+k~pL0em!ym|AIFJC@%bacp{KR*Qu6rfRWH>ej7Gy?XVi zU%x&L8Z@9`!-g0d8q%myBN{huOp_)}Xxg+X&6+i%dGqG9Xwia}EnCv6RV$2)jA-4u zHEr6o!PwZCwr$&DVq!wOcI_}VHKl#~_L!NO(V;^J%+1Z|*s&v>I(4FR=gxHL(uJ;F zyV9*&H@bK4PLCcv=-IO;y?XVcckkZx>C*=b3k&-8?MuIY{pjDnKLZ8~VBo-k3>q|u z!Gi}gWXKSP4jsy{VZ#_cd^jUUj9}!*k&GHOiqWG-GiJ;f#*Q7!xN+kcKYl!xmX=JI zFaawoE3B=pnK*GGlO|1K^5n@(nKFf`Q>QX*+BBw5pU#XKGnhGZCbMSE!p6pi*|TRe zXU-hv&YjD=dGnY*e?AKqEMVcng)CaMh{cN+vt-ESbt~JpZDafP?d;gG11BdZcJAECu3fv>y?Zx%_Uysg*_pk2_p)!_KKAe5&w&F6 zIC$_NhYlU$@ZrO_xVUiS$PtbnJ<73T$2flcI44e=;N;1ZoH})i)2C14>gvjwGiNw^ z_AKYlox{z|4R?2UJUl!&fBrn4o}OH|aDj^#FLLS9B`#mS%#|xwxO(*}*REaT`t|GF zxN(D^7QHZY65ldZN~nCcp(-+Q;A92{>8HzEgosSD{~t}kJI&e(MNinF=%=CG4xVXH zQ&kbe;jAi2b>s~0sVb!OP{K%wm6T=wn5&52w+^N8wZs|zb#{(4g3Y64nKsr;ZzDvFc~0bKjr0DFhsc;xDtdg1DRshXA>Zjf}t)9;gteH z5bWbYdd4IP)Z~Y5pOMlf6xFcc@3Mlizk;z6u58V|v%30OvG7DI>&N?N+=xtAy= z{D)Q}@%Rl(!uXAlRE8Pi>R-H{dx`h-wC5Qj#bieM^9)g9zQjDu`4V%`Pk+8d5D(kr zC6&BS`qKmodel->refresh_date = std::chrono::system_clock::now(); this->history->update_history_maybe(); this->show(); + + if (this->ui) { + this->ui->generate_logo_maybe(); + } + } else if (param_one == CustomEvent::warning_emitted) { if (param_two == CustomEventParam::invalid_location) { DialogSynchro( @@ -360,7 +365,6 @@ void App::refresh_data(CallContext context) { void App::open_about_dialog() { BOOST_LOG_TRIVIAL(debug) << "Opening about dialog"; - const auto about_content = get_about_content(); Dialog(ICON_INFORMATION, GetLangText("About"), about_content.c_str(), GetLangText("Ok"), nullptr, &handle_about_dialog_button_clicked); diff --git a/src/button.h b/src/button.h index 4474c62..f17a56e 100644 --- a/src/button.h +++ b/src/button.h @@ -56,12 +56,12 @@ class Button : public Widget, Activatable { return {pos_x, pos_y}; } -protected: void do_paint() override { const auto [pos_x, pos_y] = this->get_icon_top_left_position(); DrawBitmap(pos_x, pos_y, this->icon); } +protected: virtual void on_clicked(){}; private: diff --git a/src/config.cc b/src/config.cc index c08042e..1b25b4a 100644 --- a/src/config.cc +++ b/src/config.cc @@ -24,6 +24,8 @@ char *start_with_daily_forecast_values[] = { char *custom_api_key_value[] = {const_cast(""), nullptr}; +char *yes_no_values[] = {"@Yes", "@No", nullptr}; + // Note that translating is handled by the configuration editor but // translations must be provided as usual @@ -38,6 +40,10 @@ static iconfigedit config_template[] = { {CFG_TEXT, &icon_key, const_cast(GetLangText("Custom API key")), nullptr, const_cast("api_key"), const_cast(""), custom_api_key_value, nullptr}, + {CFG_CHOICE, &icon_wallpaper, + const_cast(GetLangText("Generate shutdown logo")), nullptr, + const_cast("generate_shutdown_logo"), "@Yes", yes_no_values, + nullptr}, {0}}; // Don't use hints since failed to find a way to change their font @@ -51,12 +57,12 @@ Config::Config() { bool Config::read_bool(const std::string &name, bool default_value) { return std::strcmp( - ReadString(config, name.c_str(), default_value ? "1" : "0"), - "1") == 0; + ReadString(config, name.c_str(), default_value ? "@Yes" : "@No"), + "@Yes") == 0; } void Config::write_bool(const std::string &name, bool value) { - WriteString(config, name.c_str(), value ? "1" : "0"); + WriteString(config, name.c_str(), value ? "@Yes" : "@No"); } int Config::read_int(const std::string &name, int default_value) { diff --git a/src/currentconditionbox.h b/src/currentconditionbox.h index 946b3fb..10b6bd1 100644 --- a/src/currentconditionbox.h +++ b/src/currentconditionbox.h @@ -24,7 +24,6 @@ class CurrentConditionBox : public Widget { this->bottom_padding); } -protected: void do_paint() override { const auto condition = this->model->current_condition; if (not condition) { diff --git a/src/dailyforecastbox.h b/src/dailyforecastbox.h index 3748fc3..18ce303 100644 --- a/src/dailyforecastbox.h +++ b/src/dailyforecastbox.h @@ -31,7 +31,6 @@ class DailyForecastBox : public Widget { this->row_height = this->bounding_box.h / DailyForecastBox::row_count; } -protected: void do_paint() override { this->draw_values(); this->draw_frame(); diff --git a/src/hourlyforecastbox.h b/src/hourlyforecastbox.h index 8cb4037..94af2ab 100644 --- a/src/hourlyforecastbox.h +++ b/src/hourlyforecastbox.h @@ -21,7 +21,6 @@ class HourlyForecastBox : public Widget { void decrease_forecast_offset(); -protected: void do_paint() override; private: diff --git a/src/icons.h b/src/icons.h index 6353e9d..2cf3015 100644 --- a/src/icons.h +++ b/src/icons.h @@ -22,6 +22,7 @@ extern const ibitmap icon_menu; extern const ibitmap icon_radio_button_checked; extern const ibitmap icon_radio_button_unchecked; extern const ibitmap icon_taranis; +extern const ibitmap icon_wallpaper; extern const ibitmap icon_warning; ; @@ -71,6 +72,9 @@ class Icons { if (name == "radio-button-unchecked") { return const_cast(&icon_radio_button_unchecked); } + if (name == "wallpaper") { + return const_cast(&icon_wallpaper); + } if (name == "warning") { return const_cast(&icon_warning); } diff --git a/src/locationbox.h b/src/locationbox.h index de9a071..7473afe 100644 --- a/src/locationbox.h +++ b/src/locationbox.h @@ -40,7 +40,6 @@ class LocationBox : public Widget { void edit_location(); -protected: void do_paint() override { std::string location_text = format_location(this->model->location); location_text = elide_maybe(location_text); diff --git a/src/logo.cc b/src/logo.cc new file mode 100644 index 0000000..3a31a1d --- /dev/null +++ b/src/logo.cc @@ -0,0 +1,74 @@ +#include "logo.h" + +#include + +#include "config.h" +#include "dailyforecastbox.h" +#include "inkview.h" +#include "statusbar.h" + +namespace taranis { + +Logo::Logo(std::shared_ptr model, std::shared_ptr icons, + std::shared_ptr fonts) + : Widget{0, 0, ScreenWidth(), ScreenHeight()}, + location_box{0, 0, model, fonts}, status_bar{model, fonts}, + forecast_box{0, + this->location_box.get_height() + Logo::vertical_padding, + ScreenWidth(), + ScreenHeight() - 2 * Logo::vertical_padding - + this->location_box.get_height() - + this->status_bar.get_height(), + model, + icons, + fonts} {} + +void Logo::do_paint() { + this->location_box.do_paint(); + this->status_bar.do_paint(); + this->forecast_box.do_paint(); +} + +void LogoGenerator::generate_maybe() const { + Config config; + const auto must_generate = config.read_bool("generate_shutdown_logo", false); + if (must_generate) { + this->generate(); + } else { + BOOST_LOG_TRIVIAL(debug) << "Skipping logo generation"; + } +} + +void LogoGenerator::generate() const { + BOOST_LOG_TRIVIAL(debug) << "Generating new logo"; + + auto *const original_canvas = GetCanvas(); + if (not original_canvas) { + return; + } + std::vector data; + data.resize(ScreenHeight() * ScreenWidth()); + + icanvas canvas{ScreenWidth(), + ScreenHeight(), + ScreenWidth(), + original_canvas->depth, + 0, + ScreenWidth(), + 0, + ScreenHeight(), + data.data()}; + SetCanvas(&canvas); + + Logo logo{this->model, this->icons, this->fonts}; + logo.paint(); + + const auto bitmap = + BitmapFromCanvas(0, 0, ScreenWidth(), ScreenHeight(), 0, &canvas); + const auto filename = + std::string{USEROFFLOGOPATH} + "/taranis_weather_forecast.bmp"; + SaveBitmap(filename.data(), bitmap); + + SetCanvas(original_canvas); +} +} // namespace taranis diff --git a/src/logo.h b/src/logo.h new file mode 100644 index 0000000..7ad0767 --- /dev/null +++ b/src/logo.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include "dailyforecastbox.h" +#include "fonts.h" +#include "icons.h" +#include "locationbox.h" +#include "model.h" +#include "statusbar.h" +#include "widget.h" + +namespace taranis { + +class Logo : public Widget { +public: + Logo(std::shared_ptr model, std::shared_ptr icons, + std::shared_ptr fonts); + + void do_paint() override; + +private: + static constexpr int horizontal_padding{25}; + static constexpr int vertical_padding{25}; + + LocationBox location_box; + StatusBar status_bar; + DailyForecastBox forecast_box; +}; + +struct LogoGenerator { + LogoGenerator(std::shared_ptr model, std::shared_ptr icons, + std::shared_ptr fonts) + : model{model}, icons{icons}, fonts{fonts} {} + + void generate_maybe() const; + +private: + std::shared_ptr model; + std::shared_ptr icons; + std::shared_ptr fonts; + + void generate() const; +}; +} // namespace taranis diff --git a/src/meson.build b/src/meson.build index 05d010e..ab62e17 100644 --- a/src/meson.build +++ b/src/meson.build @@ -70,6 +70,7 @@ sources = [about_cc, icons_cc, l10n_cc] + files( 'locationbox.cc', 'locationselector.cc', 'logging.cc', + 'logo.cc', 'main.cc', 'menu.cc', 'service.cc', diff --git a/src/statusbar.h b/src/statusbar.h index 09b4086..b537509 100644 --- a/src/statusbar.h +++ b/src/statusbar.h @@ -1,11 +1,11 @@ #pragma once #include +#include #include #include #include -#include "experimental/optional" #include "fonts.h" #include "model.h" #include "util.h" @@ -25,7 +25,6 @@ class StatusBar : public Widget { this->set_bounding_box(pos_x, pos_y, width, height); } -protected: void do_paint() override { std::stringstream first_row_text; if (this->model->refresh_date == std::experimental::nullopt) { @@ -51,12 +50,12 @@ class StatusBar : public Widget { } private: + static constexpr int left_padding{50}; + static constexpr int top_padding{50}; + static constexpr int bottom_padding{25}; + std::shared_ptr model; std::shared_ptr font; - - const int left_padding{50}; - const int top_padding{50}; - const int bottom_padding{25}; }; } // namespace taranis diff --git a/src/ui.cc b/src/ui.cc index 1544440..923526a 100644 --- a/src/ui.cc +++ b/src/ui.cc @@ -11,6 +11,7 @@ #include "keys.h" #include "locationbox.h" #include "locationselector.h" +#include "logo.h" #include "menu.h" #include "model.h" #include "statusbar.h" @@ -87,6 +88,7 @@ Ui::Ui(std::shared_ptr model) void Ui::paint() { ClearScreen(); + // Will justify to call do_paint() on children and not paint() this->check_modal_visibility(); @@ -140,6 +142,11 @@ Ui::get_location_from_location_list(size_t index) const { return this->location_selector->get_location(index); } +void Ui::generate_logo_maybe() const { + LogoGenerator generator{this->model, this->icons, this->fonts}; + generator.generate_maybe(); +} + bool Ui::is_consumer_active(std::shared_ptr consumer) { return ((consumer == this->visible_modal) or not this->visible_modal); // modals expected to register / unregister on visibility change diff --git a/src/ui.h b/src/ui.h index d7f409f..03cec85 100644 --- a/src/ui.h +++ b/src/ui.h @@ -35,16 +35,18 @@ class Ui : public KeyEventDispatcher { int handle_pointer_event(int event_type, int pointer_pos_x, int pointer_pos_y); - void edit_location() { this->location_box->edit_location(); } - void display_alert() { this->alert_viewer->open(); } void switch_forecast_widget(); + void edit_location() { this->location_box->edit_location(); } + void open_location_list(const std::vector &locations); std::optional get_location_from_location_list(size_t index) const; + void generate_logo_maybe() const; + protected: bool is_consumer_active(std::shared_ptr consumer) override; diff --git a/src/widget.h b/src/widget.h index 2dba291..3d6a166 100644 --- a/src/widget.h +++ b/src/widget.h @@ -60,6 +60,8 @@ struct Widget : public KeyEventConsumer { // screen could be outdated after a visibility update } + virtual void do_paint() = 0; + bool is_in_bouding_box(int pos_x, int pos_y) const { return IsInRect(pos_x, pos_y, &this->bounding_box); } @@ -76,8 +78,6 @@ struct Widget : public KeyEventConsumer { void fill_bounding_box() const { FillAreaRect(&this->bounding_box, WHITE); } - virtual void do_paint() = 0; - void set_bounding_box(int pos_x, int pos_y, int width, int height) { this->bounding_box.x = pos_x; this->bounding_box.y = pos_y;