@@ -84,16 +84,39 @@ void draw(Bitmap &viewport, const Game &game)
8484 }
8585}
8686
87+ ftxui::ButtonOption Animated (ftxui::Color background, // NOLINT
88+ ftxui::Color foreground, // NOLINT
89+ ftxui::Color background_active, // NOLINT
90+ ftxui::Color foreground_active) // NOLINT
91+ {
92+ ftxui::ButtonOption option;
93+ option.transform = [](const ftxui::EntryState &s) {
94+ auto element = ftxui::text (s.label );
95+ if (s.focused ) { element |= ftxui::bold; }
96+ return element;
97+ };
98+ option.animated_colors .foreground .Set (foreground, foreground_active);
99+ option.animated_colors .background .Set (background, background_active);
100+ return option;
101+ }
102+
103+ ftxui::ButtonOption Animated ()
104+ {
105+ return Animated (ftxui::Color::Black,
106+ ftxui::Color::GrayLight,//
107+ ftxui::Color::GrayDark,
108+ ftxui::Color::White);
109+ }
87110struct Displayed_Menu
88111{
89112 Displayed_Menu (Menu menu_, Game &game) : menu{ std::move (menu_) }
90113 {
91114 ftxui::Components menu_lines;
92115
93- for (const auto &item : menu.items )
94- {
116+ for (const auto &item : menu.items ) {
95117 if (!item.visible || item.visible (game)) {
96- menu_lines.push_back (ftxui::Button (item.text , [&game, &item]() { item.action (game); }, ftxui::ButtonOption{false }));
118+ menu_lines.push_back (ftxui::Button (
119+ item.text , [&game, &item]() { item.action (game); }, Animated ()));
97120 }
98121 }
99122
@@ -122,6 +145,39 @@ template<typename Mutex> class log_sink : public spdlog::sinks::base_sink<Mutex>
122145 void flush_ () override {}
123146};
124147
148+
149+
150+ // todo make PR back into FTXUI?
151+ class CatchEventBase : public ftxui ::ComponentBase
152+ {
153+ public:
154+ // Constructor.
155+ explicit CatchEventBase (std::function<bool (ftxui::Event)> on_event) : on_event_(std::move(on_event)) {}
156+
157+ // Component implementation.
158+ bool OnEvent (ftxui::Event event) override
159+ {
160+ if (on_event_ (event)) {
161+ return true ;
162+ } else {
163+ return ComponentBase::OnEvent (event);
164+ }
165+ }
166+
167+ [[nodiscard]] bool Focusable () const override { return true ; }
168+
169+ protected:
170+ std::function<bool (ftxui::Event)> on_event_;
171+ };
172+
173+ ftxui::Component CatchEvent (ftxui::Component child, std::function<bool (ftxui::Event event)> on_event)
174+ {
175+ auto out = Make<CatchEventBase>(std::move (on_event));
176+ out->Add (std::move (child));
177+ return out;
178+ }
179+
180+
125181void play_game (Game &game, std::shared_ptr<log_sink<std::mutex>> log_sink)// NOLINT cognitive complexity
126182{
127183
@@ -214,7 +270,7 @@ void play_game(Game &game, std::shared_ptr<log_sink<std::mutex>> log_sink)// NOL
214270
215271 auto container = ftxui::Container::Vertical ({});
216272
217- auto key_press = ftxui ::CatchEvent (container, [&](const ftxui::Event &event) {
273+ auto key_press = lefticus::awesome_game ::CatchEvent (container, [&](const ftxui::Event &event) {
218274 events.push_back (event);
219275 return false ;
220276 });
@@ -232,8 +288,7 @@ void play_game(Game &game, std::shared_ptr<log_sink<std::mutex>> log_sink)// NOL
232288 try {
233289 game_iteration (new_time - last_time);
234290 } catch (const std::exception &exception) {
235- const auto message =
236- fmt::format (" Unhandled std::exception in game_iteration:\n\n {}" , exception.what ());
291+ const auto message = fmt::format (" Unhandled std::exception in game_iteration:\n\n {}" , exception.what ());
237292 game.popup_message = message;
238293 spdlog::critical (message);
239294 } catch (...) {
@@ -262,6 +317,7 @@ void play_game(Game &game, std::shared_ptr<log_sink<std::mutex>> log_sink)// NOL
262317 };
263318
264319
320+ // todo at some point replace this with a renderer that detects and uses the 'focus' flag
265321 auto game_renderer = ftxui::Renderer (key_press, make_layout);
266322
267323 auto menu_renderer =
0 commit comments