您的位置:首页 > 编程语言 > Lua

LUA调试环境(三)

2020-04-29 18:28 1106 查看

LUA调试环境

  • MDI处理
  • 特殊变量内容展示

  • QQ 282397369
    天下文章一大抄,想在网上找到想要的资料,是得费一番功夫的。
    所幸还有技术基础,在明确需求的情况下,总算把目标实现了。

    快捷键处理

    在前两篇基础上,调试环境首先需要再解决的就是按键处理。
    原以为这个很简单,首先发现可直接处理Scintilla的消息。稍微深究一点就知道是WM_NOTIFY消息。查了一下,本功能需求中有用的消息类别处理如下:

    void __fastcall TCbwSynEditor::OnEditorMsg(Messages::TMessage & message) {
    if(GetTickCount() - FCreateMoment < 1000)
    return;
    SCNotification *pSCNotification = (SCNotification*)(message.LParam);
    if(pSCNotification->nmhdr.idFrom != SCINT_ID)
    return;
    int iValue;
    switch(pSCNotification->nmhdr.code) {
    case SCN_DOUBLECLICK:
    if(OnEditor_DblClick)
    OnEditor_DblClick(this);
    break;
    case SCN_UPDATEUI:
    if(OnEditor_Click)
    OnEditor_Click(this);
    if(FPaintBox_Assert)
    FPaintBox_Assert->Repaint();
    if(PageControl && PageControl->ActivePage)
    PageControl->ActivePage->Tag = GetCurrentLine();
    break;
    case SCI_SETMARGINSENSITIVEN:
    SendEditor(SCI_TOGGLEFOLD, CurrentLine, 0);
    break;
    case SCN_SAVEPOINTREACHED:   		// SCI_SETSAVEPOINT消息将会触发SCN_SAVEPOINTREACHED事件通知
    //		THelper::Debug::AddLog(L"Saved...", clBlue, true);
    FileModified = false;
    break;
    case SCN_SAVEPOINTLEFT: 			// 当文档状态变为modified时,将会触发SCN_SAVEPOINTLEFT事件通知
    //		THelper::Debug::AddLog(L"Changed...", clBlue, true);
    FileModified = true;
    break;
    case SCN_MARGINCLICK:
    iValue = SendEditor(SCI_LINEFROMPOSITION, pSCNotification->position);		// 点击位置行号
    if(pSCNotification->margin == 1) {
    SendEditor(SCI_TOGGLEFOLD, iValue);
    Application->ProcessMessages();
    FPaintBox_Assert->Invalidate();
    }
    break;
    case SCN_CHARADDED:
    THelper::Debug::AddLog(THelper::FormatString(L"%d", pSCNotification->ch), clBlue, true);
    break;
    case SCN_KEY:
    case SC_KEYMENU:
    THelper::Debug::AddLog(THelper::FormatString(L"%d", pSCNotification->ch), clBlue, true);
    break;
    default:
    break;
    }
    }

    当然,纯按键的只是支持SCN_CHARADDED,也就是可以编辑代码。
    这里发现一个问题:TAB键、上下左右键无效

    TAB键、方向键

    查资料发现,可以处理WM_GETDLGCODE消息解决

    void __fastcall TCbwSynEditor::OnDlgCode(Messages::TMessage & message) {
    message.Result = DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTTAB;  // 接受方向键和TAB键
    }

    多费了一点周折而已,还算顺利

    F8、F9

    但最终卡在F8、F9这两个键上,我的本意是F9可以运行到断点,F8可以单步运行。在上述基础上,无论怎样按F8、F9都不能触发任何事件,稍微多试一下,F1~F12都一样无效。
    我在主窗口下测试,都可以触发Form的OnKeyUp事件,其中可以处理F1~F12。
    那就只好从VCL的消息机制入手来研究怎样捕获这些消息。这肯定得看VCL中TApplication的消息循环过程中的ProcessMessage函数

    function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
    var
    Handled: Boolean;
    begin
    Result := False;
    if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
    begin
    Result := True;
    if Msg.Message <> WM_QUIT then
    begin
    Handled := False;
    if Assigned(FOnMessage) then
    FOnMessage(Msg, Handled);
    if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
    not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
    begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
    end;
    end
    else
    FTerminate := True;
    end;
    end;

    再看几个判断函数中,IsKeyMsg是所需要的,即可以在其中进行控制处理

    function TApplication.IsKeyMsg(var Msg: TMsg): Boolean;
    var
    Wnd: HWND;
    begin
    Result := False;
    with Msg do
    if (Message >= WM_KEYFIRST) and (Message <= WM_KEYLAST) then
    begin
    Wnd := GetCapture;
    if Wnd = 0 then
    begin
    Wnd := HWND;
    if (MainForm <> nil) and (Wnd = MainForm.ClientHandle) then
    Wnd := MainForm.Handle
    else
    begin
    // Find the nearest VCL component. Non-VCL windows wont know what
    // to do with CN_BASE offset messages anyway.
    // TOleControl.WndProc needs this for TranslateAccelerator
    while (FindControl(Wnd) = nil) and (Wnd <> 0) do
    Wnd := GetParent(Wnd);
    if Wnd = 0 then
    Wnd := HWND;
    end;
    if SendMessage(Wnd, CN_BASE + Message, WParam, LParam) <> 0 then
    Result := True;
    end
    else if (LongWord(GetWindowLong(Wnd, GWL_HINSTANCE)) = HInstance) then
    begin
    if SendMessage(Wnd, CN_BASE + Message, WParam, LParam) <> 0 then
    Result := True;
    end;
    end;
    end;

    看IsKeyMsg函数实现逻辑,我所关注的键盘信息应该到SendMessage(Wnd, CN_BASE + Message, WParam, LParam);再找相应的消息类型为CN_KEYDOWN,CN_KEYUP,CN_CHAR,CN_SYSKEYDOWN,CN_SYSCHAR,所以再跟一下CN_KEYUP消息处理

    procedure TWinControl.CNKeyDown(var Message: TWMKeyDown);
    var
    Mask: Integer;
    begin
    with Message do
    begin
    Result := 1;
    UpdateUIState(Message.CharCode);
    if IsMenuKey(Message) then
    Exit;
    if not(csDesigning in ComponentState) then
    begin
    if Perform(CM_CHILDKEY, CharCode, Integer(Self)) <> 0 then
    Exit;
    Mask := 0;
    case CharCode of
    VK_TAB:
    Mask := DLGC_WANTTAB;
    VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN:
    Mask := DLGC_WANTARROWS;
    VK_RETURN, VK_EXECUTE, VK_ESCAPE, VK_CANCEL:
    Mask := DLGC_WANTALLKEYS;
    end;
    if (Mask <> 0) and (Perform(CM_WANTSPECIALKEY, CharCode, 0) = 0) and
    (Perform(WM_GETDLGCODE, 0, 0) and Mask = 0) and
    (GetParentForm(Self).Perform(CM_DIALOGKEY, CharCode, KeyData) <> 0) then
    Exit;
    end;
    Result := 0;
    end;
    end;

    感觉这里的CM_CHILDKEY消息可以利用一下。试试能否捕获。在Scintilla控件里响应处理:

    void __fastcall TCbwSynEditor::OnChildKey(Messages::TMessage & message) {
    WORD Key = message.WParam;
    }

    结果OK了。
    那就加上F8、F9的响应,顺便参考Notepad++实现Alt+1的全部折叠功能

    void __fastcall TCbwSynEditor::OnChildKey(Messages::TMessage & message) {
    WORD Key = message.WParam;
    if( (Key == 120 || Key == 119) && FOnSpecialKey)
    FOnSpecialKey(message);
    if(Key == 49 && (GetKeyState(VK_MENU) < 0))  	// alt + 1: 全部折叠
    FoldAll();
    }

    MDI处理

    这个可以最简化的实现,采用PageControl作为标签,选中哪个标签就显示对应的文件内容。唯一需要处理的是记得上次显示的位置。

    void __fastcall TCbwSynEditor::OnCloseFile(TObject *Sender, int ATabIndex, bool &ACanClose) {
    UnicodeString fileName = PageControl->Pages[ATabIndex]->Caption.Trim();
    for(int i = 0; i < FFileNames->Count; ++i) {
    UnicodeString fn = FFileNames->Strings[i];
    if(THelper::File::ExtractPureFileName(fn) == fileName) {
    FFileNames->Delete(i);
    Clear();
    i = CAST_RANGE(i, 0, FFileNames->Count - 1);
    if(FFileNames->Count)
    LoadFromFile(FFileNames->Strings[i]);
    break;
    }
    }
    }
    
    void __fastcall TCbwSynEditor::OnSelectFile(TObject* Sender) {
    CBW_PREVENT_CHANGE_LOOP;
    int index = PageControl->ActivePageIndex;
    if(index == -1)
    return;
    UnicodeString fileName = PageControl->ActivePage->Hint;
    LoadFromFile(fileName, true);
    }

    这个没太多值得说的。

    特殊变量内容展示

    特殊变量,指图像与XML结构文本

    图像

    图像通过MAT处理,直接直出在Canvas上即可。

    void __fastcall TCbwSynEditor::OnPaintBox_Debug(TObject* Sender) {
    TCanvas * canvas = FPaintBox_Debug->Canvas;
    THelper::Graphics::FillCanvas(canvas, TColor(0xFFEFEF), TColor(0xFFEFEF));
    canvas->Rectangle(0, 0, FPaintBox_Debug->Width, FPaintBox_Debug->Height);
    
    if(FPaintBox_Debug->Hint == L"MAT") {
    CBW_CAST(TDrMat, drMat, FPaintBox_Debug->Tag);
    cv::Mat mat = drMat->mat;
    if(mat.empty())
    return;
    double height = std::min(mat.rows, FPaintBox_Debug->Height);
    double ratio = height / mat.rows;
    CvHelper::DrawToCanvas(canvas, drMat->mat, TRect(0, 0, mat.cols * ratio, mat.rows * ratio), DRGRAPH_FLAG_FLIP_Y);
    }
    }

    XML结构文本

    其实,XML与LUA都是文本,直接用Scintilla来表示再好不过。在上面MDI基础上,直接新开一个“调试窗口”即可。

    窗口句柄

    这个就一句话,如果想看效果,就直接把该窗口句柄置前。

    初步回顾一下,这个LUA调试环境已基本达到预期目标要求。等后续在使用过程中再逐步优化完善。

    DrGraph 原创文章 14获赞 3访问量 1378 关注 私信
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: