// // Created by Lenn on 2025/11/21. // #include "component.hh" #include "base/globalhelper.hh" #include "creeper-qt/utility/theme/theme.hh" #include "creeper-qt/utility/wrapper/layout.hh" #include "creeper-qt/utility/wrapper/widget.hh" #include "creeper-qt/layout/flow.hh" #include "creeper-qt/layout/linear.hh" #include "creeper-qt/layout/scroll.hh" #include "creeper-qt/widget/buttons/filled-button.hh" #include "creeper-qt/widget/cards/basic-card.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "globalhelper.hh" #include #include #include #include #include #include #include #include #include #include namespace repest_literals { template concept IndexInvocable = std::invocable; template void operator*(F&& f, std::size_t n) { std::ranges::for_each(std::views::iota(std::size_t{ 0 }, n), std::forward(f)); } template void operator*(std::size_t n, F&& f) { std::ranges::for_each(std::views::iota(std::size_t{ 0 }, n), std::forward(f)); } } // namespace repest_literals using namespace creeper; namespace capro = card::pro; namespace lnpro = linear::pro; namespace ibpro = icon_button::pro; namespace fbpro = filled_button::pro; static std::weak_ptr>> g_profiles_store; static std::function g_profiles_refresh; static void ShowEditProfileDialog( const ConfigProfile& current, const std::shared_ptr>>& profiles_store) { QDialog dialog; dialog.setWindowTitle(QStringLiteral("修改配置")); auto* layout = new QVBoxLayout(&dialog); auto* form = new QFormLayout(); auto* name_edit = new QLineEdit(&dialog); name_edit->setText(current.name); auto* type_combo = new QComboBox(&dialog); type_combo->addItem(QStringLiteral("压阻A型")); type_combo->addItem(QStringLiteral("压阻B型")); type_combo->addItem(QStringLiteral("霍尔型")); switch (current.type) { case Tactile_TYPE::PiezoresistiveA: type_combo->setCurrentIndex(0); break; case Tactile_TYPE::PiezoresistiveB: type_combo->setCurrentIndex(1); break; case Tactile_TYPE::Hall: type_combo->setCurrentIndex(2); break; } auto* width_spin = new QSpinBox(&dialog); auto* height_spin = new QSpinBox(&dialog); width_spin->setRange(1, 64); height_spin->setRange(1, 64); width_spin->setValue(current.matrix_width); height_spin->setValue(current.matrix_height); auto* left_spin = new QSpinBox(&dialog); auto* right_spin = new QSpinBox(&dialog); left_spin->setRange(-100000, 100000); right_spin->setRange(-100000, 100000); left_spin->setValue(current.range_left); right_spin->setValue(current.range_right); auto* baud_spin = new QSpinBox(&dialog); baud_spin->setRange(1200, 10000000); baud_spin->setValue(current.baud_rate); form->addRow(QStringLiteral("名称"), name_edit); form->addRow(QStringLiteral("类型"), type_combo); form->addRow(QStringLiteral("宽"), width_spin); form->addRow(QStringLiteral("高"), height_spin); form->addRow(QStringLiteral("量程左"), left_spin); form->addRow(QStringLiteral("量程右"), right_spin); form->addRow(QStringLiteral("波特率"), baud_spin); layout->addLayout(form); auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); layout->addWidget(buttons); QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); if (dialog.exec() == QDialog::Accepted) { ConfigProfile updated = current; updated.name = name_edit->text(); updated.type = [idx = type_combo->currentIndex()] { switch (idx) { case 0: return Tactile_TYPE::PiezoresistiveA; case 1: return Tactile_TYPE::PiezoresistiveB; default: return Tactile_TYPE::Hall; } }(); updated.matrix_width = width_spin->value(); updated.matrix_height = height_spin->value(); updated.range_left = left_spin->value(); updated.range_right = right_spin->value(); updated.baud_rate = baud_spin->value(); GlobalHelper::instance().remove_profile(current.name); GlobalHelper::instance().add_new_profile(updated); GlobalHelper::instance().reload_profiles(); if (profiles_store) { profiles_store->set(GlobalHelper::instance().get_all_profile()); } RefreshProfilesForView(); if (g_profiles_refresh) { g_profiles_refresh(); } } } static void ShowAddProfileDialog() { QDialog dialog; dialog.setWindowTitle(QStringLiteral("添加配置")); auto* layout = new QVBoxLayout(&dialog); auto* form = new QFormLayout(); auto* name_edit = new QLineEdit(&dialog); auto* type_combo = new QComboBox(&dialog); type_combo->addItem(QStringLiteral("压阻A型")); type_combo->addItem(QStringLiteral("压阻B型")); type_combo->addItem(QStringLiteral("霍尔型")); auto* width_spin = new QSpinBox(&dialog); auto* height_spin = new QSpinBox(&dialog); width_spin->setRange(1, 64); height_spin->setRange(1, 64); auto* left_spin = new QSpinBox(&dialog); auto* right_spin = new QSpinBox(&dialog); left_spin->setRange(-100000, 100000); right_spin->setRange(-100000, 100000); auto* baud_spin = new QSpinBox(&dialog); baud_spin->setRange(1200, 10000000); baud_spin->setValue(115200); form->addRow(QStringLiteral("名称"), name_edit); form->addRow(QStringLiteral("类型"), type_combo); form->addRow(QStringLiteral("宽"), width_spin); form->addRow(QStringLiteral("高"), height_spin); form->addRow(QStringLiteral("量程左"), left_spin); form->addRow(QStringLiteral("量程右"), right_spin); form->addRow(QStringLiteral("波特率"), baud_spin); layout->addLayout(form); auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); layout->addWidget(buttons); QObject::connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); if (dialog.exec() == QDialog::Accepted) { ConfigProfile profile; profile.name = name_edit->text(); profile.type = [idx = type_combo->currentIndex()] { switch (idx) { case 0: return Tactile_TYPE::PiezoresistiveA; case 1: return Tactile_TYPE::PiezoresistiveB; default: return Tactile_TYPE::Hall; } }(); profile.matrix_width = width_spin->value(); profile.matrix_height = height_spin->value(); profile.range_left = left_spin->value(); profile.range_right = right_spin->value(); profile.baud_rate = baud_spin->value(); GlobalHelper::instance().add_new_profile(profile); GlobalHelper::instance().reload_profiles(); if (auto store = g_profiles_store.lock()) { store->set(GlobalHelper::instance().get_all_profile()); } RefreshProfilesForView(); if (g_profiles_refresh) { g_profiles_refresh(); } } } static auto AddProfileLongItem(creeper::ThemeManager& manager) { return new FilledButton { fbpro::ThemeManager {manager}, fbpro::Text {QStringLiteral("添加配置")}, widget::pro::SizePolicy {QSizePolicy::Fixed, QSizePolicy::Expanding}, widget::pro::MinimumHeight {40}, widget::pro::MinimumWidth {320}, fbpro::Radius {12}, fbpro::Clickable {[]{ ShowAddProfileDialog(); }}, }; } static auto ProfileItemComponent(creeper::ThemeManager& manager, ConfigProfile& profile, const std::shared_ptr>>& profiles_store) { QString matrix_size = "规格:" + QString{ "%1 * %2" }.arg(profile.matrix_width).arg(profile.matrix_height); QString value_range = "量程:" + QString{ "%1 ~ %2" }.arg(profile.range_left).arg(profile.range_right); QString tactile_type = [profile]() -> QString { switch (profile.type) { case Tactile_TYPE::PiezoresistiveA: return "类型:压阻A型"; case Tactile_TYPE::PiezoresistiveB: return "类型:压阻B型"; case Tactile_TYPE::Hall: return "类型:霍尔型"; } return "错误"; }(); QString baud_rate = "波特率:" + QString::number(profile.baud_rate); return new FilledCard{ card::pro::SizePolicy { QSizePolicy::Expanding, }, card::pro::MinimumSize { 300, 50 }, // card::pro::FixedSize { // 200, 100 // }, card::pro::ThemeManager{ manager }, card::pro::SizePolicy { QSizePolicy::Expanding }, card::pro::LevelLow, card::pro::Layout{ lnpro::Item{ // row::pro::Stretch {1}, row::pro::Item{ text::pro::AdjustSize {}, text::pro::Text{ QString{ profile.name } }, text::pro::Apply{ [&manager](Text& self) { manager.append_handler(&self, [&](const ThemeManager& manager) { const auto scheme = manager.color_scheme(); self.set_color(scheme.primary); }); } }, }, row::pro::Item{ ibpro::ThemeManager{ manager }, ibpro::FixedSize{ 30, 30 }, ibpro::Font{ material::kRegularExtraSmallFont }, icon_button::pro::FontIcon{ material::icon::kBorderColor }, ibpro::Clickable{ [profiles_store, current = profile] { ShowEditProfileDialog(current, profiles_store); } }, }, row::pro::Item{ ibpro::ThemeManager{ manager }, ibpro::FixedSize{ 30, 30 }, ibpro::Font{ material::kRegularExtraSmallFont }, icon_button::pro::FontIcon{ material::icon::kDelete }, ibpro::Clickable{ [profiles_store, name = profile.name, &manager] { GlobalHelper::instance().remove_profile(name); GlobalHelper::instance().reload_profiles(); if (profiles_store) { profiles_store->set(GlobalHelper::instance().get_all_profile()); } RefreshProfilesForView(); manager.apply_theme(); } }, } }, lnpro::Item{ text::pro::Text{ tactile_type }, text::pro::Apply{ [&manager](Text& self) { manager.append_handler(&self, [&](const ThemeManager& manager) { const auto scheme = manager.color_scheme(); self.set_color(scheme.primary); }); } } }, lnpro::Item{ text::pro::AdjustSize {}, text::pro::Text{ matrix_size }, text::pro::Apply{ [&manager](Text& self) { manager.append_handler(&self, [&](const ThemeManager& manager) { const auto scheme = manager.color_scheme(); self.set_color(scheme.primary); }); } } }, lnpro::Item{ text::pro::Text{ value_range }, text::pro::Apply{ [&manager](Text& self) { manager.append_handler(&self, [&](const ThemeManager& manager) { const auto scheme = manager.color_scheme(); self.set_color(scheme.primary); }); } } }, lnpro::Item{ text::pro::Text{ baud_rate }, text::pro::Apply{ [&manager](Text& self) { manager.append_handler(&self, [&](const ThemeManager& manager) { const auto scheme = manager.color_scheme(); self.set_color(scheme.primary); }); } } }, }, }; } static void PopulateProfiles(Flow& flow, creeper::ThemeManager& manager, const std::vector& profiles, const std::shared_ptr>>& profiles_store) { while (auto item = flow.takeAt(0)) { if (auto* w = item->widget()) { w->deleteLater(); } delete item; } using namespace repest_literals; profiles.size() * [&](auto i) { flow.addWidget(ProfileItemComponent(manager, const_cast(profiles[i]), profiles_store)); }; flow.update(); } static void AttachProfilesObserver(const std::shared_ptr>>& store, Flow& flow, creeper::ThemeManager& manager) { struct Functor : creeper::MutableValue>::Functor { Flow& flow; creeper::ThemeManager& manager; std::weak_ptr>> store_ptr; Functor(Flow& f, creeper::ThemeManager& m, std::weak_ptr>> s) noexcept : flow(f) , manager(m) , store_ptr(std::move(s)) { } void update(const std::vector& value) override { PopulateProfiles(flow, manager, value, store_ptr.lock()); manager.apply_theme(); } }; auto functor = std::make_unique(flow, manager, store); store->callbacks[&flow] = std::move(functor); auto alive = std::weak_ptr { store->alive }; QObject::connect(&flow, &QObject::destroyed, [store, alive](auto* key) { if (alive.lock()) store->callbacks.erase(key); }); } auto SettingComponent(SettingComponentState& state) noexcept -> raw_pointer { auto profiles_ctx = std::make_shared>>( GlobalHelper::instance().get_all_profile() ); g_profiles_store = profiles_ctx; g_profiles_refresh = [profiles_ctx]() { if (auto store = profiles_ctx) { store->set(GlobalHelper::instance().get_all_profile()); } }; auto* profiles_flow = new Flow { flow::pro::RowSpacing{ 10 }, flow::pro::ColSpacing{ 10 }, flow::pro::RowLimit{ 10 }, }; PopulateProfiles(*profiles_flow, state.manager, profiles_ctx->get(), profiles_ctx); AttachProfilesObserver(profiles_ctx, *profiles_flow, state.manager); return new FilledCard { capro::ThemeManager{ state.manager }, capro::SizePolicy{ QSizePolicy::Expanding }, capro::Layout{ lnpro::Alignment {Qt::AlignTop}, lnpro::Margin {10}, lnpro::Spacing {10}, lnpro::Item{ AddProfileLongItem(state.manager) }, col::pro::Item{ scroll::pro::ThemeManager { state.manager }, scroll::pro::HorizontalScrollBarPolicy { Qt::ScrollBarAlwaysOff }, scroll::pro::Item { profiles_flow }, }, }, }; }