diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index e5dfc68436..69bad7a12b 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -55,11 +55,7 @@ class LVGLSelect : public select::Select, public Component { } void set_options_() { // Copy options from lvgl widget to select traits - const auto &widget_options = this->widget_->get_options(); - this->traits.options_.init(widget_options.size()); - for (const auto &option : widget_options) { - this->traits.options_.push_back(option); - } + this->traits.copy_options(this->widget_->get_options()); } LvSelectable *widget_; diff --git a/esphome/components/select/select_traits.h b/esphome/components/select/select_traits.h index e37ce8f77f..826ef14924 100644 --- a/esphome/components/select/select_traits.h +++ b/esphome/components/select/select_traits.h @@ -11,6 +11,8 @@ class SelectTraits { public: void set_options(std::initializer_list options); const FixedVector &get_options() const; + /// Copy options from another SelectTraits (for copy_select, lvgl) + void copy_options(const FixedVector &other) { this->options_.copy_from(other); } protected: FixedVector options_; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 9b0591c9c5..ad9770f271 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -215,6 +215,7 @@ template class FixedVector { ~FixedVector() { cleanup_(); } // Disable copy operations (avoid accidental expensive copies) + // Use copy_from() for explicit copying when needed (e.g., copy_select) FixedVector(const FixedVector &) = delete; FixedVector &operator=(const FixedVector &) = delete; @@ -246,6 +247,19 @@ template class FixedVector { return *this; } + /// Explicitly copy another FixedVector + /// This method exists instead of operator= to make copying intentional and visible. + /// Copying is expensive on embedded systems, so we require explicit opt-in. + /// Use cases: copy_select (copying source options), lvgl (copying widget options) + void copy_from(const FixedVector &other) { + cleanup_(); + reset_(); + init(other.size()); + for (const auto &item : other) { + push_back(item); + } + } + // Allocate capacity - can be called multiple times to reinit void init(size_t n) { cleanup_(); @@ -304,6 +318,11 @@ template class FixedVector { return data_[size_ - 1]; } + /// Access first element (no bounds checking - matches std::vector behavior) + /// Caller must ensure vector is not empty (size() > 0) + T &front() { return data_[0]; } + const T &front() const { return data_[0]; } + /// Access last element (no bounds checking - matches std::vector behavior) /// Caller must ensure vector is not empty (size() > 0) T &back() { return data_[size_ - 1]; } @@ -317,6 +336,12 @@ template class FixedVector { T &operator[](size_t i) { return data_[i]; } const T &operator[](size_t i) const { return data_[i]; } + /// Access element with bounds checking (matches std::vector behavior) + /// Returns reference to element at index i + /// Behavior for out of bounds access matches std::vector::at() (undefined on embedded) + T &at(size_t i) { return data_[i]; } + const T &at(size_t i) const { return data_[i]; } + // Iterator support for range-based for loops T *begin() { return data_; } T *end() { return data_ + size_; }