2026-03-26 10:45:29 +08:00
|
|
|
#include "APP/APP_UIWindow.h"
|
|
|
|
|
|
|
|
|
|
#include "APP/APP_GlassCard.h"
|
|
|
|
|
#include "APP/APP_KeyButton.h"
|
|
|
|
|
#include "APP/APP_Theme.h"
|
2026-04-17 16:25:19 +08:00
|
|
|
#include "APP/APP_UIWindow_Private.h"
|
|
|
|
|
#include <QtGui/QFontDatabase>
|
2026-03-26 10:45:29 +08:00
|
|
|
#include <QtGui/QPainter>
|
|
|
|
|
#include <QtWidgets/QComboBox>
|
|
|
|
|
#include <QtWidgets/QFormLayout>
|
|
|
|
|
#include <QtWidgets/QGridLayout>
|
2026-04-17 16:25:19 +08:00
|
|
|
#include <QtWidgets/QHeaderView>
|
2026-03-26 10:45:29 +08:00
|
|
|
#include <QtWidgets/QHBoxLayout>
|
|
|
|
|
#include <QtWidgets/QLabel>
|
|
|
|
|
#include <QtWidgets/QLineEdit>
|
2026-04-17 16:25:19 +08:00
|
|
|
#include <QtWidgets/QPlainTextEdit>
|
2026-03-26 10:45:29 +08:00
|
|
|
#include <QtWidgets/QPushButton>
|
2026-04-17 16:25:19 +08:00
|
|
|
#include <QtWidgets/QScrollBar>
|
|
|
|
|
#include <QtWidgets/QSizePolicy>
|
|
|
|
|
#include <QtWidgets/QSpinBox>
|
|
|
|
|
#include <QtWidgets/QStackedWidget>
|
2026-03-26 10:45:29 +08:00
|
|
|
#include <QtWidgets/QTabWidget>
|
2026-04-17 16:25:19 +08:00
|
|
|
#include <QtWidgets/QTableWidget>
|
|
|
|
|
#include <QtWidgets/QTableWidgetItem>
|
2026-03-26 10:45:29 +08:00
|
|
|
#include <QtWidgets/QVBoxLayout>
|
2026-04-17 16:25:19 +08:00
|
|
|
#include <Windows.h>
|
2026-03-26 10:45:29 +08:00
|
|
|
|
|
|
|
|
namespace APP {
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
namespace
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
enum App_Enum_PacketTestRow
|
|
|
|
|
{
|
|
|
|
|
App_Enum_PacketTestRow_TxHelloReq = 0,
|
|
|
|
|
App_Enum_PacketTestRow_RxHelloRsp,
|
|
|
|
|
App_Enum_PacketTestRow_TxBitmap,
|
|
|
|
|
App_Enum_PacketTestRow_RxFunctionKeyEvent,
|
|
|
|
|
App_Enum_PacketTestRow_RxLedState,
|
|
|
|
|
App_Enum_PacketTestRow_TxTimeSync,
|
|
|
|
|
App_Enum_PacketTestRow_TxThemeRgb,
|
|
|
|
|
App_Enum_PacketTestRow_RxAck,
|
|
|
|
|
App_Enum_PacketTestRow_RxError,
|
|
|
|
|
App_Enum_PacketTestRow_Count
|
|
|
|
|
};
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_Func_SetGridStretch(QGridLayout* p_Grid, int ColumnCount, int RowCount)
|
|
|
|
|
{
|
|
|
|
|
for (int Column = 0; Column < ColumnCount; ++Column)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Grid->setColumnStretch(Column, 1);
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
2026-04-17 16:25:19 +08:00
|
|
|
for (int Row = 0; Row < RowCount; ++Row)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Grid->setRowStretch(Row, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
bool App_Func_RegisterRawInputWindow(WId WindowId)
|
|
|
|
|
{
|
|
|
|
|
RAWINPUTDEVICE Device = {};
|
|
|
|
|
Device.usUsagePage = 0x01;
|
|
|
|
|
Device.usUsage = 0x06;
|
|
|
|
|
Device.dwFlags = RIDEV_INPUTSINK;
|
|
|
|
|
Device.hwndTarget = reinterpret_cast<HWND>(WindowId);
|
|
|
|
|
return RegisterRawInputDevices(&Device, 1, sizeof(Device)) == TRUE;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QString App_Func_BoolText(bool Value)
|
|
|
|
|
{
|
|
|
|
|
return Value ? QStringLiteral("Yes") : QStringLiteral("No");
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
Com_Enum_RawPacketSource App_Func_GetPacketTestTargetSource(const QComboBox* p_Combo)
|
|
|
|
|
{
|
|
|
|
|
return p_Combo == nullptr
|
|
|
|
|
? Com_Enum_RawPacketSource_None
|
|
|
|
|
: static_cast<Com_Enum_RawPacketSource>(p_Combo->currentData().toInt());
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QString App_Func_GetPacketTestTargetText(Com_Enum_RawPacketSource Source)
|
|
|
|
|
{
|
|
|
|
|
switch (Source)
|
|
|
|
|
{
|
|
|
|
|
case Com_Enum_RawPacketSource_UsbCdc:
|
|
|
|
|
return QStringLiteral("USB CDC only");
|
|
|
|
|
case Com_Enum_RawPacketSource_BleNus:
|
|
|
|
|
return QStringLiteral("BLE NUS only");
|
|
|
|
|
default:
|
|
|
|
|
return QStringLiteral("Auto (ready transport)");
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
2026-04-17 16:25:19 +08:00
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QString App_Func_FormatPacketType(quint32 RawType)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
switch (static_cast<Com_Enum_ProtocolType>(RawType & 0xFFU))
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
case Com_Enum_ProtocolType_HelloReq: return QStringLiteral("HelloReq");
|
|
|
|
|
case Com_Enum_ProtocolType_HelloRsp: return QStringLiteral("HelloRsp");
|
|
|
|
|
case Com_Enum_ProtocolType_Bitmap: return QStringLiteral("Bitmap");
|
|
|
|
|
case Com_Enum_ProtocolType_FunctionKeyEvent: return QStringLiteral("FunctionKeyEvent");
|
|
|
|
|
case Com_Enum_ProtocolType_LedState: return QStringLiteral("LedState");
|
|
|
|
|
case Com_Enum_ProtocolType_TimeSync: return QStringLiteral("TimeSync");
|
|
|
|
|
case Com_Enum_ProtocolType_ThemeRgb: return QStringLiteral("ThemeRgb");
|
|
|
|
|
case Com_Enum_ProtocolType_Ack: return QStringLiteral("Ack");
|
|
|
|
|
case Com_Enum_ProtocolType_Error: return QStringLiteral("Error");
|
|
|
|
|
default:
|
|
|
|
|
return RawType == 0
|
|
|
|
|
? QStringLiteral("None")
|
|
|
|
|
: QStringLiteral("0x%1").arg(RawType, 0, 16).toUpper();
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
2026-04-17 16:25:19 +08:00
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QString App_Func_FormatVidPid(quint32 VendorId, quint32 ProductId)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
return QStringLiteral("%1:%2")
|
|
|
|
|
.arg(VendorId, 4, 16, QLatin1Char('0'))
|
|
|
|
|
.arg(ProductId, 4, 16, QLatin1Char('0'))
|
|
|
|
|
.toUpper();
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QFont App_Func_GetMonoFont()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
QFont Font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
|
|
|
|
Font.setPointSize(9);
|
|
|
|
|
return Font;
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
int App_Func_GetFeatureStackIndex(Lgc_FunctionFeature_Type Type)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
switch (Type)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
case Lgc_FunctionFeature_Type::KeyCombination:
|
|
|
|
|
case Lgc_FunctionFeature_Type::KeySequence:
|
|
|
|
|
return 0;
|
|
|
|
|
case Lgc_FunctionFeature_Type::Website:
|
|
|
|
|
return 1;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
|
|
|
|
App_UIWindow::App_UIWindow(QWidget* parent)
|
|
|
|
|
: QWidget(parent)
|
|
|
|
|
{
|
|
|
|
|
App_Func_InitWindow();
|
|
|
|
|
App_Func_InitUi();
|
|
|
|
|
App_Func_InitConnect();
|
|
|
|
|
App_Func_InitLogic();
|
2026-04-17 16:25:19 +08:00
|
|
|
App_Func_RefreshKeypadButtons();
|
|
|
|
|
App_Func_RefreshFunctionStatus();
|
|
|
|
|
App_Func_RefreshPacketTestPanel();
|
|
|
|
|
update();
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
App_UIWindow::~App_UIWindow()
|
|
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_Close(&appLgcState);
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void App_UIWindow::paintEvent(QPaintEvent* event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
QPainter Painter(this);
|
|
|
|
|
Painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
Painter.fillRect(rect(), palette().color(QPalette::Window));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_UIWindow::resizeEvent(QResizeEvent* event)
|
|
|
|
|
{
|
|
|
|
|
QWidget::resizeEvent(event);
|
|
|
|
|
App_Func_UpdateFeatureEditorHeight();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 10:45:29 +08:00
|
|
|
bool App_UIWindow::nativeEvent(const QByteArray& EventType, void* p_Message, long* p_Result)
|
|
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Q_UNUSED(EventType);
|
|
|
|
|
App_Func_HandleSequenceRecordMessage(p_Message);
|
|
|
|
|
Lgc_Core_HandleNativeMessage(&appLgcState, p_Message);
|
2026-03-26 10:45:29 +08:00
|
|
|
return QWidget::nativeEvent(EventType, p_Message, p_Result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void App_UIWindow::App_Func_InitWindow()
|
|
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
setWindowTitle(QStringLiteral("Keyboard Control"));
|
|
|
|
|
setMinimumSize(760, 820);
|
|
|
|
|
resize(820, 900);
|
2026-03-26 10:45:29 +08:00
|
|
|
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);
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
appPageTab = new QTabWidget(this);
|
|
|
|
|
appPageTab->setDocumentMode(true);
|
|
|
|
|
appPageTab->setMovable(false);
|
|
|
|
|
appPageTab->addTab(App_Func_CreatePadCard(), QStringLiteral("Keypad"));
|
|
|
|
|
appFeaturePageIndex =
|
|
|
|
|
appPageTab->addTab(App_Func_CreateFunctionConfigCard(), QStringLiteral("Functions"));
|
|
|
|
|
appPacketTestPageIndex =
|
|
|
|
|
appPageTab->addTab(App_Func_CreatePacketTestCard(), QStringLiteral("Device Test"));
|
|
|
|
|
p_RootLayout->addWidget(appPageTab, 1);
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void App_UIWindow::App_Func_InitConnect()
|
|
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
connect(&appTimerPoll, &QTimer::timeout, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
if (Lgc_Core_Poll(&appLgcState))
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
});
|
2026-04-17 16:25:19 +08:00
|
|
|
connect(&appTimerAutoRefreshDevice, &QTimer::timeout, this, [this]()
|
|
|
|
|
{
|
|
|
|
|
const Lgc_Core_Struct_View View = Lgc_Core_GetView(&appLgcState);
|
|
|
|
|
if (View.IsConnected || View.HasOpenTransport)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_RefreshDevice(&appLgcState);
|
|
|
|
|
App_Func_RefreshKeypadState();
|
|
|
|
|
App_Func_RefreshKeypadButtons();
|
|
|
|
|
App_Func_RefreshFunctionStatus();
|
|
|
|
|
App_Func_RefreshPacketTestPanel();
|
|
|
|
|
update();
|
|
|
|
|
});
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
connect(appFeatureAddButton, &QPushButton::clicked, this, &App_UIWindow::App_Func_AddFeature);
|
|
|
|
|
connect(appFeatureDeleteButton, &QPushButton::clicked, this, &App_UIWindow::App_Func_DeleteFeature);
|
|
|
|
|
connect(appFeatureTimeSyncButton, &QPushButton::clicked, this, [this]()
|
|
|
|
|
{
|
|
|
|
|
Lgc_Core_SendTimeSync(&appLgcState);
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
|
|
|
|
connect(appFeatureThemeSwitchButton, &QPushButton::clicked, this, [this]()
|
|
|
|
|
{
|
|
|
|
|
Lgc_Core_SendThemeSwitch(&appLgcState);
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
connect(appFeatureTable, &QTableWidget::itemSelectionChanged, this, [this]()
|
|
|
|
|
{
|
|
|
|
|
const QModelIndexList IndexList = appFeatureTable->selectionModel()->selectedRows();
|
|
|
|
|
if (IndexList.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
App_Func_SelectFeature(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
const int Row = IndexList.first().row();
|
|
|
|
|
App_Func_SelectFeature(appFeatureTable->item(Row, 0)->data(Qt::UserRole).toInt());
|
|
|
|
|
});
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
const auto ConnectSaveSignal = [this](auto Sender, auto Signal)
|
|
|
|
|
{
|
|
|
|
|
connect(Sender, Signal, this, [this]()
|
|
|
|
|
{
|
|
|
|
|
if (!appIsUpdatingFeatureUi)
|
|
|
|
|
{
|
|
|
|
|
App_Func_SaveFeatureFromUi();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
connect(appPacketTestRefreshButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_RefreshDevice(&appLgcState);
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
|
|
|
|
connect(appPacketTestTargetCombo, qOverload<int>(&QComboBox::currentIndexChanged), this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
App_Func_RefreshPacketTestPanel();
|
|
|
|
|
});
|
|
|
|
|
connect(appPacketTestSendHelloButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_TestSendHello(
|
|
|
|
|
&appLgcState,
|
|
|
|
|
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
|
|
|
|
connect(appPacketTestSendBitmapCurrentButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_TestSendBitmapCurrentConfig(
|
|
|
|
|
&appLgcState,
|
|
|
|
|
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
|
|
|
|
connect(appPacketTestSendBitmapAllOnButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_TestSendBitmapAllEnabled(
|
|
|
|
|
&appLgcState,
|
|
|
|
|
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
|
|
|
|
connect(appPacketTestSendBitmapAllOffButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_TestSendBitmapAllDisabled(
|
|
|
|
|
&appLgcState,
|
|
|
|
|
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
|
|
|
|
connect(appPacketTestSendTimeSyncButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_TestSendTimeSync(
|
2026-03-26 10:45:29 +08:00
|
|
|
&appLgcState,
|
2026-04-17 16:25:19 +08:00
|
|
|
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
2026-03-26 10:45:29 +08:00
|
|
|
App_Func_RefreshAfterLogicChange();
|
2026-04-17 16:25:19 +08:00
|
|
|
});
|
|
|
|
|
connect(appPacketTestSendThemeButton, &QPushButton::clicked, this, [this]()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_TestSendThemeRgb(
|
|
|
|
|
&appLgcState,
|
|
|
|
|
static_cast<quint8>(appPacketTestThemeRedSpin->value()),
|
|
|
|
|
static_cast<quint8>(appPacketTestThemeGreenSpin->value()),
|
|
|
|
|
static_cast<quint8>(appPacketTestThemeBlueSpin->value()),
|
|
|
|
|
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
2026-03-26 10:45:29 +08:00
|
|
|
App_Func_RefreshAfterLogicChange();
|
2026-04-17 16:25:19 +08:00
|
|
|
});
|
|
|
|
|
connect(appPacketTestClearLogButton, &QPushButton::clicked, this, [this]()
|
|
|
|
|
{
|
|
|
|
|
Lgc_Core_ClearTestLog(&appLgcState);
|
|
|
|
|
App_Func_RefreshAfterLogicChange();
|
|
|
|
|
});
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_UIWindow::App_Func_InitLogic()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_Init(&appLgcState);
|
|
|
|
|
const WId WindowId = winId();
|
|
|
|
|
Lgc_Core_SetWindowHandle(&appLgcState, reinterpret_cast<void*>(WindowId));
|
|
|
|
|
if (!App_Func_RegisterRawInputWindow(WindowId))
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_SetStatusText(&appLgcState, QStringLiteral("Raw keyboard capture init failed."));
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_LoadFunctionConfig(&appLgcState);
|
|
|
|
|
App_Func_RefreshFeatureTable();
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
Lgc_Core_Start(&appLgcState);
|
|
|
|
|
appTimerPoll.setInterval(30);
|
|
|
|
|
appTimerPoll.start();
|
|
|
|
|
appTimerAutoRefreshDevice.setInterval(1500);
|
|
|
|
|
appTimerAutoRefreshDevice.start();
|
|
|
|
|
App_Func_RefreshKeypadState();
|
|
|
|
|
App_Func_RefreshKeypadButtons();
|
|
|
|
|
App_Func_RefreshFunctionStatus();
|
|
|
|
|
App_Func_RefreshPacketTestPanel();
|
|
|
|
|
update();
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget* App_UIWindow::App_Func_CreatePadCard()
|
|
|
|
|
{
|
|
|
|
|
QVBoxLayout* p_Layout = nullptr;
|
|
|
|
|
APP_GlassCard* p_Card = App_Func_CreateCard(this, &p_Layout);
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Layout->addWidget(
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Keypad"), APP_Theme::App_Func_GetMetricFont()));
|
|
|
|
|
p_Layout->addWidget(App_Func_CreateLabel(
|
|
|
|
|
p_Card,
|
|
|
|
|
QStringLiteral(
|
|
|
|
|
"Left click simulates a real press/release. Right click binds the current key to a "
|
|
|
|
|
"function. After binding, the button shows the function name and tooltip."),
|
|
|
|
|
APP_Theme::App_Func_GetBodyFont(),
|
|
|
|
|
true));
|
2026-03-26 10:45:29 +08:00
|
|
|
|
|
|
|
|
QGridLayout* p_Grid = new QGridLayout();
|
|
|
|
|
p_Grid->setSpacing(14);
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
for (const APP_KeyInfo& Key : appKeypadModel.App_Func_GetKeyList())
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
|
|
|
|
APP_KeyButton* p_Button = new APP_KeyButton(Key, p_Card);
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Button->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
connect(
|
|
|
|
|
p_Button,
|
|
|
|
|
&QPushButton::pressed,
|
|
|
|
|
this,
|
|
|
|
|
[this, Key]()
|
|
|
|
|
{
|
|
|
|
|
Lgc_Core_HandleUiKeyPress(&appLgcState, Key.usage);
|
|
|
|
|
App_Func_RefreshUi();
|
|
|
|
|
});
|
|
|
|
|
connect(
|
|
|
|
|
p_Button,
|
|
|
|
|
&QPushButton::released,
|
|
|
|
|
this,
|
|
|
|
|
[this, Key]()
|
|
|
|
|
{
|
|
|
|
|
Lgc_Core_HandleUiKeyRelease(&appLgcState, Key.usage);
|
|
|
|
|
App_Func_RefreshUi();
|
|
|
|
|
});
|
|
|
|
|
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)));
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-26 10:45:29 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QWidget* App_UIWindow::App_Func_CreateFunctionConfigCard()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
|
|
|
|
QVBoxLayout* p_Layout = nullptr;
|
|
|
|
|
APP_GlassCard* p_Card = App_Func_CreateCard(this, &p_Layout);
|
2026-04-17 16:25:19 +08:00
|
|
|
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);
|
|
|
|
|
};
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Layout->addWidget(
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Functions"), APP_Theme::App_Func_GetMetricFont()));
|
|
|
|
|
p_Layout->addWidget(App_Func_CreateLabel(
|
|
|
|
|
p_Card,
|
|
|
|
|
QStringLiteral(
|
|
|
|
|
"The function list starts empty. Add a function first, then bind keypad keys to it. "
|
|
|
|
|
"Current types: shortcut, shortcut sequence, and website."),
|
|
|
|
|
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("Add Function"), p_Card);
|
|
|
|
|
appFeatureDeleteButton = new QPushButton(QStringLiteral("Delete Function"), p_Card);
|
|
|
|
|
appFeatureTimeSyncButton = new QPushButton(QStringLiteral("Time Sync"), p_Card);
|
|
|
|
|
appFeatureThemeSwitchButton = new QPushButton(QStringLiteral("Theme Switch"), p_Card);
|
|
|
|
|
appFeatureDeleteButton->setEnabled(false);
|
|
|
|
|
p_TopRow->addWidget(appFeatureAddButton);
|
|
|
|
|
p_TopRow->addWidget(appFeatureDeleteButton);
|
|
|
|
|
p_TopRow->addWidget(appFeatureTimeSyncButton);
|
|
|
|
|
p_TopRow->addWidget(appFeatureThemeSwitchButton);
|
|
|
|
|
p_TopRow->addStretch(1);
|
|
|
|
|
p_Layout->addLayout(p_TopRow);
|
|
|
|
|
|
|
|
|
|
appFeatureTable = new QTableWidget(p_Card);
|
|
|
|
|
appFeatureTable->setColumnCount(3);
|
|
|
|
|
appFeatureTable->setHorizontalHeaderLabels(
|
|
|
|
|
{ QStringLiteral("Name"), QStringLiteral("Description"), QStringLiteral("Type") });
|
|
|
|
|
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("Example: Function 1 / Open 4399 / Prefix Macro"));
|
|
|
|
|
AddRow(p_Form, QStringLiteral("Name"), appFeatureNameEdit);
|
|
|
|
|
|
|
|
|
|
appFeatureDescriptionEdit = new QLineEdit(p_Card);
|
|
|
|
|
appFeatureDescriptionEdit->setPlaceholderText(
|
|
|
|
|
QStringLiteral("Example: Open the 4399 home page"));
|
|
|
|
|
AddRow(p_Form, QStringLiteral("Description"), appFeatureDescriptionEdit);
|
|
|
|
|
|
|
|
|
|
appFeatureTypeCombo = new QComboBox(p_Card);
|
|
|
|
|
appFeatureTypeCombo->addItem(
|
|
|
|
|
Lgc_Core_GetFeatureTypeText(Lgc_FunctionFeature_Type::KeyCombination),
|
|
|
|
|
static_cast<int>(Lgc_FunctionFeature_Type::KeyCombination));
|
|
|
|
|
appFeatureTypeCombo->addItem(
|
|
|
|
|
Lgc_Core_GetFeatureTypeText(Lgc_FunctionFeature_Type::KeySequence),
|
|
|
|
|
static_cast<int>(Lgc_FunctionFeature_Type::KeySequence));
|
|
|
|
|
appFeatureTypeCombo->addItem(
|
|
|
|
|
Lgc_Core_GetFeatureTypeText(Lgc_FunctionFeature_Type::Website),
|
|
|
|
|
static_cast<int>(Lgc_FunctionFeature_Type::Website));
|
|
|
|
|
AddRow(p_Form, QStringLiteral("Type"), 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("Example: Ctrl+C"));
|
|
|
|
|
appFeatureSequenceRecordStartButton =
|
|
|
|
|
new QPushButton(QStringLiteral("Start Record"), p_SequenceEditor);
|
|
|
|
|
appFeatureSequenceRecordStopButton =
|
|
|
|
|
new QPushButton(QStringLiteral("Stop Record"), p_SequenceEditor);
|
|
|
|
|
appFeatureSequenceRecordStopButton->setEnabled(false);
|
|
|
|
|
p_SequenceEditorLayout->addWidget(appFeatureSequenceEdit, 1);
|
|
|
|
|
p_SequenceEditorLayout->addWidget(appFeatureSequenceRecordStartButton);
|
|
|
|
|
p_SequenceEditorLayout->addWidget(appFeatureSequenceRecordStopButton);
|
|
|
|
|
p_SequenceForm->addRow(QStringLiteral("Shortcut"), 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("Example: 4399.com or www.4399.com"));
|
|
|
|
|
p_WebsiteLayout->addWidget(appFeatureWebsiteEdit);
|
|
|
|
|
|
|
|
|
|
appFeatureEditorStack->addWidget(p_SequencePage);
|
|
|
|
|
appFeatureEditorStack->addWidget(p_WebsitePage);
|
|
|
|
|
AddRow(p_Form, QStringLiteral("Parameter"), appFeatureEditorStack);
|
|
|
|
|
|
|
|
|
|
appFeatureBindingSummaryLabel = App_Func_CreateLabel(
|
|
|
|
|
p_Card,
|
|
|
|
|
QStringLiteral("No function selected yet."),
|
|
|
|
|
APP_Theme::App_Func_GetBodyFont(),
|
|
|
|
|
true);
|
|
|
|
|
AddRow(p_Form, QStringLiteral("Bindings"), appFeatureBindingSummaryLabel);
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
appFunctionLabelStatus = App_Func_CreateLabel(
|
|
|
|
|
p_Card,
|
|
|
|
|
QStringLiteral("Waiting for function-key activity."),
|
|
|
|
|
APP_Theme::App_Func_GetBodyFont(),
|
|
|
|
|
true);
|
|
|
|
|
AddRow(p_Form, QStringLiteral("Latest Action"), appFunctionLabelStatus);
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Layout->addLayout(p_Form);
|
|
|
|
|
p_Layout->addStretch(1);
|
2026-03-26 10:45:29 +08:00
|
|
|
return p_Card;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
QWidget* App_UIWindow::App_Func_CreatePacketTestCard()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
|
|
|
|
QVBoxLayout* p_Layout = nullptr;
|
|
|
|
|
APP_GlassCard* p_Card = App_Func_CreateCard(this, &p_Layout);
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Layout->addWidget(
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Device Packet Test"), APP_Theme::App_Func_GetMetricFont()));
|
|
|
|
|
p_Layout->addWidget(App_Func_CreateLabel(
|
|
|
|
|
p_Card,
|
|
|
|
|
QStringLiteral(
|
|
|
|
|
"Use this page to drive the real device. Choose Auto / USB CDC / BLE NUS first, "
|
|
|
|
|
"then send packets and watch the rolling log for HelloRsp / FunctionKeyEvent / "
|
|
|
|
|
"LedState / Ack / Error."),
|
|
|
|
|
APP_Theme::App_Func_GetBodyFont(),
|
|
|
|
|
true));
|
|
|
|
|
|
|
|
|
|
QHBoxLayout* p_TargetRow = new QHBoxLayout();
|
|
|
|
|
p_TargetRow->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
p_TargetRow->setSpacing(8);
|
|
|
|
|
p_TargetRow->addWidget(
|
|
|
|
|
App_Func_CreateLabel(
|
|
|
|
|
p_Card,
|
|
|
|
|
QStringLiteral("Target Transport"),
|
|
|
|
|
APP_Theme::App_Func_GetBodyFont()));
|
|
|
|
|
appPacketTestTargetCombo = new QComboBox(p_Card);
|
|
|
|
|
appPacketTestTargetCombo->addItem(
|
|
|
|
|
QStringLiteral("Auto (ready transport)"),
|
|
|
|
|
static_cast<int>(Com_Enum_RawPacketSource_None));
|
|
|
|
|
appPacketTestTargetCombo->addItem(
|
|
|
|
|
QStringLiteral("USB CDC only"),
|
|
|
|
|
static_cast<int>(Com_Enum_RawPacketSource_UsbCdc));
|
|
|
|
|
appPacketTestTargetCombo->addItem(
|
|
|
|
|
QStringLiteral("BLE NUS only"),
|
|
|
|
|
static_cast<int>(Com_Enum_RawPacketSource_BleNus));
|
|
|
|
|
p_TargetRow->addWidget(appPacketTestTargetCombo, 1);
|
|
|
|
|
p_Layout->addLayout(p_TargetRow);
|
|
|
|
|
|
|
|
|
|
QGridLayout* p_ButtonGrid = new QGridLayout();
|
|
|
|
|
p_ButtonGrid->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
p_ButtonGrid->setHorizontalSpacing(10);
|
|
|
|
|
p_ButtonGrid->setVerticalSpacing(10);
|
|
|
|
|
appPacketTestRefreshButton = new QPushButton(QStringLiteral("Refresh Device"), p_Card);
|
|
|
|
|
appPacketTestSendHelloButton = new QPushButton(QStringLiteral("Send HelloReq"), p_Card);
|
|
|
|
|
appPacketTestSendBitmapCurrentButton = new QPushButton(QStringLiteral("Send Bitmap(Config)"), p_Card);
|
|
|
|
|
appPacketTestSendBitmapAllOnButton = new QPushButton(QStringLiteral("Send Bitmap(All On)"), p_Card);
|
|
|
|
|
appPacketTestSendBitmapAllOffButton = new QPushButton(QStringLiteral("Send Bitmap(All Off)"), p_Card);
|
|
|
|
|
appPacketTestSendTimeSyncButton = new QPushButton(QStringLiteral("Send TimeSync"), p_Card);
|
|
|
|
|
p_ButtonGrid->addWidget(appPacketTestRefreshButton, 0, 0);
|
|
|
|
|
p_ButtonGrid->addWidget(appPacketTestSendHelloButton, 0, 1);
|
|
|
|
|
p_ButtonGrid->addWidget(appPacketTestSendTimeSyncButton, 0, 2);
|
|
|
|
|
p_ButtonGrid->addWidget(appPacketTestSendBitmapCurrentButton, 1, 0);
|
|
|
|
|
p_ButtonGrid->addWidget(appPacketTestSendBitmapAllOnButton, 1, 1);
|
|
|
|
|
p_ButtonGrid->addWidget(appPacketTestSendBitmapAllOffButton, 1, 2);
|
|
|
|
|
p_ButtonGrid->setColumnStretch(0, 1);
|
|
|
|
|
p_ButtonGrid->setColumnStretch(1, 1);
|
|
|
|
|
p_ButtonGrid->setColumnStretch(2, 1);
|
|
|
|
|
p_Layout->addLayout(p_ButtonGrid);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout* p_ThemeRow = new QHBoxLayout();
|
|
|
|
|
p_ThemeRow->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
p_ThemeRow->setSpacing(8);
|
|
|
|
|
p_ThemeRow->addWidget(
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Theme RGB"), APP_Theme::App_Func_GetBodyFont()));
|
|
|
|
|
|
|
|
|
|
const auto CreateSpin = [p_Card](int Value)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
QSpinBox* p_Spin = new QSpinBox(p_Card);
|
|
|
|
|
p_Spin->setRange(0, 255);
|
|
|
|
|
p_Spin->setValue(Value);
|
|
|
|
|
p_Spin->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
|
|
|
|
p_Spin->setAlignment(Qt::AlignCenter);
|
|
|
|
|
p_Spin->setFixedWidth(56);
|
|
|
|
|
return p_Spin;
|
2026-03-26 10:45:29 +08:00
|
|
|
};
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
appPacketTestThemeRedSpin = CreateSpin(247);
|
|
|
|
|
appPacketTestThemeGreenSpin = CreateSpin(37);
|
|
|
|
|
appPacketTestThemeBlueSpin = CreateSpin(133);
|
|
|
|
|
appPacketTestSendThemeButton = new QPushButton(QStringLiteral("Send ThemeRgb"), p_Card);
|
|
|
|
|
appPacketTestClearLogButton = new QPushButton(QStringLiteral("Clear Log"), p_Card);
|
|
|
|
|
p_ThemeRow->addWidget(appPacketTestThemeRedSpin);
|
|
|
|
|
p_ThemeRow->addWidget(appPacketTestThemeGreenSpin);
|
|
|
|
|
p_ThemeRow->addWidget(appPacketTestThemeBlueSpin);
|
|
|
|
|
p_ThemeRow->addWidget(appPacketTestSendThemeButton);
|
|
|
|
|
p_ThemeRow->addWidget(appPacketTestClearLogButton);
|
|
|
|
|
p_ThemeRow->addStretch(1);
|
|
|
|
|
p_Layout->addLayout(p_ThemeRow);
|
|
|
|
|
|
|
|
|
|
appPacketTestTransportLabel =
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Transport: -"), APP_Theme::App_Func_GetBodyFont(), true);
|
|
|
|
|
appPacketTestProtocolLabel =
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Protocol: -"), APP_Theme::App_Func_GetBodyFont(), true);
|
|
|
|
|
appPacketTestHelloLabel =
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("HelloRsp: -"), APP_Theme::App_Func_GetBodyFont(), true);
|
|
|
|
|
appPacketTestAckLabel =
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Ack: -"), APP_Theme::App_Func_GetBodyFont(), true);
|
|
|
|
|
appPacketTestErrorLabel =
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Error: -"), APP_Theme::App_Func_GetBodyFont(), true);
|
|
|
|
|
appPacketTestBitmapLabel =
|
|
|
|
|
App_Func_CreateLabel(p_Card, QStringLiteral("Bitmaps: -"), APP_Theme::App_Func_GetBodyFont(), true);
|
|
|
|
|
p_Layout->addWidget(appPacketTestTransportLabel);
|
|
|
|
|
p_Layout->addWidget(appPacketTestProtocolLabel);
|
|
|
|
|
p_Layout->addWidget(appPacketTestHelloLabel);
|
|
|
|
|
p_Layout->addWidget(appPacketTestAckLabel);
|
|
|
|
|
p_Layout->addWidget(appPacketTestErrorLabel);
|
|
|
|
|
p_Layout->addWidget(appPacketTestBitmapLabel);
|
|
|
|
|
|
|
|
|
|
appPacketTestTable = new QTableWidget(App_Enum_PacketTestRow_Count, 3, p_Card);
|
|
|
|
|
appPacketTestTable->setFont(App_Func_GetMonoFont());
|
|
|
|
|
appPacketTestTable->setHorizontalHeaderLabels(
|
|
|
|
|
{ QStringLiteral("Packet"), QStringLiteral("Direction"), QStringLiteral("Count") });
|
|
|
|
|
appPacketTestTable->verticalHeader()->setVisible(false);
|
|
|
|
|
appPacketTestTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
|
|
|
appPacketTestTable->setSelectionMode(QAbstractItemView::NoSelection);
|
|
|
|
|
appPacketTestTable->setFocusPolicy(Qt::NoFocus);
|
|
|
|
|
appPacketTestTable->horizontalHeader()->setStretchLastSection(false);
|
|
|
|
|
appPacketTestTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
|
|
|
|
appPacketTestTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
|
|
|
|
appPacketTestTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
|
|
|
|
|
appPacketTestTable->setMinimumHeight(220);
|
|
|
|
|
|
|
|
|
|
const QStringList PacketNames = {
|
|
|
|
|
QStringLiteral("HelloReq"),
|
|
|
|
|
QStringLiteral("HelloRsp"),
|
|
|
|
|
QStringLiteral("Bitmap"),
|
|
|
|
|
QStringLiteral("FunctionKeyEvent"),
|
|
|
|
|
QStringLiteral("LedState"),
|
|
|
|
|
QStringLiteral("TimeSync"),
|
|
|
|
|
QStringLiteral("ThemeRgb"),
|
|
|
|
|
QStringLiteral("Ack"),
|
|
|
|
|
QStringLiteral("Error")
|
|
|
|
|
};
|
|
|
|
|
const QStringList Directions = {
|
|
|
|
|
QStringLiteral("Host -> Device"),
|
|
|
|
|
QStringLiteral("Device -> Host"),
|
|
|
|
|
QStringLiteral("Host -> Device"),
|
|
|
|
|
QStringLiteral("Device -> Host"),
|
|
|
|
|
QStringLiteral("Device -> Host"),
|
|
|
|
|
QStringLiteral("Host -> Device"),
|
|
|
|
|
QStringLiteral("Host -> Device"),
|
|
|
|
|
QStringLiteral("Device -> Host"),
|
|
|
|
|
QStringLiteral("Device -> Host")
|
|
|
|
|
};
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
for (int Row = 0; Row < App_Enum_PacketTestRow_Count; ++Row)
|
|
|
|
|
{
|
|
|
|
|
appPacketTestTable->setItem(Row, 0, new QTableWidgetItem(PacketNames.at(Row)));
|
|
|
|
|
appPacketTestTable->setItem(Row, 1, new QTableWidgetItem(Directions.at(Row)));
|
|
|
|
|
appPacketTestTable->setItem(Row, 2, new QTableWidgetItem(QStringLiteral("0")));
|
|
|
|
|
}
|
|
|
|
|
p_Layout->addWidget(appPacketTestTable);
|
|
|
|
|
|
|
|
|
|
appPacketTestLogEdit = new QPlainTextEdit(p_Card);
|
|
|
|
|
appPacketTestLogEdit->setFont(App_Func_GetMonoFont());
|
|
|
|
|
appPacketTestLogEdit->setReadOnly(true);
|
|
|
|
|
appPacketTestLogEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
|
|
|
|
appPacketTestLogEdit->setMinimumHeight(190);
|
|
|
|
|
p_Layout->addWidget(appPacketTestLogEdit, 1);
|
|
|
|
|
return p_Card;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_UIWindow::App_Func_RefreshPacketTestTable()
|
|
|
|
|
{
|
|
|
|
|
if (appPacketTestTable == nullptr)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
return;
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
const Lgc_Core_Struct_TestView TestView = Lgc_Core_GetTestView(&appLgcState);
|
|
|
|
|
const int Counts[App_Enum_PacketTestRow_Count] = {
|
|
|
|
|
TestView.TxHelloReqCount,
|
|
|
|
|
TestView.RxHelloRspCount,
|
|
|
|
|
TestView.TxBitmapCount,
|
|
|
|
|
TestView.RxFunctionKeyEventCount,
|
|
|
|
|
TestView.RxLedStateCount,
|
|
|
|
|
TestView.TxTimeSyncCount,
|
|
|
|
|
TestView.TxThemeRgbCount,
|
|
|
|
|
TestView.RxAckCount,
|
|
|
|
|
TestView.RxErrorCount
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (int Row = 0; Row < App_Enum_PacketTestRow_Count; ++Row)
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
if (QTableWidgetItem* p_Item = appPacketTestTable->item(Row, 2))
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
p_Item->setText(QString::number(Counts[Row]));
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
2026-04-17 16:25:19 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_UIWindow::App_Func_RefreshPacketTestPanel()
|
|
|
|
|
{
|
|
|
|
|
if (appPacketTestTransportLabel == nullptr)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
const Lgc_Core_Struct_TestView TestView = Lgc_Core_GetTestView(&appLgcState);
|
|
|
|
|
const QString TargetText =
|
|
|
|
|
App_Func_GetPacketTestTargetText(App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
|
|
|
|
|
appPacketTestTransportLabel->setText(
|
|
|
|
|
QStringLiteral("Transport\nTarget: %1\nUSB: Open=%2 | Port=%3\nBLE: Open=%4 | Connected=%5 | Endpoint=%6")
|
|
|
|
|
.arg(TargetText)
|
|
|
|
|
.arg(App_Func_BoolText(TestView.IsUsbOpened))
|
|
|
|
|
.arg(TestView.UsbPortName.isEmpty() ? QStringLiteral("-") : TestView.UsbPortName)
|
|
|
|
|
.arg(App_Func_BoolText(TestView.IsNusOpened))
|
|
|
|
|
.arg(App_Func_BoolText(TestView.IsNusConnected))
|
|
|
|
|
.arg(TestView.NusEndpointSummary.isEmpty()
|
|
|
|
|
? QStringLiteral("-")
|
|
|
|
|
: TestView.NusEndpointSummary));
|
|
|
|
|
appPacketTestProtocolLabel->setText(
|
|
|
|
|
QStringLiteral("Protocol\nReady: Device=%1 | USB=%2 | BLE=%3\nPending: USB=0x%4 | BLE=0x%5\nStatus: %6")
|
|
|
|
|
.arg(App_Func_BoolText(TestView.DeviceReady))
|
|
|
|
|
.arg(App_Func_BoolText(TestView.IsUsbProtocolReady))
|
|
|
|
|
.arg(App_Func_BoolText(TestView.IsNusProtocolReady))
|
|
|
|
|
.arg(QString::number(TestView.PendingUsbCommandBits, 16).toUpper())
|
|
|
|
|
.arg(QString::number(TestView.PendingNusCommandBits, 16).toUpper())
|
|
|
|
|
.arg(TestView.StatusText.isEmpty() ? QStringLiteral("-") : TestView.StatusText));
|
|
|
|
|
appPacketTestHelloLabel->setText(
|
|
|
|
|
QStringLiteral("HelloRsp\nVersion: v%1 | FW %2.%3\nVID/PID: %4 | Caps: 0x%5")
|
|
|
|
|
.arg(TestView.HelloProtocolVersion)
|
|
|
|
|
.arg(TestView.HelloFirmwareMajor)
|
|
|
|
|
.arg(TestView.HelloFirmwareMinor)
|
|
|
|
|
.arg(App_Func_FormatVidPid(TestView.HelloVendorId, TestView.HelloProductId))
|
|
|
|
|
.arg(QString::number(TestView.HelloCapabilityFlags, 16).toUpper()));
|
|
|
|
|
appPacketTestAckLabel->setText(
|
|
|
|
|
QStringLiteral("Ack\nLastAck: %1\nLastTx: %2\nPayload: %3")
|
|
|
|
|
.arg(App_Func_FormatPacketType(TestView.LastAckedType))
|
|
|
|
|
.arg(TestView.LastTxSummary.isEmpty() ? QStringLiteral("-") : TestView.LastTxSummary)
|
|
|
|
|
.arg(TestView.LastTxHex));
|
|
|
|
|
appPacketTestErrorLabel->setText(
|
|
|
|
|
QStringLiteral("Error\nType: %1 | Code: %2\nLastRx: %3\nPayload: %4")
|
|
|
|
|
.arg(App_Func_FormatPacketType(TestView.LastErrorType))
|
|
|
|
|
.arg(TestView.LastErrorCode)
|
|
|
|
|
.arg(TestView.LastRxSummary.isEmpty() ? QStringLiteral("-") : TestView.LastRxSummary)
|
|
|
|
|
.arg(TestView.LastRxHex));
|
|
|
|
|
appPacketTestBitmapLabel->setText(
|
|
|
|
|
QStringLiteral("Bitmap\nCurrent: %1\nLastEvent: %2\nLED Mask: 0x%3")
|
|
|
|
|
.arg(TestView.FunctionMaskHex)
|
|
|
|
|
.arg(TestView.LastFunctionEventHex)
|
|
|
|
|
.arg(QString::number(TestView.DeviceLedMask, 16).toUpper()));
|
|
|
|
|
|
|
|
|
|
App_Func_RefreshPacketTestTable();
|
|
|
|
|
if (appPacketTestLogEdit->toPlainText() != TestView.LogText)
|
|
|
|
|
{
|
|
|
|
|
appPacketTestLogEdit->setPlainText(TestView.LogText);
|
|
|
|
|
appPacketTestLogEdit->verticalScrollBar()->setValue(
|
|
|
|
|
appPacketTestLogEdit->verticalScrollBar()->maximum());
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-26 10:45:29 +08:00
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_UIWindow::App_Func_RefreshUi()
|
|
|
|
|
{
|
|
|
|
|
App_Func_RefreshKeypadState();
|
|
|
|
|
App_Func_RefreshKeypadButtons();
|
|
|
|
|
App_Func_RefreshFunctionStatus();
|
|
|
|
|
App_Func_RefreshPacketTestPanel();
|
|
|
|
|
update();
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 16:25:19 +08:00
|
|
|
void App_UIWindow::App_Func_RefreshAfterLogicChange()
|
2026-03-26 10:45:29 +08:00
|
|
|
{
|
2026-04-17 16:25:19 +08:00
|
|
|
App_Func_RefreshUi();
|
2026-03-26 10:45:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace APP
|