Make UI Element the owner of the Unique pointers to other UI elements to allow for easier scope management of adding elements to one another

This commit is contained in:
Matthew Colvin 2023-10-11 10:50:56 -05:00
parent 58787f5bf0
commit 736029c89a
16 changed files with 87 additions and 166 deletions

View file

@ -24,12 +24,39 @@ UIElement::~UIElement() {
} }
} }
void UIElement::AddElement(UIElement *anUIElement) { UIElement *UIElement::AddElement(UIElement::Ptr anUIElement) {
LvglResourceManger::GetInstance().AttemptNow([this, anUIElement] { auto lock = LvglResourceManger::GetInstance().scopeLock();
lv_obj_set_parent(anUIElement->mLvglSelf, mLvglSelf); lv_obj_set_parent(anUIElement->mLvglSelf, mLvglSelf);
}); mContainedElements.push_back(std::move(anUIElement));
return mContainedElements[mContainedElements.size() - 1].get();
} }
UIElement::Ptr UIElement::RemoveElement(UIElement *anElementRef) {
auto ElemToRemoveIter =
std::find_if(mContainedElements.begin(), mContainedElements.end(),
[anElementRef](auto &anElement) {
return anElement.get() == anElementRef;
});
if (ElemToRemoveIter != mContainedElements.end()) {
auto widget = std::move(*ElemToRemoveIter);
mContainedElements.erase(ElemToRemoveIter);
return widget;
}
return nullptr;
}
bool UIElement::KeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
if (OnKeyEvent(aKeyEvent)) {
return true;
}
for (auto &elem : mContainedElements) {
if (elem->KeyEvent(aKeyEvent)) {
return true;
}
}
return false;
};
bool UIElement::IsVisible() { bool UIElement::IsVisible() {
auto lock = LvglResourceManger::GetInstance().scopeLock(); auto lock = LvglResourceManger::GetInstance().scopeLock();
return lv_obj_is_visible(mLvglSelf); return lv_obj_is_visible(mLvglSelf);
@ -220,6 +247,9 @@ void UIElement::Show() {
if (IsVisible()) { if (IsVisible()) {
return; return;
} }
for (auto &elem : mContainedElements) {
elem->OnShow();
}
{ {
auto lock = LvglResourceManger::GetInstance().scopeLock(); auto lock = LvglResourceManger::GetInstance().scopeLock();
lv_obj_clear_flag(mLvglSelf, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(mLvglSelf, LV_OBJ_FLAG_HIDDEN);
@ -231,6 +261,9 @@ void UIElement::Hide() {
if (!IsVisible()) { if (!IsVisible()) {
return; return;
} }
for (auto &elem : mContainedElements) {
elem->OnHide();
}
{ {
auto lock = LvglResourceManger::GetInstance().scopeLock(); auto lock = LvglResourceManger::GetInstance().scopeLock();
lv_obj_add_flag(mLvglSelf, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(mLvglSelf, LV_OBJ_FLAG_HIDDEN);
@ -238,10 +271,6 @@ void UIElement::Hide() {
OnHide(); OnHide();
} }
bool UIElement::KeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
return OnKeyEvent(aKeyEvent);
}
//////////////////// Statics ////////////////////////// //////////////////// Statics //////////////////////////
void UIElement::LvglEventHandler(lv_event_t *anEvent) { void UIElement::LvglEventHandler(lv_event_t *anEvent) {

View file

@ -6,12 +6,15 @@
#include <lvgl.h> #include <lvgl.h>
#include "KeyPressAbstract.hpp" #include "KeyPressAbstract.hpp"
#include <vector>
namespace UI { namespace UI {
class UIElement { class UIElement {
public: public:
using Ptr = std::unique_ptr<UIElement>;
UIElement(lv_obj_t *aLvglSelf, const ID aId = ID()); UIElement(lv_obj_t *aLvglSelf, const ID aId = ID());
virtual ~UIElement(); virtual ~UIElement();
@ -61,7 +64,12 @@ public:
lv_style_selector_t aStyle = LV_PART_MAIN); lv_style_selector_t aStyle = LV_PART_MAIN);
TextStyle GetTextStyle(lv_style_selector_t aStyle = LV_PART_MAIN); TextStyle GetTextStyle(lv_style_selector_t aStyle = LV_PART_MAIN);
virtual void AddElement(UIElement *anElement); UIElement *AddElement(UIElement::Ptr anElement);
template <class UIElemTy> UIElemTy *AddElement(UIElement::Ptr aWidget);
UIElement::Ptr RemoveElement(UIElement *aUIElementRef);
size_t GetNumContainedElements() { return mContainedElements.size(); }
ID GetID() { return mId; }; ID GetID() { return mId; };
@ -107,6 +115,9 @@ private:
lv_obj_t *mLvglSelf; lv_obj_t *mLvglSelf;
const ID mId; const ID mId;
uint32_t mLvglKeepAliveTime = 0; uint32_t mLvglKeepAliveTime = 0;
/// @brief Elements that are currently in this element
std::vector<UIElement::Ptr> mContainedElements;
}; };
/** /**
@ -130,4 +141,9 @@ UIElemTy UIElement::GetElement(lv_obj_t *aLvglObject) {
return nullptr; return nullptr;
} }
template <class UIElemTy>
UIElemTy *UIElement::AddElement(UIElement::Ptr anElement) {
return static_cast<UIElemTy *>(AddElement(std::move(anElement)));
}
} // namespace UI } // namespace UI

View file

@ -1,55 +0,0 @@
#include "WidgetContainer.hpp"
using namespace UI;
WidgetContainer::WidgetContainer(lv_obj_t *aLvglSelf, ID aID)
: UIElement(aLvglSelf, aID) {}
UI::Widget::Base *WidgetContainer::AddWidget(Widget::Base::Ptr aWidget) {
AddElement(aWidget.get());
mWidgets.push_back(std::move(aWidget));
return mWidgets[mWidgets.size() - 1].get();
}
UI::Widget::Base::Ptr
WidgetContainer::RemoveWidget(Widget::Base *aWidgetRefrence) {
auto widgetToRemoveIter = std::find_if(
mWidgets.begin(), mWidgets.end(), [aWidgetRefrence](auto &aWidget) {
return aWidget.get() == aWidgetRefrence;
});
if (widgetToRemoveIter != mWidgets.end()) {
auto widget = std::move(*widgetToRemoveIter);
mWidgets.erase(widgetToRemoveIter);
return widget;
}
return nullptr;
}
bool WidgetContainer::KeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
if (OnKeyEvent(aKeyEvent)) {
return true;
}
for (auto &widget : mWidgets) {
auto used = widget->KeyEvent(aKeyEvent);
if (used) {
return true;
}
}
return false;
};
void WidgetContainer::OnShow() {
for (auto &widget : mWidgets) {
if (widget->IsVisible()) {
widget->OnShow();
}
}
};
void WidgetContainer::OnHide() {
for (auto &widget : mWidgets) {
if (widget->IsVisible()) {
widget->OnHide();
}
}
};

View file

@ -1,38 +0,0 @@
#pragma once
#include "UIElement.hpp"
#include "WidgetBase.hpp"
namespace UI {
class WidgetContainer : public UIElement {
public:
WidgetContainer(lv_obj_t *aLvglSelf, ID aID);
template <class ElementTy> ElementTy *AddWidget(Widget::Base::Ptr aWidget);
Widget::Base *AddWidget(Widget::Base::Ptr aWidget);
Widget::Base::Ptr RemoveWidget(Widget::Base *aWidgetRefrence);
size_t GetNumWidgets() { return mWidgets.size(); }
protected:
/// @brief Forward To widgets that are visible
void OnShow() override;
/// @brief Forward To widgets that are visible
void OnHide() override;
/// @brief Pass Key Events to widgets until one is handled
/// TODO: consider an ability to have a selected widget be the first
/// one to get events.
bool KeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) override;
private:
std::vector<Widget::Base::Ptr> mWidgets;
};
template <class ElementTy>
ElementTy *WidgetContainer::AddWidget(Widget::Base::Ptr aWidget) {
return static_cast<ElementTy *>(AddWidget(std::move(aWidget)));
}
} // namespace UI

View file

@ -6,7 +6,7 @@ using namespace UI::Page;
DisplaySettings::DisplaySettings(std::shared_ptr<DisplayAbstract> aDisplay) DisplaySettings::DisplaySettings(std::shared_ptr<DisplayAbstract> aDisplay)
: Base(UI::ID::Pages::DisplaySettings), mDisplay(aDisplay), : Base(UI::ID::Pages::DisplaySettings), mDisplay(aDisplay),
mBrightnessSlider( mBrightnessSlider(
AddWidget<Widget::Slider>(std::make_unique<Widget::Slider>( AddElement<Widget::Slider>(std::make_unique<Widget::Slider>(
[this](auto aNewBrightness) { [this](auto aNewBrightness) {
mDisplay->setBrightness(aNewBrightness); mDisplay->setBrightness(aNewBrightness);
}, },

View file

@ -6,7 +6,7 @@ using namespace UI::Page;
Base::Base(ID aID) Base::Base(ID aID)
: Base(lv_obj_create(Screen::BackgroundScreen::getLvInstance()), aID) {} : Base(lv_obj_create(Screen::BackgroundScreen::getLvInstance()), aID) {}
Base::Base(lv_obj_t *aLvglSelf, ID aID) : WidgetContainer(aLvglSelf, aID) { Base::Base(lv_obj_t *aLvglSelf, ID aID) : UIElement(aLvglSelf, aID) {
SetHeight(lv_pct(100)); SetHeight(lv_pct(100));
SetWidth(lv_pct(100)); SetWidth(lv_pct(100));
SetPadding(Padding()); // Set Padding to default SetPadding(Padding()); // Set Padding to default

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "UIElement.hpp" #include "UIElement.hpp"
#include "WidgetContainer.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
@ -10,7 +9,7 @@ class PopUpScreen;
namespace UI::Page { namespace UI::Page {
class Tab; class Tab;
class TabView; class TabView;
class Base : public WidgetContainer { class Base : public UIElement {
// Classes that Own Pages // Classes that Own Pages
friend Tab; // Allow Tab to Forward all Key Events to its page friend Tab; // Allow Tab to Forward all Key Events to its page
friend TabView; // Allow Tab view to call OnShow and OnHide Since it can show friend TabView; // Allow Tab view to call OnShow and OnHide Since it can show

View file

@ -19,7 +19,7 @@ SettingsPage::SettingsPage(std::shared_ptr<HardwareAbstract> aHardware)
button->SetHeight(lv_pct(10)); button->SetHeight(lv_pct(10));
button->SetWidth(lv_pct(10)); button->SetWidth(lv_pct(10));
mButton = AddWidget(std::move(button)); mButton = AddElement<Widget::Button>(std::move(button));
} }
void SettingsPage::OnShow() {} void SettingsPage::OnShow() {}
@ -41,7 +41,7 @@ void SettingsPage::AddSlider() {
fakeSlider->SetY(nextY + 10); fakeSlider->SetY(nextY + 10);
} }
sliders.push_back(AddWidget(std::move(fakeSlider))); sliders.push_back(AddElement(std::move(fakeSlider)));
} }
bool SettingsPage::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) { bool SettingsPage::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
@ -57,7 +57,7 @@ bool SettingsPage::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
case id::Aux2: case id::Aux2:
if (aKeyEvent.mType == eventType::Release) { if (aKeyEvent.mType == eventType::Release) {
if (sliders.size() > 0) { if (sliders.size() > 0) {
auto widget = RemoveWidget(sliders[0]); RemoveElement(sliders[0]);
sliders.erase( sliders.erase(
sliders.begin()); // sliders is non owning so after removing delete sliders.begin()); // sliders is non owning so after removing delete
// it from non owning array // it from non owning array

View file

@ -13,8 +13,9 @@ public:
protected: protected:
void OnShow() override; void OnShow() override;
void OnHide() override{};
std::vector<Widget::Base *> sliders; std::vector<UIElement *> sliders;
Widget::Base *mButton; Widget::Base *mButton;
std::shared_ptr<HardwareAbstract> mHardware; std::shared_ptr<HardwareAbstract> mHardware;
}; };

View file

@ -7,12 +7,9 @@ using namespace UI::Page;
Tab::Tab(lv_obj_t *aTab, ID aId) : Base(aTab, aId) {} Tab::Tab(lv_obj_t *aTab, ID aId) : Base(aTab, aId) {}
void Tab::GiveContent(Page::Base::Ptr aContent) { void Tab::GiveContent(Page::Base::Ptr aContent) {
AddElement(aContent.get()); mContent = AddElement<Page::Base>(std::move(aContent));
mContent = std::move(aContent);
} }
Base::Ptr Tab::TakeContent() { return std::move(mContent); }
bool Tab::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) { bool Tab::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
return mContent->OnKeyEvent(aKeyEvent); return mContent->OnKeyEvent(aKeyEvent);
} }

View file

@ -22,7 +22,7 @@ public:
void OnHide() override; void OnHide() override;
private: private:
Base::Ptr mContent; Base *mContent;
}; };
class TabView : public Base { class TabView : public Base {

View file

@ -5,33 +5,24 @@
using namespace UI::Screen; using namespace UI::Screen;
HomeScreen::HomeScreen(std::shared_ptr<HardwareAbstract> aHardware) HomeScreen::HomeScreen(std::shared_ptr<HardwareAbstract> aHardware)
: Base(UI::ID::Screens::Home), mHardware(aHardware), : Base(UI::ID::Screens::Home), mHardware(aHardware) {
mTabView(ID(ID::Pages::INVALID_PAGE_ID)) {
SetBgColor(UI::Color::BLACK); SetBgColor(UI::Color::BLACK);
SetPushAnimation(LV_SCR_LOAD_ANIM_FADE_IN); SetPushAnimation(LV_SCR_LOAD_ANIM_FADE_IN);
AddElement(&mTabView); // Adds Tabview to Homescreen mTabView = AddElement<Page::TabView>(std::make_unique<Page::TabView>(
ID(ID::Pages::INVALID_PAGE_ID))); // Adds Tabview to Homescreen
// Adds pages to the Tab view // Adds pages to the Tab view
mTabView.AddTab(std::make_unique<Page::SettingsPage>(aHardware), "Settings"); mTabView->AddTab(std::make_unique<Page::SettingsPage>(aHardware), "Settings");
mTabView.AddTab(std::make_unique<Page::SettingsPage>(aHardware), "Settings1"); mTabView->AddTab(std::make_unique<Page::SettingsPage>(aHardware),
"Settings1");
} }
void HomeScreen::SetBgColor(lv_color_t value, lv_style_selector_t selector) { void HomeScreen::SetBgColor(lv_color_t value, lv_style_selector_t selector) {
mTabView.SetBgColor(value, selector); mTabView->SetBgColor(value, selector);
UI::UIElement::SetBgColor(value, selector); UI::UIElement::SetBgColor(value, selector);
} }
bool HomeScreen::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) { bool HomeScreen::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
return false; return false;
}; };
void HomeScreen::OnShow() { mTabView.OnShow(); };
void HomeScreen::OnHide() { mTabView.OnHide(); };
bool HomeScreen::KeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
if (OnKeyEvent(aKeyEvent)) {
return true;
}
return mTabView.KeyEvent(aKeyEvent);
};

View file

@ -13,15 +13,13 @@ public:
void SetBgColor(lv_color_t value, void SetBgColor(lv_color_t value,
lv_style_selector_t selector = LV_PART_MAIN) override; lv_style_selector_t selector = LV_PART_MAIN) override;
bool KeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) override;
protected: protected:
void OnShow() override; void OnShow() override{};
void OnHide() override; void OnHide() override{};
bool OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) override; bool OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) override;
private: private:
Page::TabView mTabView; Page::TabView *mTabView;
std::shared_ptr<HardwareAbstract> mHardware = nullptr; std::shared_ptr<HardwareAbstract> mHardware = nullptr;
}; };

View file

@ -6,10 +6,15 @@ using namespace UI;
using namespace UI::Screen; using namespace UI::Screen;
PopUpScreen::PopUpScreen(Page::Base::Ptr aPage) PopUpScreen::PopUpScreen(Page::Base::Ptr aPage)
: Screen::Base(UI::ID::Screens::PopUp), mContentPage(std::move(aPage)), : Screen::Base(UI::ID::Screens::PopUp) {
mExitButton(std::make_unique<Widget::Button>(
[this] { UI::Screen::Manager::getInstance().popScreen(this); })), mContentPage = AddElement<Page::Base>(std::move(aPage));
mTitle(std::make_unique<Widget::Label>(mContentPage->GetTitle())) {
mExitButton = AddElement<Widget::Button>(std::make_unique<Widget::Button>(
[this] { UI::Screen::Manager::getInstance().popScreen(this); }));
mTitle = AddElement<Widget::Label>(
std::make_unique<Widget::Label>(mContentPage->GetTitle()));
mExitButton->SetWidth(lv_pct(10)); mExitButton->SetWidth(lv_pct(10));
mExitButton->SetHeight(mExitButton->GetWidth()); mExitButton->SetHeight(mExitButton->GetWidth());
@ -18,28 +23,13 @@ PopUpScreen::PopUpScreen(Page::Base::Ptr aPage)
mTitle->SetWidth(mExitButton->GetX()); mTitle->SetWidth(mExitButton->GetX());
mTitle->SetHeight(mExitButton->GetHeight()); mTitle->SetHeight(mExitButton->GetHeight());
mTitle->AlignTo(mExitButton.get(), LV_ALIGN_OUT_LEFT_BOTTOM); mTitle->AlignTo(mExitButton, LV_ALIGN_OUT_LEFT_BOTTOM);
mTitle->SetTextStyle(mTitle->GetTextStyle().Align(LV_TEXT_ALIGN_CENTER)); mTitle->SetTextStyle(mTitle->GetTextStyle().Align(LV_TEXT_ALIGN_CENTER));
mContentPage->SetHeight(GetHeight() - mExitButton->GetBottom() - 5); mContentPage->SetHeight(GetHeight() - mExitButton->GetBottom() - 5);
mContentPage->SetY(mExitButton->GetBottom() + 5); mContentPage->SetY(mExitButton->GetBottom() + 5);
AddElement(mContentPage.get());
AddElement(mExitButton.get());
AddElement(mTitle.get());
} }
bool PopUpScreen::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) { bool PopUpScreen::OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) {
return mContentPage->OnKeyEvent(aKeyEvent); return mContentPage->OnKeyEvent(aKeyEvent);
} }
void PopUpScreen::OnShow() {
mContentPage->OnShow();
mExitButton->OnShow();
mTitle->OnShow();
};
void PopUpScreen::OnHide() {
mContentPage->OnHide();
mExitButton->OnHide();
mTitle->OnHide();
};

View file

@ -14,14 +14,10 @@ public:
bool OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) override; bool OnKeyEvent(KeyPressAbstract::KeyEvent aKeyEvent) override;
protected:
void OnShow() override;
void OnHide() override;
private: private:
UI::Page::Base::Ptr mContentPage; UI::Page::Base *mContentPage = nullptr;
std::unique_ptr<Widget::Button> mExitButton; Widget::Button *mExitButton = nullptr;
std::unique_ptr<Widget::Label> mTitle; Widget::Label *mTitle = nullptr;
}; };
} // namespace UI::Screen } // namespace UI::Screen

View file

@ -5,8 +5,6 @@
namespace UI { namespace UI {
class WidgetContainer;
namespace Screen { namespace Screen {
class PopUpScreen; class PopUpScreen;
} }
@ -16,7 +14,6 @@ namespace UI::Widget {
class Base : public UIElement { class Base : public UIElement {
// Classes that Own Widgets // Classes that Own Widgets
friend class UI::WidgetContainer;
friend class UI::Screen::PopUpScreen; friend class UI::Screen::PopUpScreen;
public: public: