#pragma once #include "Common/System/Request.h" #include "Common/Data/Text/I18n.h" #include "Common/UI/UIScreen.h" #include "Common/UI/UI.h" #include "Common/UI/View.h" #include "Common/UI/ScrollView.h" // from StringUtils enum class StringRestriction; namespace UI { static const float NO_DEFAULT_FLOAT = -1000000.0f; static const int NO_DEFAULT_INT = -1000000; class PopupScreen : public UIDialogScreen { public: PopupScreen(std::string_view title, std::string_view button1 = "", std::string_view button2 = ""); virtual void CreatePopupContents(UI::ViewGroup *parent) = 0; void CreateViews() override; bool isTransparent() const override { return true; } void touch(const TouchInput &touch) override; bool key(const KeyInput &key) override; void TriggerFinish(DialogResult result) override; void SetPopupOrigin(const UI::View *view); void SetPopupOffset(float y) { offsetY_ = y; } void SetAlignTop(bool alignTop) { alignTop_ = alignTop; } void SetHasDropShadow(bool has) { hasDropShadow_ = has; } // For the postproc param sliders on DisplayLayoutScreen bool wantBrightBackground() const override { return !hasDropShadow_; } protected: virtual bool FillVertical() const { return false; } virtual UI::Size PopupWidth() const { return 550; } virtual bool ShowButtons() const { return true; } virtual bool CanComplete(DialogResult result) { return true; } virtual void OnCompleted(DialogResult result) {} std::string_view Title() { return title_; } void update() override; private: UI::LinearLayout *box_ = nullptr; UI::Choice *defaultButton_ = nullptr; ImageID button1Image_; std::string title_; std::string button1_; std::string button2_; enum { FRAMES_LEAD_IN = 6, FRAMES_LEAD_OUT = 4, }; int frames_ = 0; int finishFrame_ = -1; DialogResult finishResult_ = DR_CANCEL; bool hasPopupOrigin_ = false; Point2D popupOrigin_; float offsetY_ = 0.0f; bool alignTop_ = false; bool hasDropShadow_ = true; }; class ListPopupScreen : public PopupScreen { public: ListPopupScreen(std::string_view title) : PopupScreen(title) {} ListPopupScreen(std::string_view title, const std::vector &items, int selected, std::function callback, bool showButtons = false) : PopupScreen(title, "OK", "Cancel"), adaptor_(items, selected), callback_(callback), showButtons_(showButtons) { } ListPopupScreen(std::string_view title, const std::vector &items, int selected, bool showButtons = false) : PopupScreen(title, "OK", "Cancel"), adaptor_(items, selected), showButtons_(showButtons) { } int GetChoice() const { return listView_->GetSelected(); } std::string GetChoiceString() const { return adaptor_.GetTitle(listView_->GetSelected()); } void SetHiddenChoices(const std::set &hidden) { hidden_ = hidden; } void SetChoiceIcons(const std::map &icons) { icons_ = icons; } const char *tag() const override { return "listpopup"; } UI::Event OnChoice; protected: bool FillVertical() const override { return false; } bool ShowButtons() const override { return showButtons_; } void CreatePopupContents(UI::ViewGroup *parent) override; UI::StringVectorListAdaptor adaptor_; UI::ListView *listView_ = nullptr; private: void OnListChoice(UI::EventParams &e); std::function callback_; bool showButtons_ = false; std::set hidden_; std::map icons_; }; class MessagePopupScreen : public PopupScreen { public: MessagePopupScreen(std::string_view title, std::string_view message, std::string_view button1, std::string_view button2, std::function callback) : PopupScreen(title, button1, button2), message_(message), callback_(callback) {} const char *tag() const override { return "MessagePopupScreen"; } UI::Event OnChoice; protected: bool FillVertical() const override { return false; } bool ShowButtons() const override { return true; } void CreatePopupContents(UI::ViewGroup *parent) override; private: void OnCompleted(DialogResult result) override; std::string message_; std::function callback_; }; class SliderPopupScreen : public PopupScreen { public: SliderPopupScreen(int *value, int minValue, int maxValue, int defaultValue, std::string_view title, int step, std::string_view units, bool liveUpdate) : PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), minValue_(minValue), maxValue_(maxValue), defaultValue_(defaultValue), step_(step), liveUpdate_(liveUpdate) {} void CreatePopupContents(ViewGroup *parent) override; void SetNegativeDisable(const std::string &str) { negativeLabel_ = str; disabled_ = *value_ < 0; } void RestrictChoices(const int *fixedChoices, size_t numFixedChoices) { fixedChoices_ = fixedChoices; numFixedChoices_ = numFixedChoices; } const char *tag() const override { return "SliderPopup"; } Event OnChange; private: void OnDecrease(EventParams ¶ms); void OnIncrease(EventParams ¶ms); void OnTextChange(EventParams ¶ms); void OnSliderChange(EventParams ¶ms); void OnCompleted(DialogResult result) override; void UpdateTextBox(); Slider *slider_ = nullptr; UI::TextEdit *edit_ = nullptr; std::string units_; std::string negativeLabel_; int *value_; int sliderValue_ = 0; int minValue_; int maxValue_; int defaultValue_; int step_; bool liveUpdate_; bool changing_ = false; bool disabled_ = false; const int *fixedChoices_ = nullptr; size_t numFixedChoices_ = 0; }; class SliderFloatPopupScreen : public PopupScreen { public: SliderFloatPopupScreen(float *value, float minValue, float maxValue, float defaultValue, std::string_view title, float step = 1.0f, std::string_view units = "", bool liveUpdate = false) : PopupScreen(title, "OK", "Cancel"), units_(units), value_(value), originalValue_(*value), minValue_(minValue), maxValue_(maxValue), defaultValue_(defaultValue), step_(step), liveUpdate_(liveUpdate) {} void CreatePopupContents(UI::ViewGroup *parent) override; const char *tag() const override { return "SliderFloatPopup"; } Event OnChange; private: void OnIncrease(EventParams ¶ms); void OnDecrease(EventParams ¶ms); void OnTextChange(EventParams ¶ms); void OnSliderChange(EventParams ¶ms); void OnCompleted(DialogResult result) override; void UpdateTextBox(); UI::SliderFloat *slider_ = nullptr; UI::TextEdit *edit_ = nullptr; std::string units_; float sliderValue_ = 0.0f; float originalValue_ = 0.0f; float *value_; float minValue_; float maxValue_; float defaultValue_; float step_; bool changing_ = false; bool liveUpdate_; }; class TextEditPopupScreen : public PopupScreen { public: TextEditPopupScreen(std::string *value, std::string_view placeholder, std::string_view title, int maxLen) : PopupScreen(title, "OK", "Cancel"), value_(value), placeholder_(placeholder), maxLen_(maxLen) {} void CreatePopupContents(ViewGroup *parent) override; const char *tag() const override { return "TextEditPopup"; } void SetPasswordMasking(bool masking) { passwordMasking_ = masking; } Event OnChange; private: void OnCompleted(DialogResult result) override; TextEdit *edit_ = nullptr; std::string *value_; std::string textEditValue_; std::string placeholder_; int maxLen_; bool passwordMasking_ = false; }; struct ContextMenuItem { const char *text; const char *imageID; }; class AbstractContextMenuScreen : public PopupScreen { public: AbstractContextMenuScreen(UI::View *sourceView) : PopupScreen("", "", ""), sourceView_(sourceView) {} protected: UI::Size PopupWidth() const override { return 350; } UI::View *sourceView_; void AlignPopup(UI::View *parent); }; // Once a selection has been made, class PopupContextMenuScreen : public AbstractContextMenuScreen { public: PopupContextMenuScreen(const ContextMenuItem *items, size_t itemCount, I18NCat category, UI::View *sourceView); const char *tag() const override { return "ContextMenuPopup"; } void SetEnabled(size_t index, bool enabled) { enabled_[index] = enabled; } UI::Event OnChoice; private: void CreatePopupContents(ViewGroup *parent) override; const ContextMenuItem *items_; size_t itemCount_; I18NCat category_; std::vector enabled_; }; class PopupCallbackScreen : public AbstractContextMenuScreen { public: PopupCallbackScreen(std::function createViews, UI::View *sourceView); const char *tag() const override { return "ContextMenuCallbackPopup"; } private: void CreatePopupContents(ViewGroup *parent) override; std::function createViews_; }; // Reads and writes value to determine the current selection. class PopupMultiChoice : public AbstractChoiceWithValueDisplay { public: PopupMultiChoice(int *value, std::string_view text, const char **choices, int minVal, int numChoices, I18NCat category, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr); void Update() override; void HideChoice(int c) { hidden_.insert(c); } bool IsChoiceHidden(int c) const { return hidden_.find(c) != hidden_.end(); } void SetPreOpenCallback(std::function callback) { preOpenCallback_ = callback; } void SetChoiceIcon(int c, ImageID id) { icons_[c] = id; } void SetChoiceIcons(std::map icons) { icons_ = icons; } UI::Event OnChoice; protected: std::string ValueText() const override; ImageID ValueImage() const override { auto iter = icons_.find(*value_); if (iter != icons_.end()) { return iter->second; } return ImageID::invalid(); } int *value_; const char **choices_; int minVal_; int numChoices_; void UpdateText(); private: void HandleClick(UI::EventParams &e); void ChoiceCallback(int num); virtual bool PostChoiceCallback(int num) { return true; } I18NCat category_; ScreenManager *screenManager_; std::string valueText_; bool restoreFocus_ = false; std::set hidden_; std::map icons_; std::function preOpenCallback_; bool callbackExecuted_ = false; }; // Allows passing in a dynamic vector of strings. Saves the string. class PopupMultiChoiceDynamic : public PopupMultiChoice { public: // TODO: This all is absolutely terrible, just done this way to be conformant with the internals of PopupMultiChoice. PopupMultiChoiceDynamic(std::string *value, std::string_view text, const std::vector &choices, I18NCat category, ScreenManager *screenManager, std::vector *values = nullptr, UI::LayoutParams *layoutParams = nullptr) : UI::PopupMultiChoice(&valueInt_, text, nullptr, 0, (int)choices.size(), category, screenManager, layoutParams), valueStr_(value) { if (values) { _dbg_assert_(choices.size() == values->size()); } choices_ = new const char *[numChoices_]; valueInt_ = 0; for (int i = 0; i < numChoices_; i++) { choices_[i] = new char[choices[i].size() + 1]; memcpy((char *)choices_[i], choices[i].c_str(), choices[i].size() + 1); if (values) { if (*value == (*values)[i]) valueInt_ = i; } if (*value == choices_[i]) valueInt_ = i; } value_ = &valueInt_; if (values) { choiceValues_ = *values; } UpdateText(); } ~PopupMultiChoiceDynamic() { for (int i = 0; i < numChoices_; i++) { delete[] choices_[i]; } delete[] choices_; } protected: bool PostChoiceCallback(int num) override { if (!valueStr_) { return true; } const char *value = choices_[num]; if (choiceValues_.size() == numChoices_) { value = choiceValues_[num].c_str(); } if (*valueStr_ != value) { *valueStr_ = value; return true; } else { return false; } } private: int valueInt_; std::string *valueStr_; std::vector choiceValues_; }; class PopupSliderChoice : public AbstractChoiceWithValueDisplay { public: PopupSliderChoice(int *value, int minValue, int maxValue, int defaultValue, std::string_view text, ScreenManager *screenManager, std::string_view units = "", LayoutParams *layoutParams = 0); PopupSliderChoice(int *value, int minValue, int maxValue, int defaultValue, std::string_view text, int step, ScreenManager *screenManager, std::string_view units = "", LayoutParams *layoutParams = 0); void SetFormat(std::string_view fmt); void SetZeroLabel(std::string_view str) { zeroLabel_ = str; } void SetLiveUpdate(bool update) { liveUpdate_ = update; } void SetNegativeDisable(std::string_view str) { negativeLabel_ = str; } void RestrictChoices(const int *fixedChoices, size_t numFixedChoices) { fixedChoices_ = fixedChoices; numFixedChoices_ = numFixedChoices; } Event OnChange; protected: std::string ValueText() const override; private: void HandleClick(EventParams &e); void HandleChange(EventParams &e); int *value_; int minValue_; int maxValue_; int defaultValue_; int step_; std::string fmt_; std::string zeroLabel_; std::string negativeLabel_; std::string units_; ScreenManager *screenManager_; bool restoreFocus_ = false; bool liveUpdate_ = false; const int *fixedChoices_ = nullptr; size_t numFixedChoices_ = 0; }; class PopupSliderChoiceFloat : public AbstractChoiceWithValueDisplay { public: PopupSliderChoiceFloat(float *value, float minValue, float maxValue, float defaultValue, std::string_view text, ScreenManager *screenManager, std::string_view units = "", LayoutParams *layoutParams = 0); PopupSliderChoiceFloat(float *value, float minValue, float maxValue, float defaultValue, std::string_view text, float step, ScreenManager *screenManager, std::string_view units = "", LayoutParams *layoutParams = 0); void SetFormat(std::string_view fmt); void SetZeroLabel(const std::string &str) { zeroLabel_ = str; } void SetLiveUpdate(bool update) { liveUpdate_ = update; } void SetHasDropShadow(bool has) { hasDropShadow_ = has; } Event OnChange; protected: std::string ValueText() const override; private: void HandleClick(EventParams &e); void HandleChange(EventParams &e); float *value_; float minValue_; float maxValue_; float defaultValue_; float step_; std::string fmt_; std::string zeroLabel_; std::string units_; ScreenManager *screenManager_; bool restoreFocus_ = false; bool liveUpdate_ = false; bool hasDropShadow_ = true; }; // NOTE: This one will defer to a system-native dialog if possible. class PopupTextInputChoice : public AbstractChoiceWithValueDisplay { public: PopupTextInputChoice(RequesterToken token, std::string *value, std::string_view title, std::string_view placeholder, int maxLen, ScreenManager *screenManager, LayoutParams *layoutParams = 0); Event OnChange; void SetRestriction(StringRestriction restriction, int minLength) { restriction_ = restriction; minLen_ = minLength; } protected: std::string ValueText() const override; private: void HandleClick(EventParams &e); void HandleChange(EventParams &e); RequesterToken token_; ScreenManager *screenManager_; std::string *value_; std::string placeHolder_; std::string defaultText_; int maxLen_; int minLen_ = 0; bool restoreFocus_ = false; StringRestriction restriction_; }; class ChoiceWithValueDisplay : public AbstractChoiceWithValueDisplay { public: ChoiceWithValueDisplay(int *value, std::string_view text, LayoutParams *layoutParams = 0) : AbstractChoiceWithValueDisplay(text, layoutParams), iValue_(value) {} ChoiceWithValueDisplay(int *value, ImageID imageId, LayoutParams *layoutParams = 0) : AbstractChoiceWithValueDisplay("", imageId, layoutParams), iValue_(value) {} ChoiceWithValueDisplay(std::string *value, std::string_view text, I18NCat category, LayoutParams *layoutParams = 0) : AbstractChoiceWithValueDisplay(text, layoutParams), sValue_(value), category_(category) {} ChoiceWithValueDisplay(std::string *value, std::string_view text, std::string(*translateCallback)(std::string_view value), LayoutParams *layoutParams = 0) : AbstractChoiceWithValueDisplay(text, layoutParams), sValue_(value), translateCallback_(translateCallback) {} private: std::string ValueText() const override; std::string *sValue_ = nullptr; int *iValue_ = nullptr; I18NCat category_ = I18NCat::CATEGORY_COUNT; std::string(*translateCallback_)(std::string_view value) = nullptr; }; enum class FileChooserFileType { WAVE_FILE, }; class FileChooserChoice : public AbstractChoiceWithValueDisplay { public: FileChooserChoice(RequesterToken token, std::string *value, std::string_view title, BrowseFileType fileType, LayoutParams *layoutParams = nullptr); std::string ValueText() const override; Event OnChange; private: std::string *value_; }; class FolderChooserChoice : public AbstractChoiceWithValueDisplay { public: FolderChooserChoice(RequesterToken token, std::string *value, std::string_view title, LayoutParams *layoutParams = nullptr); std::string ValueText() const override; Event OnChange; private: std::string *value_; RequesterToken token_; }; } // namespace UI