[select] Refactor to index-based operations for immediate and future RAM savings (#11623)
This commit is contained in:
1 parent
c7ae424613
commit
6f7e54c3f3
44 files changed
+268
-204
No files matched your search
@@ -7,19 +7,21 @@ namespace select {
|
||||
|
||||
static const char *const TAG = "select";
|
||||
|
||||
SelectCall &SelectCall::set_option(const std::string &option) {
|
||||
return with_operation(SELECT_OP_SET).with_option(option);
|
||||
SelectCall &SelectCall::set_option(const std::string &option) { return this->with_option(option); }
|
||||
|
||||
SelectCall &SelectCall::set_option(const char *option) { return this->with_option(option); }
|
||||
|
||||
SelectCall &SelectCall::set_index(size_t index) { return this->with_index(index); }
|
||||
|
||||
SelectCall &SelectCall::select_next(bool cycle) { return this->with_operation(SELECT_OP_NEXT).with_cycle(cycle); }
|
||||
|
||||
SelectCall &SelectCall::select_previous(bool cycle) {
|
||||
return this->with_operation(SELECT_OP_PREVIOUS).with_cycle(cycle);
|
||||
}
|
||||
|
||||
SelectCall &SelectCall::set_index(size_t index) { return with_operation(SELECT_OP_SET_INDEX).with_index(index); }
|
||||
SelectCall &SelectCall::select_first() { return this->with_operation(SELECT_OP_FIRST); }
|
||||
|
||||
SelectCall &SelectCall::select_next(bool cycle) { return with_operation(SELECT_OP_NEXT).with_cycle(cycle); }
|
||||
|
||||
SelectCall &SelectCall::select_previous(bool cycle) { return with_operation(SELECT_OP_PREVIOUS).with_cycle(cycle); }
|
||||
|
||||
SelectCall &SelectCall::select_first() { return with_operation(SELECT_OP_FIRST); }
|
||||
|
||||
SelectCall &SelectCall::select_last() { return with_operation(SELECT_OP_LAST); }
|
||||
SelectCall &SelectCall::select_last() { return this->with_operation(SELECT_OP_LAST); }
|
||||
|
||||
SelectCall &SelectCall::with_operation(SelectOperation operation) {
|
||||
this->operation_ = operation;
|
||||
@@ -31,89 +33,96 @@ SelectCall &SelectCall::with_cycle(bool cycle) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
SelectCall &SelectCall::with_option(const std::string &option) {
|
||||
this->option_ = option;
|
||||
SelectCall &SelectCall::with_option(const std::string &option) { return this->with_option(option.c_str()); }
|
||||
|
||||
SelectCall &SelectCall::with_option(const char *option) {
|
||||
this->operation_ = SELECT_OP_SET;
|
||||
// Find the option index - this validates the option exists
|
||||
this->index_ = this->parent_->index_of(option);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SelectCall &SelectCall::with_index(size_t index) {
|
||||
this->index_ = index;
|
||||
this->operation_ = SELECT_OP_SET;
|
||||
if (index >= this->parent_->size()) {
|
||||
ESP_LOGW(TAG, "'%s' - Index value %zu out of bounds", this->parent_->get_name().c_str(), index);
|
||||
this->index_ = {}; // Store nullopt for invalid index
|
||||
} else {
|
||||
this->index_ = index;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
optional<size_t> SelectCall::calculate_target_index_(const char *name) {
|
||||
const auto &options = this->parent_->traits.get_options();
|
||||
if (options.empty()) {
|
||||
ESP_LOGW(TAG, "'%s' - Select has no options", name);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (this->operation_ == SELECT_OP_FIRST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this->operation_ == SELECT_OP_LAST) {
|
||||
return options.size() - 1;
|
||||
}
|
||||
|
||||
if (this->operation_ == SELECT_OP_SET) {
|
||||
ESP_LOGD(TAG, "'%s' - Setting", name);
|
||||
if (!this->index_.has_value()) {
|
||||
ESP_LOGW(TAG, "'%s' - No option set", name);
|
||||
return {};
|
||||
}
|
||||
return this->index_.value();
|
||||
}
|
||||
|
||||
// SELECT_OP_NEXT or SELECT_OP_PREVIOUS
|
||||
ESP_LOGD(TAG, "'%s' - Selecting %s, with%s cycling", name,
|
||||
this->operation_ == SELECT_OP_NEXT ? LOG_STR_LITERAL("next") : LOG_STR_LITERAL("previous"),
|
||||
this->cycle_ ? LOG_STR_LITERAL("") : LOG_STR_LITERAL("out"));
|
||||
|
||||
const auto size = options.size();
|
||||
if (!this->parent_->has_state()) {
|
||||
return this->operation_ == SELECT_OP_NEXT ? 0 : size - 1;
|
||||
}
|
||||
|
||||
// Use cached active_index_ instead of index_of() lookup
|
||||
const auto active_index = this->parent_->active_index_;
|
||||
if (this->cycle_) {
|
||||
return (size + active_index + (this->operation_ == SELECT_OP_NEXT ? +1 : -1)) % size;
|
||||
}
|
||||
|
||||
if (this->operation_ == SELECT_OP_PREVIOUS && active_index > 0) {
|
||||
return active_index - 1;
|
||||
}
|
||||
|
||||
if (this->operation_ == SELECT_OP_NEXT && active_index < size - 1) {
|
||||
return active_index + 1;
|
||||
}
|
||||
|
||||
return {}; // Can't navigate further without cycling
|
||||
}
|
||||
|
||||
void SelectCall::perform() {
|
||||
auto *parent = this->parent_;
|
||||
const auto *name = parent->get_name().c_str();
|
||||
const auto &traits = parent->traits;
|
||||
const auto &options = traits.get_options();
|
||||
|
||||
if (this->operation_ == SELECT_OP_NONE) {
|
||||
ESP_LOGW(TAG, "'%s' - SelectCall performed without selecting an operation", name);
|
||||
return;
|
||||
}
|
||||
if (options.empty()) {
|
||||
ESP_LOGW(TAG, "'%s' - Cannot perform SelectCall, select has no options", name);
|
||||
|
||||
// Calculate target index (with_index() and with_option() already validate bounds/existence)
|
||||
auto target_index = this->calculate_target_index_(name);
|
||||
if (!target_index.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string target_value;
|
||||
|
||||
if (this->operation_ == SELECT_OP_SET) {
|
||||
ESP_LOGD(TAG, "'%s' - Setting", name);
|
||||
if (!this->option_.has_value()) {
|
||||
ESP_LOGW(TAG, "'%s' - No option value set for SelectCall", name);
|
||||
return;
|
||||
}
|
||||
target_value = this->option_.value();
|
||||
} else if (this->operation_ == SELECT_OP_SET_INDEX) {
|
||||
if (!this->index_.has_value()) {
|
||||
ESP_LOGW(TAG, "'%s' - No index value set for SelectCall", name);
|
||||
return;
|
||||
}
|
||||
if (this->index_.value() >= options.size()) {
|
||||
ESP_LOGW(TAG, "'%s' - Index value %zu out of bounds", name, this->index_.value());
|
||||
return;
|
||||
}
|
||||
target_value = options[this->index_.value()];
|
||||
} else if (this->operation_ == SELECT_OP_FIRST) {
|
||||
target_value = options.front();
|
||||
} else if (this->operation_ == SELECT_OP_LAST) {
|
||||
target_value = options.back();
|
||||
} else if (this->operation_ == SELECT_OP_NEXT || this->operation_ == SELECT_OP_PREVIOUS) {
|
||||
auto cycle = this->cycle_;
|
||||
ESP_LOGD(TAG, "'%s' - Selecting %s, with%s cycling", name, this->operation_ == SELECT_OP_NEXT ? "next" : "previous",
|
||||
cycle ? "" : "out");
|
||||
if (!parent->has_state()) {
|
||||
target_value = this->operation_ == SELECT_OP_NEXT ? options.front() : options.back();
|
||||
} else {
|
||||
auto index = parent->index_of(parent->state);
|
||||
if (index.has_value()) {
|
||||
auto size = options.size();
|
||||
if (cycle) {
|
||||
auto use_index = (size + index.value() + (this->operation_ == SELECT_OP_NEXT ? +1 : -1)) % size;
|
||||
target_value = options[use_index];
|
||||
} else {
|
||||
if (this->operation_ == SELECT_OP_PREVIOUS && index.value() > 0) {
|
||||
target_value = options[index.value() - 1];
|
||||
} else if (this->operation_ == SELECT_OP_NEXT && index.value() < options.size() - 1) {
|
||||
target_value = options[index.value() + 1];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target_value = this->operation_ == SELECT_OP_NEXT ? options.front() : options.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!parent->has_option(target_value)) {
|
||||
ESP_LOGW(TAG, "'%s' - Option %s is not a valid option", name, target_value.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, target_value.c_str());
|
||||
parent->control(target_value);
|
||||
auto idx = target_index.value();
|
||||
// All operations use indices, call control() by index to avoid string conversion
|
||||
ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, parent->option_at(idx));
|
||||
parent->control(idx);
|
||||
}
|
||||
|
||||
} // namespace select
|
||||
|
||||
Reference in new issue
Block a user