559 lines
15 KiB
C++
559 lines
15 KiB
C++
#include "APP/APP_UIWindow.h"
|
|
|
|
#include "APP/APP_UIWindow_Private.h"
|
|
#include <QtCore/QByteArray>
|
|
#include <QtCore/QSignalBlocker>
|
|
#include <QtWidgets/QComboBox>
|
|
#include <QtWidgets/QLineEdit>
|
|
#include <QtWidgets/QMenu>
|
|
#include <QtWidgets/QPushButton>
|
|
#include <QtWidgets/QTableWidget>
|
|
#include <Windows.h>
|
|
|
|
namespace APP {
|
|
|
|
namespace
|
|
{
|
|
|
|
bool App_Func_IsModifierRecordToken(const QString& Token)
|
|
{
|
|
return (Token == QStringLiteral("Ctrl")) ||
|
|
(Token == QStringLiteral("Shift")) ||
|
|
(Token == QStringLiteral("Alt")) ||
|
|
(Token == QStringLiteral("Win"));
|
|
}
|
|
|
|
QStringList App_Func_GetOrderedRecordedModifierTokens(const QSet<QString>& PressedKeySet)
|
|
{
|
|
const QStringList ModifierOrder = {
|
|
QStringLiteral("Ctrl"),
|
|
QStringLiteral("Shift"),
|
|
QStringLiteral("Alt"),
|
|
QStringLiteral("Win")
|
|
};
|
|
|
|
QStringList Result;
|
|
for (const QString& ModifierToken : ModifierOrder)
|
|
{
|
|
if (PressedKeySet.contains(ModifierToken))
|
|
{
|
|
Result.append(ModifierToken);
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
QString App_Func_GetRecordedCombinationText(
|
|
const QSet<QString>& PressedKeySet,
|
|
const QString& TriggerToken)
|
|
{
|
|
QStringList TokenList = App_Func_GetOrderedRecordedModifierTokens(PressedKeySet);
|
|
if (!TriggerToken.isEmpty() && !App_Func_IsModifierRecordToken(TriggerToken))
|
|
{
|
|
TokenList.append(TriggerToken);
|
|
}
|
|
return TokenList.join(QStringLiteral("+"));
|
|
}
|
|
|
|
bool App_Func_TryGetRawKeyboard(void* p_Message, RAWKEYBOARD* p_Keyboard)
|
|
{
|
|
if ((p_Message == nullptr) || (p_Keyboard == nullptr))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
MSG* p_Msg = reinterpret_cast<MSG*>(p_Message);
|
|
if (p_Msg->message != WM_INPUT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UINT NeedSize = 0;
|
|
GetRawInputData(
|
|
reinterpret_cast<HRAWINPUT>(p_Msg->lParam),
|
|
RID_INPUT,
|
|
nullptr,
|
|
&NeedSize,
|
|
sizeof(RAWINPUTHEADER));
|
|
if (NeedSize == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QByteArray Buffer(static_cast<int>(NeedSize), 0);
|
|
if (GetRawInputData(
|
|
reinterpret_cast<HRAWINPUT>(p_Msg->lParam),
|
|
RID_INPUT,
|
|
Buffer.data(),
|
|
&NeedSize,
|
|
sizeof(RAWINPUTHEADER)) == static_cast<UINT>(-1))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const RAWINPUT* p_Input = reinterpret_cast<const RAWINPUT*>(Buffer.constData());
|
|
if (p_Input->header.dwType != RIM_TYPEKEYBOARD)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
*p_Keyboard = p_Input->data.keyboard;
|
|
return true;
|
|
}
|
|
|
|
QString App_Func_GetRecordedKeyToken(const RAWKEYBOARD& Keyboard)
|
|
{
|
|
const bool IsE0 = (Keyboard.Flags & RI_KEY_E0) != 0;
|
|
const USHORT VirtualKey = Keyboard.VKey;
|
|
|
|
if ((VirtualKey >= 'A') && (VirtualKey <= 'Z'))
|
|
{
|
|
return QString(QChar(static_cast<char16_t>(VirtualKey)));
|
|
}
|
|
if ((VirtualKey >= '0') && (VirtualKey <= '9'))
|
|
{
|
|
return QString(QChar(static_cast<char16_t>(VirtualKey)));
|
|
}
|
|
if ((VirtualKey >= VK_F1) && (VirtualKey <= VK_F24))
|
|
{
|
|
return QStringLiteral("F%1").arg(VirtualKey - VK_F1 + 1);
|
|
}
|
|
|
|
switch (VirtualKey)
|
|
{
|
|
case VK_CONTROL:
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
return QStringLiteral("Ctrl");
|
|
|
|
case VK_SHIFT:
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
return QStringLiteral("Shift");
|
|
|
|
case VK_MENU:
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
return QStringLiteral("Alt");
|
|
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
return QStringLiteral("Win");
|
|
|
|
case VK_RETURN:
|
|
return IsE0 ? QStringLiteral("NumEnter") : QStringLiteral("Enter");
|
|
|
|
case VK_SPACE:
|
|
return QStringLiteral("Space");
|
|
|
|
case VK_TAB:
|
|
return QStringLiteral("Tab");
|
|
|
|
case VK_ESCAPE:
|
|
return QStringLiteral("Esc");
|
|
|
|
case VK_BACK:
|
|
return QStringLiteral("Backspace");
|
|
|
|
case VK_DELETE:
|
|
return QStringLiteral("Delete");
|
|
|
|
case VK_INSERT:
|
|
return QStringLiteral("Insert");
|
|
|
|
case VK_HOME:
|
|
return QStringLiteral("Home");
|
|
|
|
case VK_END:
|
|
return QStringLiteral("End");
|
|
|
|
case VK_PRIOR:
|
|
return QStringLiteral("PageUp");
|
|
|
|
case VK_NEXT:
|
|
return QStringLiteral("PageDown");
|
|
|
|
case VK_LEFT:
|
|
return QStringLiteral("Left");
|
|
|
|
case VK_RIGHT:
|
|
return QStringLiteral("Right");
|
|
|
|
case VK_UP:
|
|
return QStringLiteral("Up");
|
|
|
|
case VK_DOWN:
|
|
return QStringLiteral("Down");
|
|
|
|
case VK_CAPITAL:
|
|
return QStringLiteral("CapsLock");
|
|
|
|
case VK_SNAPSHOT:
|
|
return QStringLiteral("PrintScreen");
|
|
|
|
case VK_SCROLL:
|
|
return QStringLiteral("ScrollLock");
|
|
|
|
case VK_PAUSE:
|
|
return QStringLiteral("Pause");
|
|
|
|
case VK_NUMPAD0:
|
|
return QStringLiteral("Num0");
|
|
|
|
case VK_NUMPAD1:
|
|
return QStringLiteral("Num1");
|
|
|
|
case VK_NUMPAD2:
|
|
return QStringLiteral("Num2");
|
|
|
|
case VK_NUMPAD3:
|
|
return QStringLiteral("Num3");
|
|
|
|
case VK_NUMPAD4:
|
|
return QStringLiteral("Num4");
|
|
|
|
case VK_NUMPAD5:
|
|
return QStringLiteral("Num5");
|
|
|
|
case VK_NUMPAD6:
|
|
return QStringLiteral("Num6");
|
|
|
|
case VK_NUMPAD7:
|
|
return QStringLiteral("Num7");
|
|
|
|
case VK_NUMPAD8:
|
|
return QStringLiteral("Num8");
|
|
|
|
case VK_NUMPAD9:
|
|
return QStringLiteral("Num9");
|
|
|
|
case VK_DIVIDE:
|
|
return QStringLiteral("Num/");
|
|
|
|
case VK_MULTIPLY:
|
|
return QStringLiteral("Num*");
|
|
|
|
case VK_SUBTRACT:
|
|
return QStringLiteral("Num-");
|
|
|
|
case VK_ADD:
|
|
return QStringLiteral("Num+");
|
|
|
|
case VK_DECIMAL:
|
|
return QStringLiteral("Num.");
|
|
|
|
case VK_OEM_COMMA:
|
|
return QStringLiteral("Comma");
|
|
|
|
case VK_OEM_PERIOD:
|
|
return QStringLiteral("Period");
|
|
|
|
case VK_OEM_1:
|
|
return QStringLiteral("Semicolon");
|
|
|
|
case VK_OEM_2:
|
|
return QStringLiteral("Slash");
|
|
|
|
case VK_OEM_3:
|
|
return QStringLiteral("Grave");
|
|
|
|
case VK_OEM_4:
|
|
return QStringLiteral("LeftBracket");
|
|
|
|
case VK_OEM_5:
|
|
return QStringLiteral("Backslash");
|
|
|
|
case VK_OEM_6:
|
|
return QStringLiteral("RightBracket");
|
|
|
|
case VK_OEM_7:
|
|
return QStringLiteral("Quote");
|
|
|
|
case VK_OEM_MINUS:
|
|
return QStringLiteral("Minus");
|
|
|
|
case VK_OEM_PLUS:
|
|
return QStringLiteral("Equal");
|
|
|
|
default:
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void App_UIWindow::App_Func_StartSequenceRecording()
|
|
{
|
|
const Lgc_FunctionFeature_Definition Feature =
|
|
Lgc_FunctionButton_GetFeature(appLgcState.FunctionButtonConfig, appSelectedFeatureId);
|
|
if (Feature.Id <= 0 || !App_Func_IsKeyRecordFeatureType(Feature.Type))
|
|
{
|
|
return;
|
|
}
|
|
|
|
appIsSequenceRecording = true;
|
|
appSequenceRecordingPressedKeySet.clear();
|
|
appLgcState.IsFunctionSequenceRecording = true;
|
|
{
|
|
QSignalBlocker Blocker(appFeatureSequenceEdit);
|
|
appFeatureSequenceEdit->setText(QString());
|
|
}
|
|
App_Func_SaveFeatureFromUi();
|
|
appLgcState.TextFunctionStatus =
|
|
Feature.Type == Lgc_FunctionFeature_Type::KeySequence
|
|
? QStringLiteral("已开始录入快捷键序列,接下来按下任意键盘按键即可按组追加。")
|
|
: QStringLiteral("已开始录入快捷键,请按下目标组合键。");
|
|
App_Func_UpdateSequenceRecordingUi();
|
|
if (appFeatureSequenceEdit != nullptr)
|
|
{
|
|
appFeatureSequenceEdit->setFocus(Qt::OtherFocusReason);
|
|
}
|
|
App_Func_RefreshUi();
|
|
}
|
|
|
|
void App_UIWindow::App_Func_StopSequenceRecording()
|
|
{
|
|
appIsSequenceRecording = false;
|
|
appSequenceRecordingPressedKeySet.clear();
|
|
appLgcState.IsFunctionSequenceRecording = false;
|
|
appLgcState.TextFunctionStatus = QStringLiteral("已退出录入,当前功能键配置已保存。");
|
|
App_Func_UpdateSequenceRecordingUi();
|
|
App_Func_RefreshUi();
|
|
}
|
|
|
|
void App_UIWindow::App_Func_UpdateSequenceRecordingUi()
|
|
{
|
|
const Lgc_FunctionFeature_Definition Feature =
|
|
Lgc_FunctionButton_GetFeature(appLgcState.FunctionButtonConfig, appSelectedFeatureId);
|
|
const bool HasFeature = Feature.Id > 0;
|
|
const bool CanRecord =
|
|
HasFeature && App_Func_IsKeyRecordFeatureType(Feature.Type);
|
|
const bool IsLocked = appIsSequenceRecording;
|
|
|
|
if (appFeatureTable != nullptr)
|
|
{
|
|
appFeatureTable->setEnabled(!IsLocked);
|
|
}
|
|
if (appFeatureAddButton != nullptr)
|
|
{
|
|
appFeatureAddButton->setEnabled(!IsLocked);
|
|
}
|
|
if (appFeatureDeleteButton != nullptr)
|
|
{
|
|
appFeatureDeleteButton->setEnabled(HasFeature && !IsLocked);
|
|
}
|
|
if (appFeatureNameEdit != nullptr)
|
|
{
|
|
appFeatureNameEdit->setEnabled(HasFeature && !IsLocked);
|
|
}
|
|
if (appFeatureDescriptionEdit != nullptr)
|
|
{
|
|
appFeatureDescriptionEdit->setEnabled(HasFeature && !IsLocked);
|
|
}
|
|
if (appFeatureTypeCombo != nullptr)
|
|
{
|
|
appFeatureTypeCombo->setEnabled(HasFeature && !IsLocked);
|
|
}
|
|
if (appFeatureWebsiteEdit != nullptr)
|
|
{
|
|
appFeatureWebsiteEdit->setEnabled(HasFeature && !IsLocked);
|
|
}
|
|
if (appFeatureSequenceEdit != nullptr)
|
|
{
|
|
appFeatureSequenceEdit->setReadOnly(appIsSequenceRecording);
|
|
}
|
|
if (appFeatureSequenceRecordStartButton != nullptr)
|
|
{
|
|
appFeatureSequenceRecordStartButton->setEnabled(CanRecord && !appIsSequenceRecording);
|
|
}
|
|
if (appFeatureSequenceRecordStopButton != nullptr)
|
|
{
|
|
appFeatureSequenceRecordStopButton->setEnabled(CanRecord && appIsSequenceRecording);
|
|
}
|
|
}
|
|
|
|
bool App_UIWindow::App_Func_HandleSequenceRecordMessage(void* p_Message)
|
|
{
|
|
if (!appIsSequenceRecording)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RAWKEYBOARD Keyboard = {};
|
|
if (!App_Func_TryGetRawKeyboard(p_Message, &Keyboard))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const QString Token = App_Func_GetRecordedKeyToken(Keyboard);
|
|
if (Token.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const bool IsPressed = (Keyboard.Flags & RI_KEY_BREAK) == 0;
|
|
if (IsPressed)
|
|
{
|
|
if (appSequenceRecordingPressedKeySet.contains(Token))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
appSequenceRecordingPressedKeySet.insert(Token);
|
|
if (!App_Func_IsModifierRecordToken(Token))
|
|
{
|
|
App_Func_AppendRecordedSequenceToken(Token);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
appSequenceRecordingPressedKeySet.remove(Token);
|
|
return false;
|
|
}
|
|
|
|
void App_UIWindow::App_Func_AppendRecordedSequenceToken(const QString& Token)
|
|
{
|
|
if (Token.isEmpty() || appFeatureSequenceEdit == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const Lgc_FunctionFeature_Definition Feature =
|
|
Lgc_FunctionButton_GetFeature(appLgcState.FunctionButtonConfig, appSelectedFeatureId);
|
|
if (Feature.Id <= 0 || !App_Func_IsKeyRecordFeatureType(Feature.Type))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const QString CombinationText =
|
|
App_Func_GetRecordedCombinationText(appSequenceRecordingPressedKeySet, Token);
|
|
if (CombinationText.isEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const QString OldText = appFeatureSequenceEdit->text().trimmed();
|
|
const QString NewText =
|
|
Feature.Type == Lgc_FunctionFeature_Type::KeySequence
|
|
? (OldText.isEmpty()
|
|
? CombinationText
|
|
: QStringLiteral("%1 -> %2").arg(OldText, CombinationText))
|
|
: CombinationText;
|
|
{
|
|
QSignalBlocker Blocker(appFeatureSequenceEdit);
|
|
appFeatureSequenceEdit->setText(NewText);
|
|
appFeatureSequenceEdit->setCursorPosition(NewText.size());
|
|
}
|
|
|
|
App_Func_SaveFeatureFromUi();
|
|
appLgcState.TextFunctionStatus = QStringLiteral("录入中:%1").arg(NewText);
|
|
App_Func_RefreshUi();
|
|
}
|
|
|
|
void App_UIWindow::App_Func_HandleUiKeyPressed(quint16 Usage)
|
|
{
|
|
QString TextStatus;
|
|
if (Lgc_FunctionButton_HasUsageFeature(appLgcState.FunctionButtonConfig, Usage))
|
|
{
|
|
if (!Lgc_FunctionButton_RunBinding(&appLgcState, Usage, &TextStatus))
|
|
{
|
|
TextStatus = QStringLiteral("%1 当前没有可执行功能。")
|
|
.arg(Lgc_FunctionButton_GetUsageShortText(Usage));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Lgc_FunctionButton_SendUsageToWindows(Usage, true))
|
|
{
|
|
appUiPressedUsageSet.insert(Usage);
|
|
TextStatus = QStringLiteral("已模拟按下 %1。")
|
|
.arg(Lgc_FunctionButton_GetUsageShortText(Usage));
|
|
}
|
|
else
|
|
{
|
|
TextStatus = QStringLiteral("模拟按下 %1 失败。")
|
|
.arg(Lgc_FunctionButton_GetUsageShortText(Usage));
|
|
}
|
|
}
|
|
|
|
if (!TextStatus.isEmpty())
|
|
{
|
|
appLgcState.TextFunctionStatus = TextStatus;
|
|
}
|
|
App_Func_RefreshUi();
|
|
}
|
|
|
|
void App_UIWindow::App_Func_HandleUiKeyReleased(quint16 Usage)
|
|
{
|
|
if (!appUiPressedUsageSet.remove(Usage))
|
|
{
|
|
return;
|
|
}
|
|
|
|
appLgcState.TextFunctionStatus =
|
|
Lgc_FunctionButton_SendUsageToWindows(Usage, false)
|
|
? QStringLiteral("已模拟抬起 %1。").arg(Lgc_FunctionButton_GetUsageShortText(Usage))
|
|
: QStringLiteral("模拟抬起 %1 失败。").arg(Lgc_FunctionButton_GetUsageShortText(Usage));
|
|
App_Func_RefreshUi();
|
|
}
|
|
|
|
void App_UIWindow::App_Func_ShowKeyMenu(quint16 Usage, const QPoint& GlobalPos)
|
|
{
|
|
const int CurrentFeatureId =
|
|
Lgc_FunctionButton_GetUsageFeatureId(appLgcState.FunctionButtonConfig, Usage);
|
|
|
|
QMenu Menu(this);
|
|
|
|
QAction* p_NoneAction = Menu.addAction(QStringLiteral("无功能"));
|
|
p_NoneAction->setCheckable(true);
|
|
p_NoneAction->setChecked(CurrentFeatureId <= 0);
|
|
p_NoneAction->setData(0);
|
|
|
|
const QVector<int> FeatureIdList =
|
|
Lgc_FunctionButton_GetFeatureIdList(appLgcState.FunctionButtonConfig);
|
|
if (!FeatureIdList.isEmpty())
|
|
{
|
|
Menu.addSeparator();
|
|
for (int FeatureId : FeatureIdList)
|
|
{
|
|
QAction* p_Action = Menu.addAction(QStringLiteral("绑定到 %1").arg(App_Func_GetFeatureNameById(FeatureId)));
|
|
p_Action->setCheckable(true);
|
|
p_Action->setChecked(FeatureId == CurrentFeatureId);
|
|
p_Action->setData(FeatureId);
|
|
p_Action->setToolTip(App_Func_GetFeatureDescriptionById(FeatureId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Menu.addSeparator();
|
|
QAction* p_EmptyAction = Menu.addAction(QStringLiteral("暂无功能,请先到功能表添加"));
|
|
p_EmptyAction->setEnabled(false);
|
|
}
|
|
|
|
Menu.addSeparator();
|
|
QAction* p_OpenFeaturePageAction = Menu.addAction(QStringLiteral("打开功能表"));
|
|
p_OpenFeaturePageAction->setData(-1);
|
|
|
|
QAction* p_SelectedAction = Menu.exec(GlobalPos);
|
|
if (p_SelectedAction == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int ActionData = p_SelectedAction->data().toInt();
|
|
if (ActionData == -1)
|
|
{
|
|
App_Func_SelectFeature(CurrentFeatureId, true);
|
|
return;
|
|
}
|
|
|
|
App_Func_AssignFeatureToUsage(Usage, ActionData);
|
|
if (ActionData > 0)
|
|
{
|
|
App_Func_SelectFeature(ActionData);
|
|
}
|
|
}
|
|
|
|
} // namespace APP
|