diff --git a/tools/chatgui/src/soundsettingspage.cpp b/tools/chatgui/src/soundsettingspage.cpp index 2a1472cf..b1b9e90a 100644 --- a/tools/chatgui/src/soundsettingspage.cpp +++ b/tools/chatgui/src/soundsettingspage.cpp @@ -14,7 +14,7 @@ extern "C" { #include #include #include -#include +#include SoundSettingsPage::SoundSettingsPage(DbManager* db, QWidget* parent) : QWidget(parent) @@ -144,53 +144,44 @@ void SoundSettingsPage::applyAndSave() { sm->setEnabled(enabled); sm->setVolume(masterVol); + QVector> values; + values.reserve(18); for (int i = 0; i < 8; i++) { auto& e = m_events[i]; bool on = e.check->isChecked(); float ev = e.slider->value() / 100.0f; sm->setEvent(e.key, on, ev); - /* async DB persist — key\0value\0 */ - QString onKey = QStringLiteral("sound_%1_on").arg(e.key); - QString volKey = QStringLiteral("sound_%1_vol").arg(e.key); - QByteArray onVal = on ? QByteArray("1") : QByteArray("0"); - QByteArray volVal = QByteArray::number(e.slider->value()); - - for (int j = 0; j < 2; j++) { - const QByteArray key = (j == 0 ? onKey : volKey).toUtf8(); - const char* k = key.constData(); - const char* v = j == 0 ? onVal.constData() : volVal.constData(); - size_t klen = strlen(k), vlen = strlen(v); - size_t total = klen + 1 + vlen + 1; - if (total > sizeof(save_ui_state_arg::data)) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: key/value too large bytes=%zu", total); continue; - } - struct save_ui_state_arg* arg = (struct save_ui_state_arg*)u_malloc(sizeof(struct save_ui_state_arg)); - if (!arg) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: request allocation failed"); continue; } - arg->inst = gui_bridge_get_inst(); - memcpy(arg->data, k, klen + 1); - memcpy(arg->data + klen + 1, v, vlen + 1); - gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, arg); - } + const QByteArray prefix = QByteArray("sound_") + e.key; + values.append({prefix + "_on", on ? QByteArray("1") : QByteArray("0")}); + values.append({prefix + "_vol", QByteArray::number(e.slider->value())}); } + values.append({"sound_enabled", enabled ? QByteArray("1") : QByteArray("0")}); + values.append({"sound_volume", QByteArray::number(m_volumeSlider->value())}); - /* save global settings */ - { - const char* keys[] = {"sound_enabled", "sound_volume"}; - QByteArray vals[] = {enabled ? QByteArray("1") : QByteArray("0"), - QByteArray::number(m_volumeSlider->value())}; - for (int j = 0; j < 2; j++) { - size_t klen = strlen(keys[j]), vlen = vals[j].size(); - size_t total = klen + 1 + vlen + 1; - if (total > sizeof(save_ui_state_arg::data)) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: global key/value too large bytes=%zu", total); continue; - } - struct save_ui_state_arg* arg = (struct save_ui_state_arg*)u_malloc(sizeof(struct save_ui_state_arg)); - if (!arg) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: global request allocation failed"); continue; } - arg->inst = gui_bridge_get_inst(); - memcpy(arg->data, keys[j], klen + 1); - memcpy(arg->data + klen + 1, vals[j].constData(), vlen + 1); - gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, arg); + /* Один путь передачи владения для всех key\0value\0; принятый запрос освобождает callback либо мост при отмене. */ + unsigned queued = 0, failed = 0; + for (const auto& value : values) { + const auto& key = value.first; + const size_t klen = key.size(), vlen = value.second.size(), total = klen + 1 + vlen + 1; + if (total > sizeof(save_ui_state_arg::data)) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: key=%s key/value too large bytes=%zu", key.constData(), total); + ++failed; continue; } + auto* arg = static_cast(u_malloc(sizeof(save_ui_state_arg))); + if (!arg) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: key=%s request allocation failed", key.constData()); + ++failed; continue; + } + arg->inst = gui_bridge_get_inst(); + memcpy(arg->data, key.constData(), klen + 1); + memcpy(arg->data + klen + 1, value.second.constData(), vlen + 1); + if (gui_bridge_post_uasync_owned_fn(chat_core_save_ui_state_trampoline, arg) < 0) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Sound settings: key=%s save rejected by GUI bridge", key.constData()); + u_free(arg); ++failed; continue; + } + ++queued; } + DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Sound settings applied locally: enabled=%d volume=%d queued=%u failed=%u", + enabled, m_volumeSlider->value(), queued, failed); } diff --git a/tools/chatgui/src/soundsettingspage.h b/tools/chatgui/src/soundsettingspage.h index ae001019..3272882f 100644 --- a/tools/chatgui/src/soundsettingspage.h +++ b/tools/chatgui/src/soundsettingspage.h @@ -1,4 +1,5 @@ -/* Форма звуков уведомлений: общий enable/volume и индивидуальные настройки событий SoundManager. */ +/* GUI-форма звуков уведомлений: общий enable/volume и восемь событий SoundManager. + * После создания вызвать loadFromDb; applyAndSave применяет значения локально и асинхронно сохраняет через GUI bridge. */ #pragma once #include @@ -11,23 +12,28 @@ class QLabel; class SoundSettingsPage : public QWidget { Q_OBJECT public: + /* db заимствован до уничтожения формы; nullptr допустим, но loadFromDb тогда ничего не делает. */ explicit SoundSettingsPage(DbManager* db, QWidget* parent = nullptr); + /* GUI: заполнить контролы из ui_state (с defaults), не меняя SoundManager. */ void loadFromDb(); + /* GUI: немедленно обновить SoundManager и передать мосту отдельные снимки key/value. + * Отказ постановки освобождает запрос здесь, отмена — в мосте, выполнение — в callback ядра. + * Возврат не подтверждает запись в БД; частичные ошибки логируются, локальные значения сохраняются. */ void applyAndSave(); private: struct EventRow { - const char* key; - const char* label; - QCheckBox* check = nullptr; - QSlider* slider = nullptr; - QLabel* volLabel = nullptr; + const char* key; // Статическое имя события и суффикс ключей ui_state. + const char* label; // Статический исходный текст для перевода. + QCheckBox* check = nullptr; // Дочерний контрол включения события. + QSlider* slider = nullptr; // Дочерний контрол громкости 0..100. + QLabel* volLabel = nullptr; // Дочерняя подпись громкости в процентах. }; - DbManager* m_db = nullptr; - QCheckBox* m_enabledCheck; - QSlider* m_volumeSlider; - QLabel* m_volumeLabel; - EventRow m_events[8]; + DbManager* m_db = nullptr; // Заимствованный читатель БД, нужен только loadFromDb. + QCheckBox* m_enabledCheck; // Общий enable звуков, дочерний виджет. + QSlider* m_volumeSlider; // Общая громкость 0..100, дочерний виджет. + QLabel* m_volumeLabel; // Общая громкость в процентах, дочерний виджет. + EventRow m_events[8]; // Имена и контролы восьми событий уведомлений. }; diff --git a/tools/chatgui/tests/test_sound_settings.cpp b/tools/chatgui/tests/test_sound_settings.cpp index 70b5b702..a8e377a3 100644 --- a/tools/chatgui/tests/test_sound_settings.cpp +++ b/tools/chatgui/tests/test_sound_settings.cpp @@ -10,6 +10,7 @@ #include extern "C" { #include "utun_instance.h" +#include "chat/chat_core.h" #include "mem.h" #include "debug_config.h" }