如何在用户按Enter键时控制wxStyledTextCtrl中的行缩进



当用户在wxStyledTextCtrl中按下Enter键时,光标似乎总是指向行的开头(零缩进),这很可能是预期的行为。

我希望能够用以下格式编写带有行缩进的脚本代码。

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返回,而且EVT_STC_CHARADDED事件被调用了两次(我想是由于CR+LF)。

顺便说一句,我在Windows 10上使用wxWidgets-3.1.0。

任何想法都将不胜感激。

注意:下面的注释指出了这个答案的代码中的一个致命缺陷。像我在这里尝试的那样调整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事件处理程序中将其使用来进行优化。

我们需要截取一个事件,并将前一行的缩进副本添加到新行。第一个问题是使用哪个事件。按下回车键时,将触发以下事件:

  • wxEVT_CHAR_OOK
  • wxEVT_KEY_DOWN
  • wxEVT_STC_MODIFIED-修改类型:0x00100000
  • wxEVT_STC_MODIFIED-修改类型:0x00000410
  • wxEVT_STC_MODIFIED-修改类型:0x00002011
  • wxEVT_STC_CHARADDED
  • wxEVT_STC_UPDATEUI
  • wxEVT_STC_涂漆
  • 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));
}
}
}

这可能会更好,因为它会对空格和制表符进行物理计数。

相关内容

  • 没有找到相关文章

最新更新