Files
Qt_keyboard/APP/APP_UIWindow.cpp
2026-04-03 09:26:10 +08:00

421 lines
16 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "APP/APP_UIWindow.h"
#include "APP/APP_GlassCard.h"
#include "APP/APP_KeyButton.h"
#include "APP/APP_Theme.h"
#include "APP/APP_UIWindow_Private.h"
#include "LOGIC/Lgc_Func_Button.h"
#include <QtCore/QSignalBlocker>
#include <QtGui/QPainter>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSizePolicy>
#include <QtWidgets/QStackedWidget>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QTableWidget>
#include <QtWidgets/QTableWidgetItem>
#include <QtWidgets/QVBoxLayout>
namespace APP {
namespace
{
QLabel* App_Func_CreateLabel(
QWidget* parent,
const QString& Text,
const QFont& Font,
bool WordWrap = false)
{
QLabel* p_Label = new QLabel(Text, parent);
p_Label->setFont(Font);
p_Label->setAttribute(Qt::WA_TranslucentBackground, true);
p_Label->setWordWrap(WordWrap);
return p_Label;
}
APP_GlassCard* App_Func_CreateCard(QWidget* parent, QVBoxLayout** pp_Layout)
{
APP_GlassCard* p_Card = new APP_GlassCard(parent);
QVBoxLayout* p_Layout = new QVBoxLayout(p_Card);
p_Layout->setContentsMargins(20, 20, 20, 20);
p_Layout->setSpacing(14);
*pp_Layout = p_Layout;
return p_Card;
}
void App_Func_SetGridStretch(QGridLayout* p_Grid, int ColumnCount, int RowCount)
{
for (int Column = 0; Column < ColumnCount; ++Column)
{
p_Grid->setColumnStretch(Column, 1);
}
for (int Row = 0; Row < RowCount; ++Row)
{
p_Grid->setRowStretch(Row, 1);
}
}
} // namespace
int App_Func_GetFeatureStackIndex(Lgc_FunctionFeature_Type Type)
{
switch (Type)
{
case Lgc_FunctionFeature_Type::KeyCombination:
case Lgc_FunctionFeature_Type::KeySequence:
return 0;
case Lgc_FunctionFeature_Type::Website:
return 1;
default:
return 0;
}
}
bool App_Func_IsKeyRecordFeatureType(Lgc_FunctionFeature_Type Type)
{
return (Type == Lgc_FunctionFeature_Type::KeyCombination) ||
(Type == Lgc_FunctionFeature_Type::KeySequence);
}
QString App_Func_GetWebsiteEditText(const QString& UrlText)
{
const QString DisplayText = UrlText.trimmed();
return DisplayText.isEmpty() ? QStringLiteral("https://") : DisplayText;
}
App_UIWindow::App_UIWindow(QWidget* parent)
: QWidget(parent)
{
App_Func_InitWindow();
App_Func_InitUi();
App_Func_InitConnect();
App_Func_InitLogic();
App_Func_RefreshUi();
}
App_UIWindow::~App_UIWindow()
{
Lgc_Core_Close(&appLgcState);
}
void App_UIWindow::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter Painter(this);
Painter.setRenderHint(QPainter::Antialiasing, true);
Painter.fillRect(rect(), palette().color(QPalette::Window));
}
void App_UIWindow::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
App_Func_UpdateFeatureEditorHeight();
}
bool App_UIWindow::nativeEvent(const QByteArray& EventType, void* p_Message, long* p_Result)
{
Q_UNUSED(EventType);
App_Func_HandleSequenceRecordMessage(p_Message);
Lgc_Core_HandleNativeMessage(&appLgcState, p_Message);
return QWidget::nativeEvent(EventType, p_Message, p_Result);
}
void App_UIWindow::App_Func_InitWindow()
{
setWindowTitle(QStringLiteral("数字键盘上位机"));
#if APP_ENABLE_DEBUG_WINDOW
setMinimumSize(820, 960);
resize(900, 1040);
#else
setMinimumSize(760, 820);
resize(820, 900);
#endif
setAttribute(Qt::WA_StyledBackground, true);
}
void App_UIWindow::App_Func_InitUi()
{
QVBoxLayout* p_RootLayout = new QVBoxLayout(this);
p_RootLayout->setContentsMargins(26, 22, 26, 24);
p_RootLayout->setSpacing(14);
appPageTab = new QTabWidget(this);
appPageTab->setDocumentMode(true);
appPageTab->setMovable(false);
appPageTab->addTab(App_Func_CreatePadCard(), QStringLiteral("按键映射"));
appFeaturePageIndex = appPageTab->addTab(App_Func_CreateFunctionConfigCard(), QStringLiteral("功能表"));
#if APP_ENABLE_DEBUG_WINDOW
appPageTab->addTab(App_Func_CreateDebugCard(), QStringLiteral("调试"));
#endif
p_RootLayout->addWidget(appPageTab, 1);
}
void App_UIWindow::App_Func_InitConnect()
{
connect(&appTimerPoll, &QTimer::timeout, this, &App_UIWindow::App_Func_OnPollTimer);
connect(&appTimerAutoRefreshDevice, &QTimer::timeout, this, [this]()
{
if (appLgcState.IsConnected)
{
return;
}
const QString OldTextLog = appLgcState.TextLog;
Lgc_Core_RefreshDevice(&appLgcState);
if (!appLgcState.IsConnected)
{
appLgcState.TextLog = OldTextLog;
}
App_Func_RefreshAfterLogicChange();
});
connect(appFeatureAddButton, &QPushButton::clicked, this, &App_UIWindow::App_Func_AddFeature);
connect(appFeatureDeleteButton, &QPushButton::clicked, this, &App_UIWindow::App_Func_DeleteFeature);
connect(appFeatureTable, &QTableWidget::itemSelectionChanged, this, [this]()
{
const QModelIndexList IndexList = appFeatureTable->selectionModel()->selectedRows();
if (IndexList.isEmpty())
{
App_Func_SelectFeature(0);
return;
}
const int Row = IndexList.first().row();
const QTableWidgetItem* p_Item = appFeatureTable->item(Row, 0);
App_Func_SelectFeature(p_Item == nullptr ? 0 : p_Item->data(Qt::UserRole).toInt());
});
const auto ConnectSaveSignal = [this](auto Sender, auto Signal)
{
connect(Sender, Signal, this, [this]()
{
if (!appIsUpdatingFeatureUi)
{
App_Func_SaveFeatureFromUi();
}
});
};
ConnectSaveSignal(appFeatureNameEdit, &QLineEdit::textChanged);
ConnectSaveSignal(appFeatureDescriptionEdit, &QLineEdit::textChanged);
ConnectSaveSignal(appFeatureTypeCombo, qOverload<int>(&QComboBox::currentIndexChanged));
ConnectSaveSignal(appFeatureSequenceEdit, &QLineEdit::textChanged);
ConnectSaveSignal(appFeatureWebsiteEdit, &QLineEdit::textChanged);
connect(appFeatureSequenceRecordStartButton, &QPushButton::clicked, this, &App_UIWindow::App_Func_StartSequenceRecording);
connect(appFeatureSequenceRecordStopButton, &QPushButton::clicked, this, &App_UIWindow::App_Func_StopSequenceRecording);
#if APP_ENABLE_DEBUG_WINDOW
connect(appDebugPanel->Debug_Func_GetRefreshButton(), &QPushButton::clicked, this, &App_UIWindow::App_Func_OnRefreshDeviceClicked);
connect(appDebugPanel->Debug_Func_GetClearButton(), &QPushButton::clicked, this, &App_UIWindow::App_Func_OnClearLogClicked);
connect(appDebugPanel->Debug_Func_GetApplyConfigButton(), &QPushButton::clicked, this, &App_UIWindow::App_Func_OnApplyDeviceConfigClicked);
connect(appDebugPanel->Debug_Func_GetSyncTimeButton(), &QPushButton::clicked, this, &App_UIWindow::App_Func_OnSyncTimeClicked);
connect(appDebugPanel->Debug_Func_GetModeSwitchButton(), &QPushButton::clicked, this, &App_UIWindow::App_Func_OnModeSwitchClicked);
#endif
}
void App_UIWindow::App_Func_InitLogic()
{
Lgc_Core_Init(&appLgcState);
Lgc_Core_SetWindowHandle(&appLgcState, reinterpret_cast<void*>(winId()));
App_Func_RefreshFeatureTable();
#if APP_ENABLE_DEBUG_WINDOW
App_Func_RefreshDeviceConfigFromState();
#endif
Lgc_Core_Start(&appLgcState);
appTimerPoll.setInterval(30);
appTimerPoll.start();
appTimerAutoRefreshDevice.setInterval(1500);
appTimerAutoRefreshDevice.start();
App_Func_RefreshAfterLogicChange();
}
QWidget* App_UIWindow::App_Func_CreatePadCard()
{
QVBoxLayout* p_Layout = nullptr;
APP_GlassCard* p_Card = App_Func_CreateCard(this, &p_Layout);
p_Layout->addWidget(App_Func_CreateLabel(
p_Card,
QStringLiteral("按键映射"),
APP_Theme::App_Func_GetMetricFont()));
p_Layout->addWidget(App_Func_CreateLabel(
p_Card,
QStringLiteral("左键直接模拟真实小键盘按下/抬起;右键把当前按键绑定到某个功能。绑定后,键帽左上角会显示功能名,悬停会显示功能简介。"),
APP_Theme::App_Func_GetBodyFont(),
true));
QGridLayout* p_Grid = new QGridLayout();
p_Grid->setSpacing(14);
for (const APP_KeyInfo& Key : appKeypadModel.App_Func_GetKeyList())
{
APP_KeyButton* p_Button = new APP_KeyButton(Key, p_Card);
p_Button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(p_Button, &QPushButton::pressed, this, [this, Key]() { App_Func_HandleUiKeyPressed(Key.usage); });
connect(p_Button, &QPushButton::released, this, [this, Key]() { App_Func_HandleUiKeyReleased(Key.usage); });
connect(p_Button, &QWidget::customContextMenuRequested, this, [this, p_Button, Key](const QPoint&)
{
App_Func_ShowKeyMenu(
Key.usage,
p_Button->mapToGlobal(QPoint(p_Button->width() / 2, p_Button->height() / 2)));
});
appKeypadButtonMap.insert(Key.id, p_Button);
p_Grid->addWidget(p_Button, Key.row, Key.column, Key.rowSpan, Key.columnSpan);
}
App_Func_SetGridStretch(p_Grid, 4, 5);
p_Layout->addLayout(p_Grid, 1);
return p_Card;
}
QWidget* App_UIWindow::App_Func_CreateFunctionConfigCard()
{
QVBoxLayout* p_Layout = nullptr;
APP_GlassCard* p_Card = App_Func_CreateCard(this, &p_Layout);
const auto AddRow = [p_Card](QFormLayout* p_Form, const QString& LabelText, QWidget* p_Field)
{
p_Form->addRow(App_Func_CreateLabel(p_Card, LabelText, APP_Theme::App_Func_GetBodyFont()), p_Field);
};
p_Layout->addWidget(App_Func_CreateLabel(
p_Card,
QStringLiteral("功能表"),
APP_Theme::App_Func_GetMetricFont()));
p_Layout->addWidget(App_Func_CreateLabel(
p_Card,
QStringLiteral("功能表默认为空,请先添加功能,再把按钮绑定到某个功能。当前支持“快捷键”“快捷键序列”和“打开网址”三种功能类型。"),
APP_Theme::App_Func_GetBodyFont(),
true));
QHBoxLayout* p_TopRow = new QHBoxLayout();
p_TopRow->setContentsMargins(0, 0, 0, 0);
p_TopRow->setSpacing(10);
appFeatureAddButton = new QPushButton(QStringLiteral("添加功能"), p_Card);
appFeatureDeleteButton = new QPushButton(QStringLiteral("删除功能"), p_Card);
appFeatureDeleteButton->setEnabled(false);
p_TopRow->addWidget(appFeatureAddButton);
p_TopRow->addWidget(appFeatureDeleteButton);
p_TopRow->addStretch(1);
p_Layout->addLayout(p_TopRow);
appFeatureTable = new QTableWidget(p_Card);
appFeatureTable->setColumnCount(3);
appFeatureTable->setHorizontalHeaderLabels({ QStringLiteral("功能名"), QStringLiteral("功能简介"), QStringLiteral("类型") });
appFeatureTable->setSelectionBehavior(QAbstractItemView::SelectRows);
appFeatureTable->setSelectionMode(QAbstractItemView::SingleSelection);
appFeatureTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
appFeatureTable->setAlternatingRowColors(true);
appFeatureTable->verticalHeader()->setVisible(false);
appFeatureTable->horizontalHeader()->setStretchLastSection(false);
appFeatureTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
appFeatureTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
appFeatureTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
appFeatureTable->setMinimumHeight(220);
p_Layout->addWidget(appFeatureTable);
QFormLayout* p_Form = new QFormLayout();
p_Form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
p_Form->setLabelAlignment(Qt::AlignLeft | Qt::AlignVCenter);
p_Form->setHorizontalSpacing(10);
p_Form->setVerticalSpacing(10);
appFeatureNameEdit = new QLineEdit(p_Card);
appFeatureNameEdit->setPlaceholderText(QStringLiteral("例如功能1 / 打开4399 / 前缀补码"));
AddRow(p_Form, QStringLiteral("功能名"), appFeatureNameEdit);
appFeatureDescriptionEdit = new QLineEdit(p_Card);
appFeatureDescriptionEdit->setPlaceholderText(QStringLiteral("例如:打开 4399 首页"));
AddRow(p_Form, QStringLiteral("功能简介"), appFeatureDescriptionEdit);
appFeatureTypeCombo = new QComboBox(p_Card);
appFeatureTypeCombo->addItem(
Lgc_FunctionButton_GetFeatureTypeText(Lgc_FunctionFeature_Type::KeyCombination),
static_cast<int>(Lgc_FunctionFeature_Type::KeyCombination));
appFeatureTypeCombo->addItem(
Lgc_FunctionButton_GetFeatureTypeText(Lgc_FunctionFeature_Type::KeySequence),
static_cast<int>(Lgc_FunctionFeature_Type::KeySequence));
appFeatureTypeCombo->addItem(
Lgc_FunctionButton_GetFeatureTypeText(Lgc_FunctionFeature_Type::Website),
static_cast<int>(Lgc_FunctionFeature_Type::Website));
AddRow(p_Form, QStringLiteral("功能类型"), appFeatureTypeCombo);
appFeatureEditorStack = new QStackedWidget(p_Card);
appFeatureEditorStack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QWidget* p_SequencePage = new QWidget(appFeatureEditorStack);
QFormLayout* p_SequenceForm = new QFormLayout(p_SequencePage);
p_SequenceForm->setContentsMargins(0, 0, 0, 0);
p_SequenceForm->setHorizontalSpacing(10);
p_SequenceForm->setVerticalSpacing(10);
QWidget* p_SequenceEditor = new QWidget(p_SequencePage);
QHBoxLayout* p_SequenceEditorLayout = new QHBoxLayout(p_SequenceEditor);
p_SequenceEditorLayout->setContentsMargins(0, 0, 0, 0);
p_SequenceEditorLayout->setSpacing(8);
appFeatureSequenceEdit = new QLineEdit(p_SequenceEditor);
appFeatureSequenceEdit->setPlaceholderText(QStringLiteral("例如Ctrl+C"));
appFeatureSequenceRecordStartButton = new QPushButton(QStringLiteral("开始录入"), p_SequenceEditor);
appFeatureSequenceRecordStopButton = new QPushButton(QStringLiteral("退出录入"), p_SequenceEditor);
appFeatureSequenceRecordStopButton->setEnabled(false);
p_SequenceEditorLayout->addWidget(appFeatureSequenceEdit, 1);
p_SequenceEditorLayout->addWidget(appFeatureSequenceRecordStartButton);
p_SequenceEditorLayout->addWidget(appFeatureSequenceRecordStopButton);
p_SequenceForm->addRow(QStringLiteral("快捷键"), p_SequenceEditor);
QWidget* p_WebsitePage = new QWidget(appFeatureEditorStack);
QVBoxLayout* p_WebsiteLayout = new QVBoxLayout(p_WebsitePage);
p_WebsiteLayout->setContentsMargins(0, 0, 0, 0);
p_WebsiteLayout->setSpacing(0);
appFeatureWebsiteEdit = new QLineEdit(p_WebsitePage);
appFeatureWebsiteEdit->setPlaceholderText(QStringLiteral("例如直接输入 4399.com 或 www.4399.com"));
p_WebsiteLayout->addWidget(appFeatureWebsiteEdit);
appFeatureEditorStack->addWidget(p_SequencePage);
appFeatureEditorStack->addWidget(p_WebsitePage);
AddRow(p_Form, QStringLiteral("功能参数"), appFeatureEditorStack);
appFeatureBindingSummaryLabel = App_Func_CreateLabel(
p_Card,
QStringLiteral("当前还没有功能,请点击“添加功能”。"),
APP_Theme::App_Func_GetBodyFont(),
true);
AddRow(p_Form, QStringLiteral("绑定情况"), appFeatureBindingSummaryLabel);
appFunctionLabelStatus = App_Func_CreateLabel(
p_Card,
QStringLiteral("等待按键动作。"),
APP_Theme::App_Func_GetBodyFont(),
true);
AddRow(p_Form, QStringLiteral("最近一次动作"), appFunctionLabelStatus);
p_Layout->addLayout(p_Form);
p_Layout->addStretch(1);
return p_Card;
}
#if APP_ENABLE_DEBUG_WINDOW
QWidget* App_UIWindow::App_Func_CreateDebugCard()
{
appDebugPanel = new DEBUG::Debug_Panel(this);
appDebugPanel->Debug_Func_SetConnectionText(QStringLiteral("未连接,等待枚举设备。"), false);
appDebugPanel->Debug_Func_SetLogText(QStringLiteral("等待收到输入包。"));
return appDebugPanel;
}
#endif
} // namespace APP