#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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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(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(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(&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(&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(appPacketTestThemeRedSpin->value()), static_cast(appPacketTestThemeGreenSpin->value()), static_cast(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(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(Lgc_FunctionFeature_Type::KeyCombination)); appFeatureTypeCombo->addItem( Lgc_Core_GetFeatureTypeText(Lgc_FunctionFeature_Type::KeySequence), static_cast(Lgc_FunctionFeature_Type::KeySequence)); appFeatureTypeCombo->addItem( Lgc_Core_GetFeatureTypeText(Lgc_FunctionFeature_Type::Website), static_cast(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(Com_Enum_RawPacketSource_None)); appPacketTestTargetCombo->addItem( QStringLiteral("USB CDC only"), static_cast(Com_Enum_RawPacketSource_UsbCdc)); appPacketTestTargetCombo->addItem( QStringLiteral("BLE NUS only"), static_cast(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