#ifndef _VERTICAL_MENU_H #define _VERTICAL_MENU_H #include "typeDefs.h" #include "libstd_replacements.h" #include "text_data_table.h" enum class MenuInputHandleState { /** * @brief This value means no input was handled */ NOT_HANDLED, /** * @brief This value means that the input was handled. */ HANDLED, /** * @brief This value means that a choice was made. * This means that if the vertical_menu is running with ::run(), * it should exit and return that choice. */ CHOICE_MADE, /** * @brief The menu was cancelled. */ CANCELLED }; /** * @brief This interface represents an item widget in the vertical menu. * It's responsible for rendering itself and handling its input if * the widget is focused. */ class i_item_widget { public: virtual ~i_item_widget(); virtual void render_item(text_data_table &text_table, unsigned x, unsigned y, bool is_focused) = 0; virtual MenuInputHandleState handle_input() = 0; protected: private: }; /** * @brief This interface allows you to register a handler * to deal with selection changes. * This is useful for managing external components, such as a cursor. */ class i_vertical_menu_state_changed_handler { public: virtual ~i_vertical_menu_state_changed_handler(); virtual void on_show() = 0; virtual void on_hide() = 0; virtual void on_selection_changed(unsigned new_index, unsigned x, unsigned y) = 0; protected: private: }; /** * @brief If you are using the vertical_menu::run() function, * you may want to manage external components on very run cycle. * This interface allows you to do that by implementing the i_run_cycle_handler */ class i_run_cycle_handler { public: virtual ~i_run_cycle_handler(); virtual void on_run_cycle() = 0; protected: private: }; typedef struct vertical_menu_settings { unsigned x; unsigned y; unsigned width; unsigned height; unsigned margin_top; unsigned margin_bottom; unsigned initial_focus_index; unsigned item_height; int text_table_index; /** * @brief This boolean indicates whether the menu * can be "cancelled with the B button." */ bool allow_cancel; /** * @brief This boolean indicates whether the menu should take * ownership of its item widgets. * That means that if this boolean is true, * the destructor of vertical_menu will delete/free all of its * item widgets. */ bool should_delete_item_widgets_on_destruct; /** * @brief This boolean indicates whether the state changed handler (cursor?) * should be hidden when the menu is not focused. */ bool should_hide_state_changed_handler_on_not_focused; } vertical_menu_settings; class vertical_menu { public: vertical_menu(const vertical_menu_settings &settings); ~vertical_menu(); void show(); void hide(); /** * @brief Get the current settings of this widget */ const vertical_menu_settings& get_settings() const; /** * @brief Replace the settings of this widget. */ void set_settings(const vertical_menu_settings &settings); /** * @brief This function is a way to bulk add item widgets to the menu. * The main difference between calling this function and add_item_widget() * multiple times is that this function will reserve() enough space in the * vector first, to avoid multiple resizes. */ void add_item_widgets(i_item_widget **item_widgets, unsigned num_widgets); /** * @brief This function adds an item widget to the menu. * The menu will take ownership of the pointer, if * settings_.should_delete_item_widgets_on_destruct is true. * Otherwise, the caller is responsible for managing the memory of the item widgets. */ void add_item_widget(i_item_widget *item_widget); /** * @brief This function clears all item widgets from the menu. * If settings_.should_delete_item_widgets_on_destruct is true, * it will also delete the item widgets. */ void clear_item_widgets(); /** * @brief This function gets the item widget * at the given index. */ i_item_widget* get_item_widget_at(unsigned index) const; /** * @brief Sets whether the menu is focused or not. * A focused menu will handle input. */ void set_focused(bool is_focused); /** * @brief This function handles button input for the menu. */ MenuInputHandleState handle_input(); /** * @brief This function updates the viewport by * re-rendering the visible items. */ void update_viewport(); void clear_viewport(); /** * @brief This function can be used to manage a blocking call * to vertical_menu, in which you want vertical_menu * to handle everything. * * This can be useful during one of those script_array commands. * Alternatively, you can just call handle_input() and manage the loop * manually. * * @return Will return the index of the chosen item if a choice was made, * or UINT32_MAX if no choice was made and the menu was exited in some other way * (ex: B button). */ unsigned run(); /** * @brief Sets the optional state changed handler. * This is useful for managing external components, such as a cursor. */ void set_state_changed_handler(i_vertical_menu_state_changed_handler *handler); /** * @brief This function sets a handler that will run on every * cycle in the run() function. * * It's only relevant if you use ::run() though. */ void set_run_cycle_handler(i_run_cycle_handler *handler); protected: private: void handle_selection_change(unsigned new_index, unsigned x, unsigned y); vertical_menu_settings settings_; i_vertical_menu_state_changed_handler *state_changed_handler_; i_run_cycle_handler *run_cycle_handler_; unsigned focused_index_; unsigned viewport_start_index_; ptgb::vector items_; bool is_focused_; }; typedef struct simple_item_widget_data { struct { unsigned text_table_index; unsigned margin_left; unsigned margin_top; } text; unsigned value; void (*on_execute_callback)(void *context); } simple_item_widget_data; class simple_item_renderer : public i_item_widget { public: simple_item_renderer(const simple_item_widget_data &data); virtual ~simple_item_renderer(); const simple_item_widget_data& get_data() const; void render_item(text_data_table &text_table, unsigned x, unsigned y, bool is_focused) override; MenuInputHandleState handle_input() override; protected: private: simple_item_widget_data data_; }; #endif