当用户按下 Enter 时如何控制 wxStyledTextCtrl 中的 LineIndentation
How to control LineIndentation in wxStyledTextCtrl when user presses Enter
当用户在 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 时,不会以这种方式发生,例如转到行首然后转到缩进级别。相反,光标 立即 转到 /follows 缩进级别。有没有一种方法可以使光标立即进入缩进级别,而无需最初进入零缩进级别。
顺便说一下,我尝试了EVT_STC_CHARADDED
,这似乎是ZeroBraneStudio中实现的方式,但是当按下回车键时evt.GetKeyCode()
returns一个奇怪的整数和evt.GetUnicodeKey
returns [=16=]
而且 EVT_STC_CHARADDED
事件被调用了两次(我猜是由于 CR+LF)。
顺便说一下,我在 Windows 10.
上使用 wxWidgets-3.1.0
如有任何想法,我们将不胜感激。
注意:下面的评论指出了这个答案的代码中的一个致命缺陷。像我在这里尝试做的那样调整 UpdateUI 事件处理程序中的光标位置是个坏主意。我发布了另一个答案,希望效果更好。
我不能保证这是最好的方法,但这是一种方法。首先,这需要在您的脚本中添加一个整数成员 class 作为一个标志,指示需要添加缩进。在下文中,我将其称为 'm_indentToAdd'.
要检测是否添加了一行,可以使用 wxEVT_STC_MODIFIED 事件。如果修改类型表明它是用户操作,已插入文本,并且已添加 1 行,则下一行将需要添加缩进。除了按下 enter 键外,这还会在粘贴包含行尾的单行时捕获。
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_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事件。下一个问题是在该事件处理程序中做什么。我已经修改了 here 中的代码以与 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));
}
}
}
这可能会更好,因为它在物理上计算了空格和制表符。
当用户在 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 时,不会以这种方式发生,例如转到行首然后转到缩进级别。相反,光标 立即 转到 /follows 缩进级别。有没有一种方法可以使光标立即进入缩进级别,而无需最初进入零缩进级别。
顺便说一下,我尝试了EVT_STC_CHARADDED
,这似乎是ZeroBraneStudio中实现的方式,但是当按下回车键时evt.GetKeyCode()
returns一个奇怪的整数和evt.GetUnicodeKey
returns [=16=]
而且 EVT_STC_CHARADDED
事件被调用了两次(我猜是由于 CR+LF)。
顺便说一下,我在 Windows 10.
上使用 wxWidgets-3.1.0如有任何想法,我们将不胜感激。
注意:下面的评论指出了这个答案的代码中的一个致命缺陷。像我在这里尝试做的那样调整 UpdateUI 事件处理程序中的光标位置是个坏主意。我发布了另一个答案,希望效果更好。
我不能保证这是最好的方法,但这是一种方法。首先,这需要在您的脚本中添加一个整数成员 class 作为一个标志,指示需要添加缩进。在下文中,我将其称为 'm_indentToAdd'.
要检测是否添加了一行,可以使用 wxEVT_STC_MODIFIED 事件。如果修改类型表明它是用户操作,已插入文本,并且已添加 1 行,则下一行将需要添加缩进。除了按下 enter 键外,这还会在粘贴包含行尾的单行时捕获。
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_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事件。下一个问题是在该事件处理程序中做什么。我已经修改了 here 中的代码以与 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));
}
}
}
这可能会更好,因为它在物理上计算了空格和制表符。