Files
0417_QT_code/APP/APP_UIWindow.cpp

871 lines
33 KiB
C++

#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 <QtGui/QFontDatabase>
#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/QPlainTextEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QSizePolicy>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QStackedWidget>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QTableWidget>
#include <QtWidgets/QTableWidgetItem>
#include <QtWidgets/QVBoxLayout>
#include <Windows.h>
namespace APP {
namespace
{
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
};
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);
}
}
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;
}
QString App_Func_BoolText(bool Value)
{
return Value ? QStringLiteral("Yes") : QStringLiteral("No");
}
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());
}
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)");
}
}
QString App_Func_FormatPacketType(quint32 RawType)
{
switch (static_cast<Com_Enum_ProtocolType>(RawType & 0xFFU))
{
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();
}
}
QString App_Func_FormatVidPid(quint32 VendorId, quint32 ProductId)
{
return QStringLiteral("%1:%2")
.arg(VendorId, 4, 16, QLatin1Char('0'))
.arg(ProductId, 4, 16, QLatin1Char('0'))
.toUpper();
}
QFont App_Func_GetMonoFont()
{
QFont Font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
Font.setPointSize(9);
return Font;
}
} // 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_RefreshKeypadButtons();
App_Func_RefreshFunctionStatus();
App_Func_RefreshPacketTestPanel();
update();
}
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("Keyboard Control"));
setMinimumSize(760, 820);
resize(820, 900);
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("Keypad"));
appFeaturePageIndex =
appPageTab->addTab(App_Func_CreateFunctionConfigCard(), QStringLiteral("Functions"));
appPacketTestPageIndex =
appPageTab->addTab(App_Func_CreatePacketTestCard(), QStringLiteral("Device Test"));
p_RootLayout->addWidget(appPageTab, 1);
}
void App_UIWindow::App_Func_InitConnect()
{
connect(&appTimerPoll, &QTimer::timeout, this, [this]()
{
if (Lgc_Core_Poll(&appLgcState))
{
App_Func_RefreshAfterLogicChange();
}
});
connect(&appTimerAutoRefreshDevice, &QTimer::timeout, this, [this]()
{
const Lgc_Core_Struct_View View = Lgc_Core_GetView(&appLgcState);
if (View.IsConnected || View.HasOpenTransport)
{
return;
}
Lgc_Core_RefreshDevice(&appLgcState);
App_Func_RefreshKeypadState();
App_Func_RefreshKeypadButtons();
App_Func_RefreshFunctionStatus();
App_Func_RefreshPacketTestPanel();
update();
});
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();
});
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();
App_Func_SelectFeature(appFeatureTable->item(Row, 0)->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);
connect(appPacketTestRefreshButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_RefreshDevice(&appLgcState);
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestTargetCombo, qOverload<int>(&QComboBox::currentIndexChanged), this, [this]()
{
App_Func_RefreshPacketTestPanel();
});
connect(appPacketTestSendHelloButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_TestSendHello(
&appLgcState,
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestSendBitmapCurrentButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_TestSendBitmapCurrentConfig(
&appLgcState,
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestSendBitmapAllOnButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_TestSendBitmapAllEnabled(
&appLgcState,
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestSendBitmapAllOffButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_TestSendBitmapAllDisabled(
&appLgcState,
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestSendTimeSyncButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_TestSendTimeSync(
&appLgcState,
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestSendThemeButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_TestSendThemeRgb(
&appLgcState,
static_cast<quint8>(appPacketTestThemeRedSpin->value()),
static_cast<quint8>(appPacketTestThemeGreenSpin->value()),
static_cast<quint8>(appPacketTestThemeBlueSpin->value()),
App_Func_GetPacketTestTargetSource(appPacketTestTargetCombo));
App_Func_RefreshAfterLogicChange();
});
connect(appPacketTestClearLogButton, &QPushButton::clicked, this, [this]()
{
Lgc_Core_ClearTestLog(&appLgcState);
App_Func_RefreshAfterLogicChange();
});
}
void App_UIWindow::App_Func_InitLogic()
{
Lgc_Core_Init(&appLgcState);
const WId WindowId = winId();
Lgc_Core_SetWindowHandle(&appLgcState, reinterpret_cast<void*>(WindowId));
if (!App_Func_RegisterRawInputWindow(WindowId))
{
Lgc_Core_SetStatusText(&appLgcState, QStringLiteral("Raw keyboard capture init failed."));
}
Lgc_Core_LoadFunctionConfig(&appLgcState);
App_Func_RefreshFeatureTable();
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();
}
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("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));
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]()
{
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)));
});
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("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);
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);
p_Layout->addLayout(p_Form);
p_Layout->addStretch(1);
return p_Card;
}
QWidget* App_UIWindow::App_Func_CreatePacketTestCard()
{
QVBoxLayout* p_Layout = nullptr;
APP_GlassCard* p_Card = App_Func_CreateCard(this, &p_Layout);
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)
{
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;
};
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")
};
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;
}
void App_UIWindow::App_Func_RefreshPacketTestTable()
{
if (appPacketTestTable == nullptr)
{
return;
}
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)
{
if (QTableWidgetItem* p_Item = appPacketTestTable->item(Row, 2))
{
p_Item->setText(QString::number(Counts[Row]));
}
}
}
void App_UIWindow::App_Func_RefreshPacketTestPanel()
{
if (appPacketTestTransportLabel == nullptr)
{
return;
}
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());
}
}
void App_UIWindow::App_Func_RefreshUi()
{
App_Func_RefreshKeypadState();
App_Func_RefreshKeypadButtons();
App_Func_RefreshFunctionStatus();
App_Func_RefreshPacketTestPanel();
update();
}
void App_UIWindow::App_Func_RefreshAfterLogicChange()
{
App_Func_RefreshUi();
}
} // namespace APP