Как управлять LineIndentation в wxStyledTextCtrl, когда пользователь нажимает Enter

Когда пользователь нажимает Введите ключ в wxStyledTextCtrl, кажется, что курсор всегда идет в начало строки (нулевой отступ), что, скорее всего, является ожидаемым поведением.

Я хочу иметь возможность писать код Script в следующем формате с отступами строки.

for i=1,10 do --say there is no indentation
i=i+1 -- now there is indentation via tab key
-- pressing enter should proceed with this level of indentation
print(i) -- same level of indentation with the previous code line
end

Я использую следующий код C ++, чтобы иметь возможность контролировать отступы на самом базовом уровне.

void Script::OnKeyUp(wxKeyEvent& evt)
{
if ((evt.GetKeyCode() == WXK_RETURN || evt.GetKeyCode() == WXK_NUMPAD_ENTER)) {
long int col, line;
PositionToXY(GetInsertionPoint(), &col, &line);
int PreviousIndentation = GetLineIndentation(line-1);
SetLineIndentation(line, PreviousIndentation);
GotoPos(GetCurrentPos() + PreviousIndentation);
}
}

Приведенный выше код C ++ сохраняет уровень отступа, однако курсор сначала идет к началу строки, а затем к уровню отступа. При использовании других IDE это не происходит таким образом, как переход к началу строки, а затем к уровню отступа. Скорее курсор немедленно идет на / следует за уровнем отступа. Есть ли способ, чтобы курсор мог сразу перейти на уровень отступа, не переходя изначально на нулевой уровень отступа.

Кстати, я пытался EVT_STC_CHARADDEDчто похоже на способ, реализованный в ZeroBraneStudio, но когда нажата клавиша Enter evt.GetKeyCode() возвращает странное целое число и evt.GetUnicodeKey возвращается \0 и более того EVT_STC_CHARADDED событие вызывается дважды (наверное, из-за CR + LF).

Кстати, я использую wxWidgets-3.1.0 на Windows 10.

Любые идеи были бы хорошы.

1

Решение

Нам нужно перехватить событие и добавить копию отступа из предыдущей строки в новую строку. Первый вопрос — какое событие использовать. При нажатии клавиши ввода запускаются следующие события:

  • wxEVT_CHAR_HOOK
  • wxEVT_KEY_DOWN
  • wxEVT_STC_MODIFIED — тип модификации: 0x00100000
  • wxEVT_STC_MODIFIED — тип модификации: 0x00000410
  • wxEVT_STC_MODIFIED — тип модификации: 0x00002011
  • wxEVT_STC_CHARADDED
  • wxEVT_STC_UPDATEUI
  • wxEVT_STC_PAINTED
  • wxEVT_KEY_UP

С событиями char_hook и key_down ключ еще не был отправлен элементу управления, поэтому он не сможет предоставить необходимую информацию о положении. Элемент управления не должен быть изменен в событии stc_modified, поэтому мы не должны использовать эти события. К моменту события stc_painted курсор уже отрисован, поэтому он и событие key_up слишком поздние. И я узнал в другом ответе, что событие stc_updateui не будет работать.

Таким образом, в процессе исключения единственной возможностью является событие wxEVT_STC_CHARADDED. Следующий вопрос — что делать в этом обработчике событий. Я адаптировал код из Вот работать с wxStyledTextCtrl.

void Script::OnCharAdded(wxStyledTextEvent& event)
{
int new_line_key=(GetEOLMode()==wxSTC_EOL_CR)?13:10;

if ( event.GetKey() == new_line_key )
{
int cur_pos = GetCurrentPos();
int cur_line = LineFromPosition(cur_pos);

if ( cur_line > 0 )
{
wxString prev_line = GetLine(cur_line-1);
size_t prev_line_indent_chars(0);
for ( size_t i=0; i<prev_line.Length(); ++i )
{
wxUniChar cur_char=prev_line.GetChar(i);

if (cur_char==' ')
{
++prev_line_indent_chars;
}
else if (cur_char=='\t')
{
++prev_line_indent_chars;
}
else
{
break;
}
}

AddText(prev_line.Left(prev_line_indent_chars));
}
}
}

Это может быть лучше, так как он физически подсчитывает пробелы и вкладки.

1

Другие решения

Заметка: Комментарии ниже указывают на фатальный недостаток в коде для этого ответа. Корректировать положение курсора в обработчике события UpdateUI, как я пытался сделать здесь, — плохая идея. Я отправил другой ответ, который, надеюсь, работает лучше.


Я не могу гарантировать, что это лучший способ, но вот один из способов. Во-первых, это требует добавления целочисленного члена к вашему классу скрипта, чтобы он служил флагом, указывающим на необходимость добавления отступа. Далее я назвал его m_indentToAdd.

Чтобы обнаружить, что строка была добавлена, вы можете использовать событие wxEVT_STC_MODIFIED. Если тип модификации указывает, что это было действие пользователя, текст был вставлен, и что была добавлена ​​1 строка, то в следующей строке должен быть добавлен отступ. В дополнение к нажатию клавиши ввода, это будет срабатывать при вставке одной строки, включающей окончания строки.

void Script::OnModified(wxStyledTextEvent& event)
{
int mt = event.GetModificationType();

if(mt&wxSTC_MOD_INSERTTEXT && mt&wxSTC_PERFORMED_USER && event.GetLinesAdded()==1)
{
int cur_line = m_stc->LineFromPosition(event.GetPosition());
int cur_indent = m_stc->GetLineIndentation(cur_line);
m_indentToAdd=cur_indent;
}
}

Чтобы курсор не начинался с начала строки, а затем перемещался к отступу, вы можете обработать событие wxEVT_STC_UPDATEUI и сбросить там положение:

void Script::OnUpdateUI(wxStyledTextEvent& event)
{
if(m_indentToAdd)
{
int cur_pos = m_stc->GetCurrentPos();
int cur_line = m_stc->LineFromPosition(cur_pos);
m_stc->SetLineIndentation(cur_line, m_indentToAdd);
m_stc->GotoPos(cur_pos+m_indentToAdd);

m_indentToAdd=0;
}
}

Событие UpdateUI не предоставляет текущую позицию или строку, поэтому их необходимо пересчитать, прежде чем можно будет установить отступ. Я полагаю, что это можно оптимизировать, сохранив эти значения в обработчике события OnModified, а затем используя их в обработчике события UpdateUI.

2

По вопросам рекламы [email protected]