Почти достиг того что мне нужно, но не смог пока побороть Enter, который проходит через блокирующий обработчик и проскакивает в редактор. Т.е., допустим, выбрал я пункт в списке нажимаю Enter (Enter еренаправляется окно попап-списка), этот пункт вставляется вредактор, но происходит так, то пункт вставляется с переносом на новую строку, хотя отловил Enter в сабклассе перенаправил в список, блокировал его дальнейшую обработку в редакторе с помощью WindowNoNextProc, но нет, он все равно проскаивает в редактор, гадина такая!
Code: Select all
AkelPad.Include("MemHelp.js")
AkelPad.Include("log.js")
AkelPad.Include("timer.js")
AkelPad.Include("Coder.js") // для Stop- StartRedraw()
var oSys = AkelPad.SystemFunction()
var hMainWnd = AkelPad.GetMainWnd()
var hInstDLL = AkelPad.GetInstanceDll()
var sClass
var MessageLoopOwnFlag
var shtdTaskFlag
var oPopup = {hWnd:0, hLB:0, hEdit:0, aItems:[]}
// Тестовый запуск
if (!AkelPad.IsInclude())
ShowAutoCompletePopup(["Яблоко", "Груша", "Апельсин", "Банан", "Вишня", "Дыня", "Ежевика", "Жимолость", "Земляника", "Ирга"])
//var ticCount = oSys.Call("kernel32::GetTickCount")
function ShowAutoCompletePopup(aItems){
var hWndEdit
oPopup.aItems = aItems
oPopup.hEdit = AkelPad.GetEditWnd()
if (!oPopup.hEdit) return
sClass = "AkelPad::AutoCompletePopup::" + hInstDLL
if (hDlg = oSys.Call("User32::FindWindowExW", 0, 0, sClass, 0)){
//SetForegroundWindow(hDlg)
return
}
AkelPad.WindowRegisterClass(sClass)
return (ShowAutoCompletePopup = function (aItems){
hWndEdit = AkelPad.GetEditWnd()
var caretPos = GetCaretPos(oPopup.hEdit)
var nX = caretPos.X
var nY = caretPos.Y + AkelPad.SendMessage(oPopup.hEdit, 3188/*AEM_GETCHARSIZE*/, 0, 0)
oPopup.hWnd = oSys.Call("User32::CreateWindowExW",
0x00000080 | 0x08000000, // WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE (критично: не забирает фокус у редактора)
sClass, "",
//WS_POPUP | WS_VISIBLE | WS_THICKFRAME
0x80000000 | 0x10000000 | 0x00040000,
nX, nY, 220, 200,
hMainWnd, 0, hInstDLL, DialogCallback)
if (!oPopup.hWnd){
AkelPad.WindowUnregisterClass(sClass)
return
}
// Показываем без активации (SW_SHOWNOACTIVATE = 4) ~ но все равно активируется, кстати ~
//StopRedraw()
oSys.Call("User32::ShowWindow", oPopup.hWnd, 4)
//StartRedraw()
oSys.Call("User32::SetFocus", AkelPad.GetEditWnd()) // возвращаем фокус в окно редактора, чтобы не терять вомзможность ввода
// Поднимаем в Z-порядке, но НЕ активируем
//oSys.Call("User32::SetWindowPos", oPopup.hWnd, -2, 0, 0, 0, 0, 0x0001 | 0x0002 | 0x0010)
//oSys.Call("User32::SetFocus", AkelPad.GetEditWnd())
// Выделяем первый элемент по умолчанию (как в Coder)
//oSys.Call("User32::SetFocus", oPopup.hLB)
// Отложенная инициализация выделения первого пункта
//oSys.Call("User32::PostMessageW", oPopup.hWnd, 0x8000/*WM_APP*/, 0, 0)
if (!AkelPad.ScriptHandle(0, 13)){
MessageLoopOwnFlag = true
AkelPad.WindowGetMessage()
MessageLoopOwnFlag = false
AkelPad.WindowUnregisterClass(sClass)
} else {
if (!shtdTaskFlag) {
shutdownTasks.add(function (){
AkelPad.WindowUnregisterClass(sClass)
})
shtdTaskFlag = true
}
}
})(aItems)
var hWndEditSubClass
function EditCallback(hWnd, uMsg, wParam, lParam){
if (uMsg == 0x0202/*WM_LBUTTONUP*/){ // если был клик в произвольной области окна редактора
AkelPad.WindowNextProc(hWndEditSubClass, hWnd, uMsg, wParam, lParam)
ClosePopup() // то закрываем попап
} else if (wParam == 13/*VK_RETURN*/ || wParam == 27/*VK_ESCAPE*/ || wParam == 38/*VK_UP*/ || wParam == 40/*VK_DOWN*/){
//PrintLog('hWndEditSubClass = ' + hWndEditSubClass)
oSys.Call("User32::PostMessageW", oPopup.hLB, uMsg, wParam, lParam)// перенаправляем сообщение в попап-листбокс
//PrintLog('wParam = ' + wParam)
AkelPad.WindowNoNextProc(hWndEditSubClass) // Блокируем дальнейшую обработку
//oSys.Call("User32::PostMessageW", oPopup.hLB, 0x0100/*WM_KEYDOWN*/, 40/*VK_DOWN*/, 0)
//oSys.Call("User32::SetFocus", hWnd)
return 1 // обработано
}
return 0
}
var oDIS, sRect, lpRect, oRect // стек сохраняемых данных функции DialogCallback
function DialogCallback(hWnd, uMsg, wParam, lParam){
var WM_MOUSEACTIVATE = 33
var MA_NOACTIVATE = 3
var WM_NCACTIVATE = 0x86 // 134
//return (DialogCallback = function (hWnd, uMsg, wParam, lParam){
switch(uMsg){
/*switch (uMsg){
case 32: break
case 132: break
case 160: break
case 512: break
default:
PrintLog('uMsg = ' + uMsg + ' wParam = ' + wParam)
break
}*/
case 1: // WM_CREATE
oPopup.hLB = oSys.Call("User32::CreateWindowExW",
0x00000200, // WS_EX_CLIENTEDGE
"LISTBOX", "",
/*WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS*/
0x40000000 | 0x10000000 | 0x00200000 | 0x00000001 | 0x00000100 | 0x00001000/* | 0x0010 | 0x0040*/,
/*2, 2*/0, 0, 212, 192,
hWnd, 1001, hInstDLL, 0)
var hFont = AkelPad.SendMessage(oPopup.hEdit, 0x0031/*WM_GETFONT*/, 0, 0)
if (hFont) AkelPad.SendMessage(oPopup.hLB, 0x0030/*WM_SETFONT*/, hFont, true)
// Заполнение листбокса значениями
for (var i = 0; i < oPopup.aItems.length; i++)
AkelPad.SendMessage(oPopup.hLB, 0x0180/*LB_ADDSTRING*/, 0, oPopup.aItems[i])
// Выделяем первый элемент по умолчанию
// AkelPad.SendMessage(oPopup.hLB, 0x018E/*LB_SETCURSEL*/, 0, 0)
// oSys.Call("User32::InvalidateRect", oPopup.hLB, 0, true)
oSys.Call("User32::PostMessageW", oPopup.hLB, 0x0100/*WM_KEYDOWN*/, 40/*VK_DOWN*/, 0)
// Запускаем субклассирование редактора для того, чтобы перенаправлять VK_DOWN, VK_UP, VK_RETURN, VK_ESCAPE в попап
hWndEditSubClass = AkelPad.WindowSubClass(hWndEdit, EditCallback, 0x0100/*WM_KEYDOWN*/, 0x0101/*WM_KEYUP*/, 0x0202/*WM_LBUTTONUP*/) //
return 0
// case 0x8000: // WM_APP // выделение первого пункта в LB
// AkelPad.SendMessage(oPopup.hLB, 0x018E, 0, 0) // LB_SETCURSEL(0)
// oSys.Call("User32::InvalidateRect", oPopup.hLB, 0, true)
// return 0
case 5: // WM_SIZE
// Растягиваем ListBox при изменении размера попапа
var cx = lParam & 0xFFFF
var cy = (lParam >> 16) & 0xFFFF
if (oPopup.hLB)
oSys.Call("User32::MoveWindow", oPopup.hLB, 2, 2, cx - 4, cy - 4, true)
return 0
// case 0x002B: // WM_DRAWITEM
// //var lpDIS = lParam; // указатель на DRAWITEMSTRUCT
// PrintLog('WM_DRAWITEM')
// if (!oDIS) {
// oDIS = DRAWITEMSTRUCTwrp(lParam)
// sRect = makeStrBuff(8) //16/2 (sizeOf(RECT))
// lpRect = StrPtr(sRect)
// oRect = RECTwrp(lpRect)
// // PrintLog('Инициализация данных DialogCallback')
// } else {
// oDIS.pStructSet(lParam)
// }
// if (oDIS.CtlID() == 1001) {
// try {
// var hDC = oDIS.hDC() //AkelPad.MemRead(_PtrAdd(lpDIS, _X64?24:16), 8); // hDC (на x64 8 байт)
//
// var nCurSel = AkelPad.SendMessage(oPopup.hLB, 0x0188, 0, 0); // LB_GETCURSEL
// // Выбрать цвет фона и текста
// var bkColor, textColor;
// if ((oDIS.itemState() & 0x0001) || (oDIS.itemID() == nCurSel)) { // ODS_SELECTED
// bkColor = oSys.Call("User32::GetSysColor", 13); // COLOR_HIGHLIGHT
// textColor = oSys.Call("User32::GetSysColor", 14); // COLOR_HIGHLIGHTTEXT
// } else {
// bkColor = oSys.Call("User32::GetSysColor", 5); // COLOR_WINDOW
// textColor = oSys.Call("User32::GetSysColor", 8); // COLOR_WINDOWTEXT
// } //PrintLog('bkColor textColor: ' + bkColor + ' ' + textColor) // вывод: 16777215 0
//
// // Заливка фона
// var hBrush = oSys.Call("Gdi32::CreateSolidBrush", bkColor)//PrintLog('hBrush = ' + hBrush) // вывод - число (хэндл)
// var lprcItem = oDIS.rcItemPtr()
// var ret = oSys.Call("User32::FillRect", hDC, lprcItem, hBrush)
// //PrintLog('FillRect() => ' + ret) // вывод: 1
// oSys.Call("Gdi32::DeleteObject", hBrush);
//
// // Рисование текста
// /*ret = */oSys.Call("Gdi32::SetBkMode", hDC, 1);// TRANSPARENT
// //PrintLog('SetBkMode() => ' + ret) // вывод 2 либо 1
// oSys.Call("Gdi32::SetTextColor", hDC, textColor)
//
// with(oDIS.rcItem()){
// oRect.leftSet(left())
// oRect.topSet(top())
// oRect.rightSet(right())
// oRect.bottomSet(bottom())
// }
// var sText = GetItemText(oPopup.hLB, oDIS.itemID()) // Получение текста элемента
// /*ret = */oSys.Call("User32::DrawTextW", hDC, StrPtr(sText), -1, lpRect, 0x0000 | 0x0008 | 0x0010); // DT_SINGLELINE | DT_VCENTER | DT_LEFT
// //PrintLog('DrawTextW() => ' + ret) // вывод 17
//
// return 1; // обработано
// } catch(e) {
// PrintLog('Ошибка в WM_DRAWITEM' + e.message)
// return 0
// }
// }
// break;
case WM_MOUSEACTIVATE: //
// Возвращаем MA_NOACTIVATE (3) → клик по попапу не активирует его, фокус остаётся в редакторе
return MA_NOACTIVATE
case WM_NCACTIVATE: // предотвращает изменение внешнего вида окна (например, заголовка) при попытке активации.
return 1 //TRUE
// case 7:
// oSys.Call("User32::SetFocus", hWndEdit)
case 273: // WM_COMMAND
var nID = wParam & 0xFFFF
var nCode = (wParam >> 16) & 0xFFFF
//PrintLog('WM_COMMAND: ' + nID + ' ' + nCode + ' ' + wParam)
if (nID == 1001 && nCode == 2){ // LBN_DBLCLK WScript.Echo((1874).toString(16))
DoInsertAndClose()
return 0
}
return 0
case 0x0100: // WM_KEYDOWN
try {
switch (wParam){
case 13: // VK_RETURN
//oSys.Call("User32::SetFocus", oPopup.hWnd)
//setTimeout(DoInsertAndClose, 200) //выполняем DoInsertAndClose с отсрочкой 200 мс
DoInsertAndClose()
return 0
case 27: // VK_ESCAPE
ClosePopup()
return 0
case 38: // Up
var nCur = AkelPad.SendMessage(oPopup.hLB, 0x0188, 0, 0);
if (nCur > 0) {
AkelPad.SendMessage(oPopup.hLB, 0x018E, nCur - 1, 0);
// Принудительно перерисовать listbox, чтобы обновить выделение
oSys.Call("User32::InvalidateRect", oPopup.hLB, 0, true);
}
return 0;
case 40: // Down
var nCur = AkelPad.SendMessage(oPopup.hLB, 0x0188, 0, 0);
var nCount = AkelPad.SendMessage(oPopup.hLB, 0x018B, 0, 0);
if (nCur < nCount - 1) {
AkelPad.SendMessage(oPopup.hLB, 0x018E, nCur + 1, 0);
oSys.Call("User32::InvalidateRect", oPopup.hLB, 0, true);
}
return 0;
}
// Навигация и скроллинг (передаём в ListBox)
if (/*wParam == 38 || wParam == 40 || */wParam == 33 || wParam == 34 || wParam == 9){
oSys.Call("User32::SendMessageW", oPopup.hLB, uMsg, wParam, lParam)
return 0
}
} catch(e) {
PrintLog('Ошибка в WM_KEYDOWN: ' + e.message)
}
return 0
case 0x020A: // WM_MOUSEWHEEL
oSys.Call("User32::SendMessageW", oPopup.hLB, uMsg, wParam, lParam)
return 0
case 0x0202: // WM_LBUTTONUP
var hChild = oSys.Call("User32::ChildWindowFromPointEx", hWnd, lParam & 0xFFFF, (lParam >> 16) & 0xFFFF, 2)
if (hChild != oPopup.hLB && hChild != 0){
ClosePopup()
return 0
}
return 0
// case 0x0008: // WM_KILLFOCUS
// if (lParam != oPopup.hLB) ClosePopup()
// return 0
case 16: // WM_CLOSE
oSys.Call("User32::DestroyWindow", hWnd)
return 0
case 2: // WM_DESTROY
oPopup.hWnd = 0
oPopup.hLB = 0
if (MessageLoopOwnFlag) oSys.Call("User32::PostQuitMessage", 0)
return 0
}
return 0
//}(hWnd, uMsg, wParam, lParam)
}
function ListBoxCallback(hWnd, uMsg, wParam, lParam){
switch(uMsg){
case 0x02B: // WM_DRAWITEM
}
}
function GetItemText(hLB, nItemID){
var nLen = AkelPad.SendMessage(hLB, 0x18A/*LB_GETTEXTLEN*/, nItemID, 0)
var sText = makeStrBuff(nLen)
var lpText = AkelPad.MemStrPtr(sText)
AkelPad.SendMessage(hLB, 0x0189/*LB_GETTEXT*/, nItemID, lpText) // LB_GETTEXT
return sText
}
function DoInsertAndClose(){
var nSel = AkelPad.SendMessage(oPopup.hLB, 0x0188/*LB_GETCURSEL*/, 0, 0) // LB_GETCURSEL
if (nSel == -1) return
var nLen = AkelPad.SendMessage(oPopup.hLB, 0x18A/*LB_GETTEXTLEN*/, nSel, 0)
if (nLen <= 0 || nLen > 32768) return
var sText = makeStrBuff(nLen)
var lpText = AkelPad.MemStrPtr(sText)
AkelPad.SendMessage(oPopup.hLB, 0x0189/*LB_GETTEXT*/, nSel, lpText) // LB_GETTEXT
ClosePopup()
//oSys.Call("User32::SetFocus", oPopup.hEdit)
AkelPad.ReplaceSel(sText)
}
function ClosePopup(){
AkelPad.WindowUnsubClass(hWndEdit) // удалит самый последний субкласс, а именно тот, который был создан в DialogCallback
if (oPopup.hWnd && oSys.Call("User32::IsWindow", oPopup.hWnd))
oSys.Call("User32::PostMessageW", oPopup.hWnd, 16/*WM_CLOSE*/, 0, 0)
}
function ResetContent(){
AkelPad.SendMessage(oPopup.hLB, 0x184/*LB_RESETCONTENT*/, 0, 0)
}
}
//---Вспомогательные функции---//
function LoWord(nParam) {
return (nParam & 0xFFFF)
}
//WScript.Echo(HiWord(263145))
function HiWord(nParam) {
return (nParam >> 16) & 0xFFFF
}
function GetCaretPos(hEdit){
var sPointBuf = makeStrBuff(4)
var lpPoint = sPointBuf.StrPtr()
return (GetCaretPos = function(hEdit){
if (!hEdit) hEdit = AkelPad.GetEditWnd()
AkelPad.SendMessage(hEdit, 0x00D6/*EM_POSFROMCHAR*/, lpPoint, AkelPad.GetSelStart())
oSys.Call("User32::ClientToScreen", hEdit, lpPoint)
return {
X: AkelPad.MemRead(lpPoint, 3),
Y: AkelPad.MemRead(_PtrAdd(lpPoint, 4), 3)
}
})(hEdit)
}
function SetForegroundWindow(hWnd){
if (oSys.Call("User32::IsIconic", hWnd)) oSys.Call("User32::ShowWindow", hWnd, 9)
oSys.Call("User32::SetForegroundWindow", hWnd)
}
/*typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, *NPRECT, *LPRECT;*/
function RECTwrp(pStruct, oStruct, fullInit){
var aOffsets = (_X64)?[16,4,8,12]:[16,4,8,12]
var aDataFields = ['left', 3,
'top', 3,
'right', 3,
'bottom', 3]
return (RECTwrp = function (pStruct, oStruct, fullInit){
return makeStructWrapper(pStruct, oStruct, fullInit, aOffsets, aDataFields)
})(pStruct, oStruct, fullInit)
}
/*typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
ULONG_PTR itemData;
} DRAWITEMSTRUCT, *PDRAWITEMSTRUCT, *LPDRAWITEMSTRUCT;*/
function DRAWITEMSTRUCTwrp(pStruct, oStruct, fullInit){
var aOffsets = (_X64)?[64,4,8,12,16,24,32,40,56]:[48,4,8,12,16,20,24,28,44]
var aDataFields = ['CtlType', 3,
'CtlID', 3,
'itemID', 3,
'itemAction', 3,
'itemState', 3,
'hwndItem', 2,
'hDC', 2,
'rcItem', 6, RECTwrp,
'itemData', 2]
return (DRAWITEMSTRUCTwrp = function (pStruct, oStruct, fullInit){
return makeStructWrapper(pStruct, oStruct, fullInit, aOffsets, aDataFields)
})(pStruct, oStruct, fullInit)
}