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调试环境已基本达到预期目标要求。等后续在使用过程中再逐步优化完善。