当用户按Enter

时间:2017-02-15 06:09:45

标签: c++ wxwidgets scintilla 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 \0以及EVT_STC_CHARADDED事件被调用两次(我猜是因为CR + LF)。

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

任何想法都会受到赞赏。

2 个答案:

答案 0 :(得分:2)

注意:下面的评论指出了此答案的代码中的致命缺陷。像我试图在这里做的那样调整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事件处理程序中使用它们来优化。

答案 1 :(得分:1)

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

  • wxEVT_CHAR_HOOK
  • wxEVT_KEY_DOWN
  • wxEVT_STC_MODIFIED - ModificationType:0x00100000
  • wxEVT_STC_MODIFIED - ModificationType:0x00000410
  • wxEVT_STC_MODIFIED - ModificationType: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));
        }
    }
}

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