feat:完成设置界面和主界面的参数配置

This commit is contained in:
2025-11-25 15:58:04 +08:00
parent b2350a3b35
commit 0ec07218ab
192 changed files with 1404 additions and 56345 deletions

View File

@@ -5,12 +5,12 @@
#ifndef TOUCHSENSOR_HEATMAP_H
#define TOUCHSENSOR_HEATMAP_H
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/pimpl.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/utility/wrapper/common.hh"
#include "creeper-qt/utility/wrapper/pimpl.hh"
#include "creeper-qt/utility/wrapper/property.hh"
#include "qcustomplot/qcustomplot.h"
#include "modern-qt/utility/wrapper/widget.hh"
#include "creeper-qt/utility/wrapper/widget.hh"
#include <concepts>
#include <qcontainerfwd.h>
#include <qvector.h>
@@ -82,10 +82,13 @@ namespace plot_widget::pro {
}
};
using Data = common::pro::Vector<Token, PointData,
[](auto& self, const auto& data) {
self.set_data(data);
}>;
// using Data = common::pro::Vector<Token, PointData,
// [](auto& self, const auto& data) {
// self.set_data(data);
// }>;
using Data = DerivedProp<Token, QVector<QString>, [](auto& self, const auto& data) {
self.set_data(data);
}>;
struct ColorRange : Token {
double min;
@@ -107,11 +110,15 @@ namespace plot_widget::pro {
template<class PlotWidget>
concept trait = std::derived_from<PlotWidget, Token>;
using PlotData = common::pro::Vector<Token, PointData, [](auto& self, const auto& vec) {
self.set_data(vec);
}>;
// using PlotData = common::pro::Vector<Token, PointData, [](auto& self, const auto& vec) {
// self.set_data(vec);
// }>;
using PlotData = DerivedProp<Token, QVector<PointData>, [](auto& self, const auto& vec){self.set_data(vec);}>;
using DataRange = common::pro::Array<Token, int, 2, [](auto& self, const auto& arr) {
// using DataRange = common::pro::Array<Token, int, 2, [](auto& self, const auto& arr) {
// self.set_color_gradient_range(arr[0], arr[1]);
// }>;
using DataRange = DerivedProp<Token, std::array<int, 2>, [](auto& self, const auto& arr) {
self.set_color_gradient_range(arr[0], arr[1]);
}>;

View File

@@ -1,16 +1,17 @@
//
// Created by Lenn on 2025/10/17.
//
#ifndef TOUCHSENSOR_HEATMAP_IMPL_HH
#define TOUCHSENSOR_HEATMAP_IMPL_HH
#include "heatmap.hh"
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/widget/sliders.hh"
#include "qcustomplot/qcustomplot.h"
//
// Created by Lenn on 2025/10/17.
//
#ifndef TOUCHSENSOR_HEATMAP_IMPL_HH
#define TOUCHSENSOR_HEATMAP_IMPL_HH
#include "heatmap.hh"
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/widget/sliders.hh"
#include "qcustomplot/qcustomplot.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <qcolor.h>
#include <qdebug.h>
#include <qfont.h>
@@ -19,42 +20,45 @@ using namespace creeper::plot_widget::internal;
struct BasicPlot::Impl {
explicit Impl(BasicPlot& self) noexcept : self{self}, initialized(false), matrix_size(QSize{3, 4}) {}
public:
auto set_xlabel_text(const QString& text) -> void {
xlabel = text;
if (initialized) {
self.xAxis->setLabel(text);
self.replot();
}
}
auto set_ylabel_text(const QString& text) -> void {
ylabel = text;
if (initialized) {
self.yAxis->setLabel(text);
self.replot();
}
}
auto set_matrix_size(const QSize& size) -> void {
matrix_size = size;
if (initialized) {
reset_plot();
if (!data_points.isEmpty()) {
set_data(data_points);
}
}
}
auto load_theme_manager(ThemeManager& mgr) -> void {
mgr.append_handler(&self, [this](const ThemeManager& mgr) {
if (initialized) {
self.replot();
}
});
}
public:
std::optional<creeper::ColorScheme> scheme;
auto set_xlabel_text(const QString& text) -> void {
xlabel = text;
if (initialized) {
self.xAxis->setLabel(text);
self.replot();
}
}
auto set_ylabel_text(const QString& text) -> void {
ylabel = text;
if (initialized) {
self.yAxis->setLabel(text);
self.replot();
}
}
auto set_matrix_size(const QSize& size) -> void {
matrix_size = size;
if (initialized) {
reset_plot();
if (!data_points.isEmpty()) {
set_data(data_points);
}
}
}
auto load_theme_manager(ThemeManager& mgr) -> void {
mgr.append_handler(&self, [this](const ThemeManager& mgr) {
scheme = mgr.color_scheme();
apply_color_scheme();
if (initialized) {
self.replot();
}
});
}
auto set_color_gradient_range(const double& min, const double& max) -> void {
if (initialized && color_map) {
color_map->setDataRange(QCPRange(min, max));
@@ -62,8 +66,8 @@ public:
}
color_min = min;
color_max = max;
}
}
auto set_data(const QVector<PointData>& data) -> void {
data_points = data;
if (initialized && color_map) {
@@ -80,33 +84,33 @@ public:
cpmp->data()->setRange(QCPRange(0.5, matrix_size.width() - 0.5),
QCPRange(0.5, matrix_size.height() - 0.5));
QSharedPointer<QCPAxisTickerText> xticker(new QCPAxisTickerText);
QSharedPointer<QCPAxisTickerText> yticker(new QCPAxisTickerText);
xticker->setSubTickCount(1);
yticker->setSubTickCount(1);
self.xAxis->setVisible(false);
self.yAxis->setVisible(false);
self.xAxis->setTicker(xticker);
self.yAxis->setTicker(yticker);
self.xAxis->grid()->setPen(Qt::NoPen);
self.yAxis->grid()->setPen(Qt::NoPen);
self.xAxis->grid()->setSubGridVisible(true);
self.yAxis->grid()->setSubGridVisible(true);
self.xAxis->setSubTicks(true);
self.yAxis->setSubTicks(true);
self.xAxis->setTickLength(0);
self.yAxis->setTickLength(0);
self.xAxis->setSubTickLength(6);
self.yAxis->setSubTickLength(6);
self.xAxis->setRange(0, matrix_size.width());
self.yAxis->setRange(0, matrix_size.height());
if (!xlabel.isEmpty()) self.xAxis->setLabel(xlabel);
if (!ylabel.isEmpty()) self.yAxis->setLabel(ylabel);
QSharedPointer<QCPAxisTickerText> xticker(new QCPAxisTickerText);
QSharedPointer<QCPAxisTickerText> yticker(new QCPAxisTickerText);
xticker->setSubTickCount(1);
yticker->setSubTickCount(1);
self.xAxis->setVisible(false);
self.yAxis->setVisible(false);
self.xAxis->setTicker(xticker);
self.yAxis->setTicker(yticker);
self.xAxis->grid()->setPen(Qt::NoPen);
self.yAxis->grid()->setPen(Qt::NoPen);
self.xAxis->grid()->setSubGridVisible(true);
self.yAxis->grid()->setSubGridVisible(true);
self.xAxis->setSubTicks(true);
self.yAxis->setSubTicks(true);
self.xAxis->setTickLength(0);
self.yAxis->setTickLength(0);
self.xAxis->setSubTickLength(6);
self.yAxis->setSubTickLength(6);
self.xAxis->setRange(0, matrix_size.width());
self.yAxis->setRange(0, matrix_size.height());
if (!xlabel.isEmpty()) self.xAxis->setLabel(xlabel);
if (!ylabel.isEmpty()) self.yAxis->setLabel(ylabel);
if (!color_scale) {
color_scale = new QCPColorScale(&self);
color_scale->setType(QCPAxis::atBottom);
@@ -125,24 +129,25 @@ public:
QCPColorGradient gradient;
gradient.setColorStopAt(0.0, QColor(240, 246, 255)); // 低值淡色
gradient.setColorStopAt(0.35, QColor(142, 197, 252));
gradient.setColorStopAt(0.7, QColor(56, 128, 199));
gradient.setColorStopAt(1.0, QColor(8, 36, 95)); // 高值深色
cpmp->setGradient(gradient);
cpmp->setDataRange(QCPRange(color_min, color_max));
cpmp->setInterpolate(false);
gradient.setColorStopAt(0.7, QColor(56, 128, 199));
gradient.setColorStopAt(1.0, QColor(8, 36, 95)); // 高值深色
cpmp->setGradient(gradient);
cpmp->setDataRange(QCPRange(color_min, color_max));
cpmp->setInterpolate(false);
QCPMarginGroup *margin_group = new QCPMarginGroup(&self);
self.axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
color_scale->setMarginGroup(QCP::msLeft | QCP::msRight, margin_group);
initialized = true;
if (!data_points.isEmpty()) {
update_plot_data();
}
}
initialized = true;
apply_color_scheme();
if (!data_points.isEmpty()) {
update_plot_data();
}
}
auto reset_plot() -> void {
// 清除所有绘图元素
self.clearPlottables();
@@ -183,19 +188,19 @@ public:
// 重绘
self.replot();
}
auto is_plot_initialized() const -> bool {
return initialized;
}
auto get_matrix_size() const -> QSize {
return matrix_size;
}
private:
QString xlabel;
QString ylabel;
QSize matrix_size;
auto is_plot_initialized() const -> bool {
return initialized;
}
auto get_matrix_size() const -> QSize {
return matrix_size;
}
private:
QString xlabel;
QString ylabel;
QSize matrix_size;
QVector<PointData> data_points;
double color_min = 0.0;
double color_max = 800.0;
@@ -204,6 +209,40 @@ private:
QCPColorScale* color_scale = nullptr;
QCPColorMap* color_map = nullptr;
QVector<QCPItemText*> cell_labels;
QColor label_text_color = QColor(0, 0, 0);
void apply_color_scheme() {
QColor text_color = QColor(30, 30, 30);
if (scheme.has_value()) {
if (scheme->on_surface.isValid()) {
text_color = scheme->on_surface;
}
}
label_text_color = QColor(0, 0, 0); // 固定黑色
const auto pen = QPen(text_color);
self.xAxis->setTickLabelColor(text_color);
self.yAxis->setTickLabelColor(text_color);
self.xAxis->setLabelColor(text_color);
self.yAxis->setLabelColor(text_color);
self.xAxis->setBasePen(pen);
self.yAxis->setBasePen(pen);
self.xAxis->setTickPen(pen);
self.yAxis->setTickPen(pen);
if (color_scale && color_scale->axis()) {
color_scale->axis()->setTickLabelColor(text_color);
color_scale->axis()->setLabelColor(text_color);
color_scale->axis()->setBasePen(pen);
color_scale->axis()->setTickPen(pen);
}
// 已有标签更新
for (auto* label : cell_labels) {
if (!label) continue;
label->setColor(label_text_color);
}
}
void clear_labels() {
for (auto* label : cell_labels) {
@@ -245,7 +284,7 @@ private:
font.setPointSize(8);
}
label->setFont(font);
label->setColor(Qt::black);
label->setColor(label_text_color);
label->setSelectable(false);
cell_labels.push_back(label);
}
@@ -266,12 +305,6 @@ private:
? values[static_cast<std::size_t>(idx)]
: 0.0;
label->setText(QString::number(value, 'f', 0));
const double normalized = std::clamp((value - color_min) / range, 0.0, 1.0);
if (normalized > 0.55) {
label->setColor(Qt::white);
} else {
label->setColor(QColor(25, 25, 25));
}
const int x = idx % width;
const int y = idx / width;
label->position->setCoords(x + 0.5, y + 0.5);

View File

@@ -1,45 +1,46 @@
#pragma once
#include "cpdecoder.hh"
#include <cstdint>
#include <optional>
#include <vector>
#pragma once
#include "cpdecoder.hh"
#include <cstdint>
#include <optional>
#include <vector>
namespace ffmsep::tactile {
inline constexpr std::uint8_t kStartByteFirst = 0xAA;
inline constexpr std::uint8_t kStartByteSecond = 0x55;
enum class FunctionCode : std::uint8_t {
Unknown = 0x00,
ReadMatrix = 0x01,
ReadSingle = 0x02,
ReadTemperature = 0x03,
SetDeviceId = 0x51,
SetMatrixSize = 0x52,
CalibrationMode = 0x53,
};
struct MatrixSize {
std::uint8_t long_edge = 0;
std::uint8_t short_edge = 0;
};
struct TactileFrame {
std::uint8_t device_address = 0;
std::uint8_t reserved = 0;
std::uint8_t response_function = 0;
FunctionCode function = FunctionCode::Unknown;
std::uint32_t start_address = 0;
std::uint16_t return_byte_count = 0;
std::uint8_t status = 0;
std::vector<std::uint8_t> payload;
Unknown = 0x00,
ReadMatrix = 0x01,
ReadSingle = 0x02,
ReadTemperature = 0x03,
SetDeviceId = 0x51,
SetMatrixSize = 0x52,
CalibrationMode = 0x53,
};
std::optional<TactileFrame> parse_frame(const CPFrame& frame);
std::vector<std::uint16_t> parse_pressure_values(const TactileFrame& frame);
std::optional<MatrixSize> parse_matrix_size_payload(const TactileFrame& frame);
std::optional<MatrixSize> parse_patrix_coordinate_payload(const TactileFrame& frame);
const CPCodec* tactile_codec();
void register_tactile_codec();
}
struct MatrixSize {
std::uint8_t long_edge = 0;
std::uint8_t short_edge = 0;
};
struct TactileFrame {
std::uint8_t device_address = 0;
std::uint8_t reserved = 0;
std::uint8_t response_function = 0;
FunctionCode function = FunctionCode::Unknown;
std::uint32_t start_address = 0;
std::uint16_t return_byte_count = 0;
std::uint8_t status = 0;
std::vector<std::uint8_t> payload;
};
std::optional<TactileFrame> parse_frame(const CPFrame &frame);
std::vector<std::uint16_t> parse_pressure_values(const TactileFrame &frame);
std::optional<MatrixSize> parse_matrix_size_payload(const TactileFrame &frame);
std::optional<MatrixSize>
parse_patrix_coordinate_payload(const TactileFrame &frame);
const CPCodec *tactile_codec();
void register_tactile_codec();
} // namespace ffmsep::tactile

View File

@@ -1,14 +1,14 @@
#include "component.hh"
#include "modern-qt/core/application.hh"
#include "modern-qt/layout/group.hh"
#include "modern-qt/layout/linear.hh"
#include "modern-qt/layout/mutual-exclusion-group.hh"
#include "modern-qt/utility/material-icon.hh"
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/widget/buttons/icon-button.hh"
#include "modern-qt/widget/cards/filled-card.hh"
#include "modern-qt/widget/image.hh"
#include "creeper-qt/core/application.hh"
#include "creeper-qt/layout/group.hh"
#include "creeper-qt/layout/linear.hh"
#include "creeper-qt/layout/mutual-exclusion-group.hh"
#include "creeper-qt/utility/material-icon.hh"
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/widget/buttons/icon-button.hh"
#include "creeper-qt/widget/cards/filled-card.hh"
#include "creeper-qt/widget/image.hh"
using namespace creeper;
namespace fc = filled_card::pro;
@@ -26,7 +26,6 @@ auto NavComponent(NavComponentState& state) noexcept -> raw_pointer<QWidget> {
im::BorderWidth {3},
im::PainterResource {
":/images/images/logo.png",
// "./images/logo.png",
},
};
state.manager.append_handler(AvatarComponent, [AvatarComponent](const ThemeManager& manager) {
@@ -74,7 +73,10 @@ auto NavComponent(NavComponentState& state) noexcept -> raw_pointer<QWidget> {
navigation_icons_config,
status,
ic::FontIcon(icon.data()),
ic::Clickable {[=]{state.switch_callback(index, name);}},
ic::Clickable {[=] {
// state.switch_callback(index, name);
state.stacked_callback(index);
}},
};
},
Qt::AlignHCenter,

437
components/setting.cc Normal file
View File

@@ -0,0 +1,437 @@
//
// 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 <concepts>
#include <creeper-qt/utility/material-icon.hh>
#include <creeper-qt/utility/wrapper/mutable-value.hh>
#include <creeper-qt/widget/buttons/icon-button.hh>
#include <creeper-qt/widget/cards/filled-card.hh>
#include <creeper-qt/widget/cards/outlined-card.hh>
#include <creeper-qt/widget/dropdown-menu.hh>
#include <creeper-qt/widget/image.hh>
#include <creeper-qt/widget/shape/wave-circle.hh>
#include <creeper-qt/widget/sliders.hh>
#include <creeper-qt/widget/switch.hh>
#include <creeper-qt/widget/text-fields.hh>
#include <creeper-qt/widget/text.hh>
#include <creeper-qt/widget/buttons/icon-button.hh>
#include <cstddef>
#include <iterator>
#include <qcontainerfwd.h>
#include <qlogging.h>
#include <qnamespace.h>
#include <ranges>
#include <utility>
#include "globalhelper.hh"
#include <functional>
#include <memory>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QLineEdit>
#include <QSpinBox>
#include <QVBoxLayout>
#include <sys/stat.h>
namespace repest_literals {
template<class F>
concept IndexInvocable = std::invocable<F, std::size_t>;
template<IndexInvocable F>
void operator*(F&& f, std::size_t n) {
std::ranges::for_each(std::views::iota(std::size_t{ 0 }, n), std::forward<F>(f));
}
template<IndexInvocable F>
void operator*(std::size_t n, F&& f) {
std::ranges::for_each(std::views::iota(std::size_t{ 0 }, n), std::forward<F>(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<MutableValue<std::vector<ConfigProfile>>> g_profiles_store;
static std::function<void()> g_profiles_refresh;
static void ShowEditProfileDialog(
const ConfigProfile& current,
const std::shared_ptr<MutableValue<std::vector<ConfigProfile>>>& 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<MutableValue<std::vector<ConfigProfile>>>& 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<Col>{
lnpro::Item<Row>{
// row::pro::Stretch {1},
row::pro::Item<Text>{
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<IconButton>{
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<IconButton>{
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>{
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>{
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>{
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>{
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<ConfigProfile>& profiles,
const std::shared_ptr<MutableValue<std::vector<ConfigProfile>>>& 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<ConfigProfile&>(profiles[i]), profiles_store));
};
flow.update();
}
static void AttachProfilesObserver(const std::shared_ptr<MutableValue<std::vector<ConfigProfile>>>& store,
Flow& flow, creeper::ThemeManager& manager) {
struct Functor : creeper::MutableValue<std::vector<ConfigProfile>>::Functor {
Flow& flow;
creeper::ThemeManager& manager;
std::weak_ptr<MutableValue<std::vector<ConfigProfile>>> store_ptr;
Functor(Flow& f, creeper::ThemeManager& m,
std::weak_ptr<MutableValue<std::vector<ConfigProfile>>> s) noexcept
: flow(f)
, manager(m)
, store_ptr(std::move(s)) { }
void update(const std::vector<ConfigProfile>& value) override {
PopulateProfiles(flow, manager, value, store_ptr.lock());
manager.apply_theme();
}
};
auto functor = std::make_unique<Functor>(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<QWidget> {
auto profiles_ctx = std::make_shared<MutableValue<std::vector<ConfigProfile>>>(
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<Col>{
lnpro::Alignment {Qt::AlignTop},
lnpro::Margin {10},
lnpro::Spacing {10},
lnpro::Item{
AddProfileLongItem(state.manager)
},
col::pro::Item<ScrollArea>{
scroll::pro::ThemeManager { state.manager },
scroll::pro::HorizontalScrollBarPolicy { Qt::ScrollBarAlwaysOff },
scroll::pro::Item {
profiles_flow
},
},
},
};
}

File diff suppressed because it is too large Load Diff