diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp --- a/lldb/source/Core/IOHandlerCursesGUI.cpp +++ b/lldb/source/Core/IOHandlerCursesGUI.cpp @@ -1908,6 +1908,144 @@ SelectionType m_selection_type; }; +class EnvironmentVariableNameFieldDelegate : public TextFieldDelegate { +public: + EnvironmentVariableNameFieldDelegate(const char *label, const char *content) + : TextFieldDelegate(label, content, true) {} + + // Environment variable names can't contain an equal sign. + bool IsAcceptableChar(int key) override { + return TextFieldDelegate::IsAcceptableChar(key) && key != '='; + } + + const std::string &GetName() { return m_content; } +}; + +class EnvironmentVariableFieldDelegate : public FieldDelegate { +public: + EnvironmentVariableFieldDelegate() + : m_name_field(EnvironmentVariableNameFieldDelegate("Name", "")), + m_value_field(TextFieldDelegate("Value", "", /*required=*/false)), + m_selection_type(SelectionType::Name) {} + + // Signify which element is selected. The variable name field or its value + // field. + enum class SelectionType { Name, Value }; + + // An environment variable field is drawn as two text fields with a right + // arrow in between. The first text field stores the name of the variable and + // the second stores the value if the variable. + // + // __[Name]____________ __[Value]___________ + // | | > | | + // |__________________| |__________________| + // - Error message if it exists. + + // The environment variable field has a height that is equal to the maximum + // height between the name and value fields. + int FieldDelegateGetHeight() override { + return std::max(m_name_field.FieldDelegateGetHeight(), + m_value_field.FieldDelegateGetHeight()); + } + + void DrawArrow(SubPad &surface) { + surface.MoveCursor(0, 1); + surface.PutChar(ACS_RARROW); + } + + void FieldDelegateDraw(SubPad &surface, bool is_selected) override { + Rect bounds = surface.GetFrame(); + Rect name_field_bounds, arrow_and_value_field_bounds; + bounds.VerticalSplit(bounds.size.width / 2, name_field_bounds, + arrow_and_value_field_bounds); + Rect arrow_bounds, value_field_bounds; + arrow_and_value_field_bounds.VerticalSplit(1, arrow_bounds, + value_field_bounds); + + SubPad name_field_surface = SubPad(surface, name_field_bounds); + SubPad arrow_surface = SubPad(surface, arrow_bounds); + SubPad value_field_surface = SubPad(surface, value_field_bounds); + + bool name_is_selected = + m_selection_type == SelectionType::Name && is_selected; + m_name_field.FieldDelegateDraw(name_field_surface, name_is_selected); + DrawArrow(arrow_surface); + bool value_is_selected = + m_selection_type == SelectionType::Value && is_selected; + m_value_field.FieldDelegateDraw(value_field_surface, value_is_selected); + } + + HandleCharResult SelectNext(int key) { + if (FieldDelegateOnLastOrOnlyElement()) + return eKeyNotHandled; + + m_selection_type = SelectionType::Value; + m_name_field.FieldDelegateExitCallback(); + return eKeyHandled; + } + + HandleCharResult SelectPrevious(int key) { + if (FieldDelegateOnFirstOrOnlyElement()) + return eKeyNotHandled; + + m_selection_type = SelectionType::Name; + m_value_field.FieldDelegateExitCallback(); + return eKeyHandled; + } + + HandleCharResult FieldDelegateHandleChar(int key) override { + switch (key) { + case '\t': + SelectNext(key); + return eKeyHandled; + case KEY_SHIFT_TAB: + SelectPrevious(key); + return eKeyHandled; + default: + break; + } + + // If the key wasn't handled, pass the key to the selected field. + if (m_selection_type == SelectionType::Name) + return m_name_field.FieldDelegateHandleChar(key); + else + return m_value_field.FieldDelegateHandleChar(key); + + return eKeyNotHandled; + } + + bool FieldDelegateOnFirstOrOnlyElement() override { + return m_selection_type == SelectionType::Name; + } + + bool FieldDelegateOnLastOrOnlyElement() override { + return m_selection_type == SelectionType::Value; + } + + void FieldDelegateSelectFirstElement() override { + m_selection_type = SelectionType::Name; + } + + void FieldDelegateSelectLastElement() override { + m_selection_type = SelectionType::Value; + } + + bool FieldDelegateHasError() override { + return m_name_field.FieldDelegateHasError() || + m_value_field.FieldDelegateHasError(); + } + + const std::string &GetName() { return m_name_field.GetName(); } + + const std::string &GetValue() { return m_value_field.GetText(); } + +protected: + EnvironmentVariableNameFieldDelegate m_name_field; + TextFieldDelegate m_value_field; + // See SelectionType class enum. + SelectionType m_selection_type; +}; + class FormAction { public: FormAction(const char *label, std::function action) @@ -2059,6 +2197,13 @@ return delegate; } + EnvironmentVariableFieldDelegate *AddEnvironmentVariableField() { + EnvironmentVariableFieldDelegate *delegate = + new EnvironmentVariableFieldDelegate(); + m_fields.push_back(FieldDelegateUP(delegate)); + return delegate; + } + // Factory methods for adding actions. void AddAction(const char *label, std::function action) {