Antonshka Опубликовано 3 января, 2023 Поделиться Опубликовано 3 января, 2023 (изменено) Привет, с наступившим Новый Годом🌲! Хорошо его провести. Кто-нибудь игрался с самодельной таблицей акселераторов? Как известно, стандартная WinAPI таблица акселераторов поддерживает только одну нажатую клавишу, совместно с SHIFT, CONTROL, или ALT. Например, позволено делать только так, - "SHIFT + ALT + M", или "ALT + G". Но что если нужно так, "SHIFT + ALT + M + A + T"? У меня дело дошло до горячих клавиш для пунктов меню. Текст то выводится, а про саму реализацию клавиш я и забыл. Сейчас думаю как реализовать самодельную таблицу. В основном цикле сообщений будет своя функция translateAccelerator Спойлер while ((resultOfGetMessage = GetMessageW(&msg, NULL, 0, 0)) != 0) { if (resultOfGetMessage == -1) { break; } else { translateAccelerator(&msg); TranslateMessage(&msg); DispatchMessageW(&msg); } } Далее, в этой функции (translateAccelerator), нужно сделать обход по всем виджетам. Для тех виджетов, чей список горячих клавиш непустой, сделать проверку, на совпадение. Список горячих клавиш думаю будет таким - Спойлер struct Keystroke { std::array<BYTE> hotkeys; CustomAcceleratorsIDs id{ 0 } }; std::vector<Keystroke*> Keystrokes; //Это будет полем класса, у каждого виждета свое. Если проверка на совпадение покажет, что такие клавиши для этого виджета установленны, то будет послано сообщение, этому виджету - Спойлер constexpr DWORD WM_USERAcceleratorMessage = WM_APP + 1; //Допустим это будет тип сообщения //a это, существующие различные ID enum class CustomAcceleratorsIDs : INT { CreateFile, OpenFile }; BOOL isSequenceFound{ FALSE }; //Допустим проверка на совпадение показала TRUE isSequenceFound = TRUE; Keystroke* foundedKeytroke = Keystrokes.at(индекс, при котором найдено совпадение клавиш) if (isSequenceFound){ PostMessageW(hWnd, WM_USERAcceleratorMessage, (WPARAM)foundedKeytroke, 0); //hWnd - это хендл окна у которого нашлось совпадение клавиш } //Далее, в своей оконой процедуре, это окно проверит сообщение switch (msg){ case WM_USERAcceleratorMessage: return wmAcceleratorMessage(wParam); } LRESULT wmAcceleratorMessage(WPARAM wParam){ Keystroke* foundedKeytroke = reinterpret_cast<Keystroke*>(wParam) switch (foundedKeytroke.id){ case CustomAcceleratorsIDs::CreateFile: return createFile(); case CustomAcceleratorsIDs::OpenFile: return openFile(); } } Осталось придумать как эффективнее проверять клавиши. Как вообще отслеживать последовательность нажатых клавиш. Изменено 3 января, 2023 пользователем Antonshka Ссылка на комментарий Поделиться на другие сайты Поделиться
Antonshka Опубликовано 4 января, 2023 Автор Поделиться Опубликовано 4 января, 2023 Не знаю, хорошо ли это, писать сейчас на форуме. Ведь форум сейчас на компьютере у @Xipho, плюс никто не пишет ничего чего-то, плюс форум как я понял будет скоро закрыт. Однако, напишу все же, раз тему завел. Да простят меня все. Я лишь хотел сказать, что я закончил с этой таблицей акселератора. И для тех кому интересно, реализовал я ее так. Это пример того как создается главное окно, создается меню, назначаются горячие клавиши. Спойлер constexpr UINT ACCELERATOR_OpenProject = 0; constexpr UINT ACCELERATOR_OpenFolder = 1; constexpr UINT ACCELERATOR_Delete = 2; int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { WGW::GUI::initializeWGWLibrary(hInstance); WGW::Window* mainWindow = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 0, 0, 900, 900); mainWindow->menu.enabled = TRUE; WGW::PopupMenu* popupMenuFile = new WGW::PopupMenu(); WGW::ItemOfMainMenu* fileItem = mainWindow->menu.addItem(WGW::EnabledState::Enabled, L"File", nullptr, popupMenuFile); WGW::ItemOfMainMenu* editItem = mainWindow->menu.addItem(WGW::EnabledState::Enabled, L"Edit", nullptr, nullptr); WGW::PopupMenu* popupMenuEdit = new WGW::PopupMenu(); editItem->setPopupMenu(popupMenuEdit); WGW::ItemOfPopupMenu* openProjectItem = popupMenuFile->addItem(WGW::EnabledState::Enabled, WGW::TypeOfItemOfMenu::Default, WGW::CheckableState::Uncheckable, WGW::CheckedState::Unchecked, WGW::CheckMarkType::Flag, L"OpenProject", L"Ctrl + G", nullptr, nullptr, WGW::SettingsButtonState::Disabled, nullptr); WGW::ItemOfPopupMenu* openFolderItem = popupMenuFile->addItem(WGW::EnabledState::Enabled, WGW::TypeOfItemOfMenu::Default, WGW::CheckableState::Uncheckable, WGW::CheckedState::Unchecked, WGW::CheckMarkType::Flag, L"OpenFolder", L"Ctrl + Shift + K", nullptr, nullptr, WGW::SettingsButtonState::Disabled, nullptr); WGW::ItemOfPopupMenu* deleteItem = popupMenuEdit->addItem(WGW::EnabledState::Enabled, WGW::TypeOfItemOfMenu::Default, WGW::CheckableState::Uncheckable, WGW::CheckedState::Unchecked, WGW::CheckMarkType::Flag, L"Delete", L"Ctrl + D", nullptr, nullptr, WGW::SettingsButtonState::Disabled, nullptr); mainWindow->keystroke->addKeystrokeEntry(ACCELERATOR_OpenProject, FALSE, VK_CONTROL, 0x47); mainWindow->keystroke->addKeystrokeEntry(ACCELERATOR_OpenFolder, FALSE, VK_CONTROL, VK_SHIFT, 0x4B); mainWindow->keystroke->addKeystrokeEntry(ACCELERATOR_Delete, FALSE, VK_CONTROL, 0x44); //FALSE значит NoRepeat mainWindow->runMessageCycle(); return 0; } Это главный messageLoop Спойлер while ((resultOfGetMessage = GetMessageW(&msg, NULL, 0, 0)) != 0) { if (resultOfGetMessage == -1) { break; } else { if (!translateAccelerator(&msg)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } } Это translateAccelerator Спойлер BOOL Window::translateAccelerator(MSG* msg) { BOOL isProcessed{ FALSE }; if (msg->message == WM_KEYDOWN) { Keystroke::determinePressedKeys(); for (auto& window : allCreatedLibWindowObjects) { //allCreatedLibWindowObjects = inline static std::unordered_map<size_t, Window*> allCreatedLibWindowObjects; for (auto& entry : window.second->keystroke->entries) { if (entry->isRepeated || !entry->isNeedReset) { if (entry->isKeysMatches(Keystroke::pressedKeys)) { entry->isNeedReset = TRUE; PostMessageW(window.second->hWnd, WM_USERAcceleratorEvent, entry->commandID, 0); isProcessed = TRUE; } } } } } else if (msg->message == WM_KEYUP) { Keystroke::determinePressedKeys(); if (!Keystroke::pressedKeys.size()) { for (auto& window : allCreatedLibWindowObjects) { for (auto& entry : window.second->keystroke->entries) { entry->isNeedReset = FALSE; } } } else { for (auto& window : allCreatedLibWindowObjects) { for (auto& entry : window.second->keystroke->entries) { if (entry->isNeedReset) { if (!entry->isKeysMatches(Keystroke::pressedKeys)) { entry->isNeedReset = FALSE; } } } } } } return isProcessed; } А это классы для работы самодельного аккселератора. Кое-какой функционал будет еще добавлен для них. Keystroke.h Спойлер namespace WGW { class Keystroke; class KeystrokeEntry { friend class Keystroke; friend class Window; protected: KeystrokeEntry() = default; KeystrokeEntry(CONST KeystrokeEntry&) = delete; KeystrokeEntry& operator=(CONST KeystrokeEntry&) = delete; ~KeystrokeEntry() = default; protected: BOOL isKeysMatches(std::vector<UINT>& pressedKeys); protected: UINT commandID{ 0 }; BOOL isNeedReset{ FALSE }; BOOL isRepeated{ FALSE }; std::vector<UINT> keys; }; //------------------------------------------------------------------------------------------- class Keystroke { friend class Window; protected: Keystroke(Window* windowOwner); Keystroke(CONST Keystroke&) = delete; Keystroke& operator=(CONST Keystroke&) = delete; ~Keystroke(); public: BOOL addKeystrokeEntry(UINT commandID, BOOL isRepeated, UINT key1, UINT key2 = 0, UINT key3 = 0, UINT key4 = 0, UINT key5 = 0); BOOL deleteKeystrokeEntry(UINT commandID); protected: static VOID determinePressedKeys(); protected: inline static std::vector<Keystroke*> allCreatedLibKeystrokeObjects; protected: std::vector<KeystrokeEntry*> entries; inline static BYTE keyboardState[256]; inline static std::vector<UINT> pressedKeys; Window* windowOwner{ nullptr }; }; } И Keystroke.cpp Спойлер BOOL KeystrokeEntry::isKeysMatches(std::vector<UINT>& pressedKeys) { BOOL isKeysMathes{ FALSE }; for (auto& key : keys) { isKeysMathes = FALSE; for (auto& pressedKey : pressedKeys) { if (key == pressedKey) { isKeysMathes = TRUE; break; } } if (!isKeysMathes) { break; } } return isKeysMathes; } Keystroke::Keystroke(Window* windowOwner) : windowOwner(windowOwner) { allCreatedLibKeystrokeObjects.push_back(this); } Keystroke::~Keystroke() { if (!GUI::isMessageCycleTerminated) { std::erase(allCreatedLibKeystrokeObjects, this); } for (auto& entry : entries) { delete entry; } } BOOL Keystroke::addKeystrokeEntry(UINT commandID, BOOL isRepeated, UINT key1, UINT key2, UINT key3, UINT key4, UINT key5) { BOOL isCommandIDExist{ FALSE }; BOOL isKeystrokeEntryAdded{ FALSE }; for (auto& entry : entries) { if (entry->commandID == commandID) { isCommandIDExist = TRUE; break; } } if (!isCommandIDExist) { KeystrokeEntry* entry = new KeystrokeEntry(); entry->commandID = commandID; entry->isRepeated = isRepeated; entry->keys.push_back(key1); if (key2) { entry->keys.push_back(key2); } if (key3) { entry->keys.push_back(key3); } if (key4) { entry->keys.push_back(key4); } if (key5) { entry->keys.push_back(key5); } entries.push_back(entry); isKeystrokeEntryAdded = TRUE; } return isKeystrokeEntryAdded; } BOOL Keystroke::deleteKeystrokeEntry(UINT commandID) { BOOL isKeystrokeEntryDeleted{ FALSE }; KeystrokeEntry* entryToDelete{ nullptr }; for (auto& entry : entries) { if (entry->commandID == commandID) { entryToDelete = entry; break; } } if (entryToDelete) { std::erase(entries, entryToDelete); delete entryToDelete; isKeystrokeEntryDeleted = TRUE; } return isKeystrokeEntryDeleted; } VOID Keystroke::determinePressedKeys() { pressedKeys.clear(); BOOL result = GetKeyboardState(keyboardState); if (result) { for (UINT i = 0; i < 256; i++) { if (i != VK_LSHIFT && i != VK_RSHIFT && i != VK_LCONTROL && i != VK_RCONTROL && i != VK_LMENU && i != VK_RMENU) { if (keyboardState[i] & 0x80) { pressedKeys.push_back(i); } } } } } } 2 Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения