BLE Rx/Tx App Cleanup (#1570)

* Showing highest dB first
* Including namestring in search
* Using replace seems to work better.
* bletx cleanup
* removing pop
* pop is needed on this view.
* cleanup switching
* reduce to 1 entry list
* Setting to use the name of BLE with a toggle checkbox.
* Removed &nav reference.
* Removing const
* Fixed issue with memory being run out when switching from a previous app
* Fixed not setting updated progressbar for each new packet.
This commit is contained in:
Netro 2023-11-11 14:46:51 -05:00 committed by GitHub
parent f7f784c0f4
commit a3249cae26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 159 deletions

View File

@ -22,7 +22,6 @@
*/ */
#include "ble_rx_app.hpp" #include "ble_rx_app.hpp"
#include "ble_tx_app.hpp"
#include "ui_modemsetup.hpp" #include "ui_modemsetup.hpp"
@ -65,8 +64,9 @@ void RecentEntriesTable<BleRecentEntries>::draw(
Painter& painter, Painter& painter,
const Style& style) { const Style& style) {
std::string line{}; std::string line{};
line.reserve(30);
if (!entry.nameString.empty()) { if (!entry.nameString.empty() && entry.include_name) {
line = entry.nameString; line = entry.nameString;
if (line.length() < 17) { if (line.length() < 17) {
@ -107,13 +107,16 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
text_mac_address.set(to_string_mac_address(entry.packetData.macAddress, 6, false)); text_mac_address.set(to_string_mac_address(entry.packetData.macAddress, 6, false));
button_done.on_select = [this](const ui::Button&) { button_done.on_select = [&nav](const ui::Button&) {
nav_.pop(); nav.pop();
}; };
button_send.on_select = [this](const ui::Button&) { button_send.on_select = [this, &nav](const ui::Button&) {
nav_.set_on_pop([this]() { launch_bletx(entry_); }); auto packetToSend = build_packet();
nav_.pop(); nav.set_on_pop([packetToSend, &nav]() {
nav.replace<BLETxView>(packetToSend);
});
nav.pop();
}; };
} }
@ -199,18 +202,18 @@ void BleRecentEntryDetailView::set_entry(const BleRecentEntry& entry) {
set_dirty(); set_dirty();
} }
void BleRecentEntryDetailView::launch_bletx(BleRecentEntry packetEntry) { BLETxPacket BleRecentEntryDetailView::build_packet() {
BLETxPacket bleTxPacket; BLETxPacket bleTxPacket;
memset(&bleTxPacket, 0, sizeof(BLETxPacket)); memset(&bleTxPacket, 0, sizeof(BLETxPacket));
std::string macAddressStr = to_string_mac_address(packetEntry.packetData.macAddress, 6, true); std::string macAddressStr = to_string_mac_address(entry_.packetData.macAddress, 6, true);
strncpy(bleTxPacket.macAddress, macAddressStr.c_str(), 12); strncpy(bleTxPacket.macAddress, macAddressStr.c_str(), 12);
strncpy(bleTxPacket.advertisementData, packetEntry.dataString.c_str(), packetEntry.packetData.dataLen * 2); strncpy(bleTxPacket.advertisementData, entry_.dataString.c_str(), entry_.packetData.dataLen * 2);
strncpy(bleTxPacket.packetCount, "50", 3); strncpy(bleTxPacket.packetCount, "50", 3);
bleTxPacket.packet_count = 50; bleTxPacket.packet_count = 50;
nav_.replace<BLETxView>(bleTxPacket); return bleTxPacket;
} }
static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) { static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) {
@ -255,28 +258,20 @@ BLERxView::BLERxView(NavigationView& nav)
&options_channel, &options_channel,
&field_frequency, &field_frequency,
&check_log, &check_log,
&check_name,
&label_sort, &label_sort,
&options_sort, &options_sort,
&button_filter, &button_filter,
&button_switch, &button_switch,
&recent_entries_view, &recent_entries_view});
&recent_entries_filter_view,
&recent_entry_detail_view});
recent_entry_detail_view.hidden(true);
recent_entries_filter_view.hidden(true);
recent_entries_view.on_select = [this](const BleRecentEntry& entry) { recent_entries_view.on_select = [this](const BleRecentEntry& entry) {
nav_.push<BleRecentEntryDetailView>(entry); nav_.push<BleRecentEntryDetailView>(entry);
}; };
recent_entries_filter_view.on_select = [this](const BleRecentEntry& entry) { button_filter.on_select = [this](Button&) {
nav_.push<BleRecentEntryDetailView>(entry);
};
button_filter.on_select = [this, &nav](Button&) {
text_prompt( text_prompt(
nav, nav_,
filterBuffer, filterBuffer,
64, 64,
[this](std::string& buffer) { [this](std::string& buffer) {
@ -284,9 +279,8 @@ BLERxView::BLERxView(NavigationView& nav)
}); });
}; };
button_switch.on_select = [this, &nav](Button&) { button_switch.on_select = [&nav](Button&) {
nav_.set_on_pop([this]() { nav_.push<BLETxView>(); }); nav.replace<BLETxView>();
nav_.pop();
}; };
field_frequency.set_step(0); field_frequency.set_step(0);
@ -301,6 +295,13 @@ BLERxView::BLERxView(NavigationView& nav)
logger->append(LOG_ROOT_DIR "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT"); logger->append(LOG_ROOT_DIR "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
}; };
check_name.set_value(true);
check_name.on_select = [this](Checkbox&, bool v) {
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
};
options_channel.on_change = [this](size_t, int32_t i) { options_channel.on_change = [this](size_t, int32_t i) {
field_frequency.set_value(get_freq_by_channel_number(i)); field_frequency.set_value(get_freq_by_channel_number(i));
channel_number = i; channel_number = i;
@ -308,44 +309,8 @@ BLERxView::BLERxView(NavigationView& nav)
baseband::set_btlerx(channel_number); baseband::set_btlerx(channel_number);
}; };
options_sort.on_change = [this](size_t, int32_t i) { options_sort.on_change = [this](size_t, int32_t index) {
switch (i) { handle_entries_sort(index);
case 0:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
break;
case 1:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.numHits; }, false);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.numHits; }, false);
break;
case 2:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
break;
case 3:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
break;
case 4:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.nameString; }, true);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.nameString; }, true);
break;
default:
break;
}
recent_entries_view.set_dirty();
recent_entries_filter_view.set_dirty();
}; };
options_channel.set_selected_index(0, true); options_channel.set_selected_index(0, true);
@ -428,42 +393,66 @@ void BLERxView::on_data(BlePacketData* packet) {
// Start of Packet stuffing. // Start of Packet stuffing.
// Masking off the top 2 bytes to avoid invalid keys. // Masking off the top 2 bytes to avoid invalid keys.
auto& entry = ::on_packet(recent, macAddressEncoded & 0xFFFFFFFFFFFF); auto& entry = ::on_packet(recent, macAddressEncoded & 0xFFFFFFFFFFFF);
truncate_entries(recent, 32);
updateEntry(packet, entry); updateEntry(packet, entry);
// Add entries if they meet the criteria.
auto value = filter;
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
handle_entries_sort(options_sort.selected_index());
// Log at End of Packet. // Log at End of Packet.
if (logger && logging) { if (logger && logging) {
logger->log_raw_data(str_console); logger->log_raw_data(str_console);
} }
} }
void BLERxView::on_switch_table(const std::string value) { void BLERxView::on_switch_table(std::string value) {
filter = value; // New filter? Reset list from recent entries.
if (filter != value) {
if (!value.empty()) { resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
removeEntriesWithoutKey(recent, filterEntries, [&value](const BleRecentEntry& entry) { return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
return entry.dataString.find(value) == std::string::npos;
}); });
}
recent_entries_view.set_dirty(); filter = value;
}
recent_entries_filter_view.hidden(false); void BLERxView::handle_entries_sort(uint8_t index) {
recent_entries_view.hidden(true); switch (index) {
} else { case 0:
recent_entries_view.hidden(false); sortEntriesBy(
recent_entries_filter_view.hidden(true); recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
break;
case 1:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.numHits; }, false);
break;
case 2:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, false);
break;
case 3:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
break;
case 4:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.nameString; }, true);
break;
default:
break;
} }
recent_entries_view.set_dirty(); recent_entries_view.set_dirty();
recent_entries_filter_view.set_dirty();
} }
void BLERxView::set_parent_rect(const Rect new_parent_rect) { void BLERxView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect); View::set_parent_rect(new_parent_rect);
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height - switch_button_height}; const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height - switch_button_height};
recent_entries_view.set_parent_rect(content_rect); recent_entries_view.set_parent_rect(content_rect);
recent_entry_detail_view.set_parent_rect(content_rect);
recent_entries_filter_view.set_parent_rect(content_rect);
} }
BLERxView::~BLERxView() { BLERxView::~BLERxView() {
@ -502,6 +491,7 @@ void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry)
} }
entry.nameString = ""; entry.nameString = "";
entry.include_name = check_name.value();
uint8_t currentByte = 0; uint8_t currentByte = 0;
uint8_t length = 0; uint8_t length = 0;
@ -526,48 +516,6 @@ void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry)
stringFound = true; stringFound = true;
} }
} }
switch (options_sort.selected_index()) {
case 0:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.macAddress; }, true);
break;
case 1:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.numHits; }, false);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.numHits; }, false);
break;
case 2:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.dbValue; }, true);
break;
case 3:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.timestamp; }, false);
break;
case 4:
sortEntriesBy(
recent, [](const BleRecentEntry& entry) { return entry.nameString; }, true);
sortEntriesBy(
filterEntries, [](const BleRecentEntry& entry) { return entry.nameString; }, true);
break;
default:
break;
}
on_switch_table(filter);
// TODO: Crude hack, should be a more formal listener arrangement...
if (entry.key() == recent_entry_detail_view.entry().key()) {
recent_entry_detail_view.set_entry(entry);
}
} }
} /* namespace ui */ } /* namespace ui */

View File

@ -24,6 +24,8 @@
#ifndef __BLE_RX_APP_H__ #ifndef __BLE_RX_APP_H__
#define __BLE_RX_APP_H__ #define __BLE_RX_APP_H__
#include "ble_tx_app.hpp"
#include "ui.hpp" #include "ui.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
@ -79,6 +81,7 @@ struct BleRecentEntry {
std::string timestamp; std::string timestamp;
std::string dataString; std::string dataString;
std::string nameString; std::string nameString;
bool include_name;
uint16_t numHits; uint16_t numHits;
BleRecentEntry() BleRecentEntry()
@ -93,6 +96,7 @@ struct BleRecentEntry {
timestamp{}, timestamp{},
dataString{}, dataString{},
nameString{}, nameString{},
include_name{},
numHits{} { numHits{} {
} }
@ -118,7 +122,7 @@ class BleRecentEntryDetailView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
BleRecentEntry entry_{}; BleRecentEntry entry_{};
void launch_bletx(BleRecentEntry packetEntry); BLETxPacket build_packet();
static constexpr uint8_t total_data_lines{5}; static constexpr uint8_t total_data_lines{5};
@ -167,7 +171,8 @@ class BLERxView : public View {
private: private:
void on_data(BlePacketData* packetData); void on_data(BlePacketData* packetData);
void on_switch_table(const std::string value); void on_switch_table(std::string value);
void handle_entries_sort(uint8_t index);
void updateEntry(const BlePacketData* packet, BleRecentEntry& entry); void updateEntry(const BlePacketData* packet, BleRecentEntry& entry);
NavigationView& nav_; NavigationView& nav_;
@ -228,15 +233,21 @@ class BLERxView : public View {
{"Name", 4}}}; {"Name", 4}}};
Button button_filter{ Button button_filter{
{12 * 8, 3 * 8, 4 * 8, 16}, {11 * 8, 3 * 8, 4 * 8, 16},
"Filter"}; "Filter"};
Checkbox check_log{ Checkbox check_log{
{20 * 8, 3 * 8}, {17 * 8, 3 * 8},
3, 3,
"Log", "Log",
true}; true};
Checkbox check_name{
{23 * 8, 3 * 8},
3,
"Name",
true};
Console console{ Console console{
{0, 4 * 16, 240, 240}}; {0, 4 * 16, 240, 240}};
@ -258,10 +269,7 @@ class BLERxView : public View {
{"dB", 4}, {"dB", 4},
}}; }};
BleRecentEntry entry_{};
BleRecentEntriesView recent_entries_view{columns, recent}; BleRecentEntriesView recent_entries_view{columns, recent};
BleRecentEntriesView recent_entries_filter_view{columns, filterEntries};
BleRecentEntryDetailView recent_entry_detail_view{nav_, entry_};
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::BlePacket, Message::ID::BlePacket,

View File

@ -219,17 +219,17 @@ void BLETxView::start() {
return; return;
} else { } else {
// Send first or single packet. // Send first or single packet.
packet_counter = packets[current_packet].packet_count; progressbar.set_max(packets[0].packet_count);
progressbar.set_max(packets[current_packet].packet_count);
button_play.set_bitmap(&bitmap_stop); button_play.set_bitmap(&bitmap_stop);
baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, packets[current_packet].advertisementData, pduType); baseband::set_btletx(channel_number, random_mac ? randomMac : packets[0].macAddress, packets[0].advertisementData, pduType);
transmitter_model.enable(); transmitter_model.enable();
is_running = true; is_running = true;
} }
} else { } else {
// Send next packet. // Send next packet.
progressbar.set_max(packets[current_packet].packet_count);
baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, packets[current_packet].advertisementData, pduType); baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, packets[current_packet].advertisementData, pduType);
} }
@ -238,7 +238,6 @@ void BLETxView::start() {
} }
packet_counter--; packet_counter--;
progressbar.set_value(packets[current_packet].packet_count - packet_counter); progressbar.set_value(packets[current_packet].packet_count - packet_counter);
} }
@ -248,10 +247,7 @@ void BLETxView::stop() {
button_play.set_bitmap(&bitmap_play); button_play.set_bitmap(&bitmap_play);
check_loop.set_value(false); check_loop.set_value(false);
current_packet = 0; update_current_packet(packets[0], 0);
text_packets_sent.set(to_string_dec_uint(packets[0].packet_count));
packet_counter = packets[0].packet_count;
update_packet_display(packets[0]);
is_running = false; is_running = false;
} }
@ -265,16 +261,13 @@ void BLETxView::on_tx_progress(const bool done) {
if (current_packet == (num_packets - 1)) { if (current_packet == (num_packets - 1)) {
// If looping, restart from beginning. // If looping, restart from beginning.
if (check_loop.value()) { if (check_loop.value()) {
current_packet = 0; update_current_packet(packets[current_packet], 0);
packet_counter = packets[current_packet].packet_count;
update_packet_display(packets[current_packet]);
} else { } else {
stop(); stop();
} }
} else { } else {
current_packet++; current_packet++;
packet_counter = packets[current_packet].packet_count; update_current_packet(packets[current_packet], current_packet);
update_packet_display(packets[current_packet]);
} }
} else { } else {
if ((timer_count % timer_period) == 0) { if ((timer_count % timer_period) == 0) {
@ -344,8 +337,8 @@ BLETxView::BLETxView(NavigationView& nav)
random_mac = v; random_mac = v;
}; };
button_open.on_select = [this, &nav](Button&) { button_open.on_select = [this](Button&) {
auto open_view = nav.push<FileLoadView>(".TXT"); auto open_view = nav_.push<FileLoadView>(".TXT");
open_view->on_changed = [this](std::filesystem::path new_file_path) { open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path); on_file_changed(new_file_path);
@ -364,9 +357,8 @@ BLETxView::BLETxView(NavigationView& nav)
}); });
}; };
button_switch.on_select = [this, &nav](Button&) { button_switch.on_select = [&nav](Button&) {
nav_.set_on_pop([this]() { nav_.push<BLERxView>(); }); nav.replace<BLERxView>();
nav_.pop();
}; };
} }
@ -375,7 +367,8 @@ BLETxView::BLETxView(
BLETxPacket packet) BLETxPacket packet)
: BLETxView(nav) { : BLETxView(nav) {
packets[0] = packet; packets[0] = packet;
update_packet_display(packets[0]);
update_current_packet(packets[0], 0);
num_packets = 1; num_packets = 1;
file_override = true; file_override = true;
@ -404,7 +397,6 @@ void BLETxView::on_file_changed(const fs::path& new_file_path) {
uint64_t packetCountSize = strlen(packets[num_packets].packetCount); uint64_t packetCountSize = strlen(packets[num_packets].packetCount);
packets[num_packets].packet_count = stringToUint32(packets[num_packets].packetCount); packets[num_packets].packet_count = stringToUint32(packets[num_packets].packetCount);
packet_counter = packets[num_packets].packet_count;
// Verify Data. // Verify Data.
if ((macAddressSize == mac_address_size_str) && (advertisementDataSize < max_packet_size_str) && (packetCountSize < max_packet_repeat_str) && if ((macAddressSize == mac_address_size_str) && (advertisementDataSize < max_packet_size_str) && (packetCountSize < max_packet_repeat_str) &&
@ -425,7 +417,7 @@ void BLETxView::on_file_changed(const fs::path& new_file_path) {
} while (num_packets < max_num_packets); } while (num_packets < max_num_packets);
update_packet_display(packets[0]); update_current_packet(packets[0], 0);
} }
} }
@ -447,7 +439,7 @@ void BLETxView::on_data(uint32_t value, bool is_data) {
console.write(str_console); console.write(str_console);
} }
void BLETxView::update_packet_display(BLETxPacket packet) { void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex) {
std::string formattedMacAddress = to_string_formatted_mac_address(packet.macAddress); std::string formattedMacAddress = to_string_formatted_mac_address(packet.macAddress);
std::vector<std::string> strings = splitIntoStrings(packet.advertisementData); std::vector<std::string> strings = splitIntoStrings(packet.advertisementData);
@ -463,6 +455,9 @@ void BLETxView::update_packet_display(BLETxPacket packet) {
for (const std::string& str : strings) { for (const std::string& str : strings) {
console.writeln(str); console.writeln(str);
} }
packet_counter = packet.packet_count;
current_packet = currentIndex;
} }
void BLETxView::set_parent_rect(const Rect new_parent_rect) { void BLETxView::set_parent_rect(const Rect new_parent_rect) {

View File

@ -85,10 +85,10 @@ class BLETxView : public View {
private: private:
void on_data(uint32_t value, bool is_data); void on_data(uint32_t value, bool is_data);
void on_tx_progress(const bool done);
void on_file_changed(const std::filesystem::path& new_file_path); void on_file_changed(const std::filesystem::path& new_file_path);
void update_packet_display(BLETxPacket packet);
void on_save_file(const std::string value); void on_save_file(const std::string value);
void on_tx_progress(const bool done);
void update_current_packet(BLETxPacket packet, uint32_t currentIndex);
NavigationView& nav_; NavigationView& nav_;
TxRadioState radio_state_{ TxRadioState radio_state_{
@ -152,7 +152,7 @@ class BLETxView : public View {
static constexpr uint8_t max_packet_size_str{62}; static constexpr uint8_t max_packet_size_str{62};
static constexpr uint8_t max_packet_repeat_str{10}; static constexpr uint8_t max_packet_repeat_str{10};
static constexpr uint32_t max_packet_repeat_count{UINT32_MAX}; static constexpr uint32_t max_packet_repeat_count{UINT32_MAX};
static constexpr uint32_t max_num_packets{256}; static constexpr uint32_t max_num_packets{32};
BLETxPacket packets[max_num_packets]; BLETxPacket packets[max_num_packets];

View File

@ -103,19 +103,28 @@ void sortEntriesBy(ContainerType& entries, KeySelector keySelector, SortOrder as
} }
template <typename ContainerType, typename KeySelector> template <typename ContainerType, typename KeySelector>
void removeEntriesWithoutKey(ContainerType& entries, ContainerType& filteredEntries, KeySelector keySelector) { void resetFilteredEntries(ContainerType& entries, KeySelector keySelector) {
// Clear the filteredEntries container // Clear the filteredEntries container
filteredEntries.clear();
auto it = entries.begin(); auto it = entries.begin();
while (it != entries.end()) { while (it != entries.end()) {
if (!keySelector(*it)) { if (keySelector(*it)) {
filteredEntries.emplace_back(*it); // Add a new entry to filteredEntries entries.erase(it); // Add a new entry to filteredEntries
} }
++it; // Move to the next element, outside of the if block ++it; // Move to the next element, outside of the if block
} }
} }
template <typename ContainerType, typename MemberPtr, typename KeyValue>
void setAllMembersToValue(ContainerType& entries, MemberPtr memberPtr, const KeyValue& keyValue) {
for (auto& entry : entries) {
// Check if the member specified by memberPtr is equal to keyValue
if (entry.*memberPtr != keyValue) {
// Update the member with keyValue
entry.*memberPtr = keyValue;
}
}
}
namespace ui { namespace ui {
using RecentEntriesColumn = std::pair<std::string, size_t>; using RecentEntriesColumn = std::pair<std::string, size_t>;