Files
Qt_keyboard/APP/APP_UIWindow.cpp

421 lines
16 KiB
C++
Raw Permalink Normal View History

2026-04-03 09:26:10 +08:00
#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