From 6485195e26830de7d7e4f17dffc9e8ef27465330 Mon Sep 17 00:00:00 2001 From: JF002 Date: Sun, 27 Jan 2019 18:05:45 +0100 Subject: [PATCH] Develop (#15) * Fix build (add missing include in Widget.h and simpleExample) * Add PlatformIO project file to simpleExample * Update m5stack lib (#12) * Fix DrawBitmap according to new versions of M5Stack lib * Reduce flickering (#13) * Do not refresh the whole button when only the texte has changed * Replace the UpdateFlag logic by a (better) Invalidate logic * Fix crash when trying to zoom on a NULL widget. * Removed unused include. (#14) * Set version to 0.3.0. --- README.md | 9 ++- examples/simpleExample/.travis.yml | 67 ++++++++++++++++++ examples/simpleExample/platformio.ini | 16 +++++ examples/simpleExample/{ => src}/main.cpp | 3 +- library.properties | 2 +- src/Bar.cpp | 4 +- src/Button.cpp | 83 +++++++++++++++++------ src/Button.h | 5 +- src/ButtonInfoBar.cpp | 33 ++++++--- src/ButtonInfoBar.h | 3 + src/Screen.cpp | 4 +- src/StatusBar.cpp | 53 ++++++++++----- src/StatusBar.h | 9 ++- src/UpDownButton.cpp | 20 +++--- src/Widget.cpp | 6 +- src/Widget.h | 21 ++++-- src/WidgetMosaic.cpp | 27 ++++---- 17 files changed, 276 insertions(+), 89 deletions(-) create mode 100644 examples/simpleExample/.travis.yml create mode 100644 examples/simpleExample/platformio.ini rename examples/simpleExample/{ => src}/main.cpp (99%) diff --git a/README.md b/README.md index 9b2def4..7c3e544 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,6 @@ Then, you just need to include the headers (e.g. #include ) and write Look at examples if you need some inspiration ;-) -**NOTE : ** This project was built with version 0.1.9 of M5STack. Since then, a little change has been made on method DrawBitmap which is not compatible with this version of cfGUI. Until I write a fix, you should use version 0.1.9 of M5Stack. - Here is an example of `platformio.ini` file: ``` @@ -31,7 +29,7 @@ Here is an example of `platformio.ini` file: platform = espressif32 board = m5stack-core-esp32 framework = arduino -lib_deps=M5Stack@0.1.9, NTPClient +lib_deps=M5Stack, NTPClient build_flags=-std=gnu++11 ``` @@ -41,6 +39,11 @@ build_flags=-std=gnu++11 - Better 'focus' management # Changelog +## 0.3.0 + - New redraw() strategy (replace SetUpdatedFlag() by Invalidate(), performances improved and flickering reduced + - Support new version of M5Stack lib + - Fix crash when the center button was pushed with no widget selected + ## 0.2.0 - Change default font (looks better) - New widgets (AppScreen, StatusBar, ButtonInfoBar, UpDownButton) diff --git a/examples/simpleExample/.travis.yml b/examples/simpleExample/.travis.yml new file mode 100644 index 0000000..7c486f1 --- /dev/null +++ b/examples/simpleExample/.travis.yml @@ -0,0 +1,67 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < https://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < https://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < https://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choose one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to be used as a library with examples. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/examples/simpleExample/platformio.ini b/examples/simpleExample/platformio.ini new file mode 100644 index 0000000..9f4ddc4 --- /dev/null +++ b/examples/simpleExample/platformio.ini @@ -0,0 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:m5stack-core-esp32] +platform = espressif32 +board = m5stack-core-esp32 +framework = arduino +lib_deps=M5Stack@0.1.9, NTPClient +build_flags=-std=gnu++11 diff --git a/examples/simpleExample/main.cpp b/examples/simpleExample/src/main.cpp similarity index 99% rename from examples/simpleExample/main.cpp rename to examples/simpleExample/src/main.cpp index b5cf699..664e981 100644 --- a/examples/simpleExample/main.cpp +++ b/examples/simpleExample/src/main.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -11,6 +10,8 @@ #include #include #include +#include + using namespace Codingfield::UI; AppScreen* screen; diff --git a/library.properties b/library.properties index b3a9e6a..d573c63 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=cfGUI -version=0.2.0 +version=0.3.0 author=Jean-François Milants maintainer=Jean-François Milants sentence=cfGUI diff --git a/src/Bar.cpp b/src/Bar.cpp index d793cf5..8a2ed46 100644 --- a/src/Bar.cpp +++ b/src/Bar.cpp @@ -4,8 +4,8 @@ using namespace Codingfield::UI; void Bar::Draw() { if(IsHidden()) return; - if(isUpdated) + if(isInvalidated) M5.Lcd.fillRect(position.x, position.y, size.width, size.height, WHITE); - isUpdated = false; + isInvalidated = false; } diff --git a/src/Button.cpp b/src/Button.cpp index a10d2ad..ef02639 100644 --- a/src/Button.cpp +++ b/src/Button.cpp @@ -14,6 +14,7 @@ Codingfield::UI::Button::Button(Widget* parent, Point position, Size size) : Wid } void Codingfield::UI::Button::SetBackgroundColor(Color c) { + backgroundColorUpdated = true; this->backgroundColor = c; } @@ -23,50 +24,92 @@ void Codingfield::UI::Button::SetTextColor(Color c) { void Codingfield::UI::Button::SetSelected(bool s) { if(isSelected != s) { + wasSelected = isSelected; isSelected = s; - SetUpdateFlag(); } } void Codingfield::UI::Button::SetText(const std::string& t) { if(text != t) { + if(text.empty()) + oldText = t; + else + this->oldText = this->text; this->text = t; - SetUpdateFlag(); } } void Codingfield::UI::Button::SetTitle(const std::string& t) { if(title != t) { + if(title.empty()) + oldTitle = t; + else + oldTitle = title; title = t; - SetUpdateFlag(); } } void Codingfield::UI::Button::Draw() { - if(IsHidden()) return; - if(isUpdated) { - M5.Lcd.setTextColor(textColor); + if(IsHidden()){ + return; + } + if(true) { + bool forceUpdate = isInvalidated; + if(forceUpdate) { + Serial.println("FORCEUPDATE"); - M5.Lcd.fillRect(position.x, position.y, size.width, size.height, backgroundColor); - M5.Lcd.setTextDatum(MC_DATUM); - M5.Lcd.setTextColor(textColor); + } + int x = position.x + (size.width/2); + int yText; + int yTitle; if(title.empty()) { - M5.Lcd.setFreeFont(FF22); - M5.Lcd.drawString(text.c_str(), position.x + (size.width/2), position.y + (size.height/2)); - M5.Lcd.setFreeFont(FF21); + yText = position.y + (size.height/2); + } else { + yText = position.y + (size.height/3); + yTitle = position.y + ((size.height/3)*2); } - else { + + if(backgroundColorUpdated || forceUpdate) { + M5.Lcd.fillRect(position.x, position.y, size.width, size.height, backgroundColor); + backgroundColorUpdated = false; + forceUpdate = true; + } + + if(forceUpdate || (oldText != text)) { + M5.Lcd.setTextDatum(MC_DATUM); + M5.Lcd.setTextColor(backgroundColor); + M5.Lcd.setFreeFont(FF22); - M5.Lcd.drawString(text.c_str(), position.x + (size.width/2), position.y + (size.height/3)); + M5.Lcd.drawString(oldText.c_str(), x, yText); + + M5.Lcd.setTextColor(textColor); + M5.Lcd.drawString(text.c_str(), x, yText); + oldText = text; + } + + if(forceUpdate || (oldTitle != title)) { + M5.Lcd.setTextDatum(MC_DATUM); + M5.Lcd.setTextColor(backgroundColor); + M5.Lcd.setFreeFont(FF21); - M5.Lcd.drawString(title.c_str(), position.x + (size.width/2), position.y + ((size.height/3)*2)); + M5.Lcd.drawString(oldTitle.c_str(), x, yTitle); + + M5.Lcd.setTextColor(textColor); + M5.Lcd.drawString(title.c_str(), x, yTitle); + oldTitle = title; } - if(isSelected) { - M5.Lcd.drawRect(position.x, position.y, size.width, size.height, RED); - M5.Lcd.drawRect(position.x+1, position.y+1, size.width-2, size.height-2, RED); - M5.Lcd.drawRect(position.x+2, position.y+2, size.width-4, size.height-4, RED); + if(forceUpdate || (wasSelected != isSelected)) { + if(isSelected) { + M5.Lcd.drawRect(position.x, position.y, size.width, size.height, RED); + M5.Lcd.drawRect(position.x+1, position.y+1, size.width-2, size.height-2, RED); + M5.Lcd.drawRect(position.x+2, position.y+2, size.width-4, size.height-4, RED); + } else { + M5.Lcd.drawRect(position.x, position.y, size.width, size.height, backgroundColor); + M5.Lcd.drawRect(position.x + 1, position.y + 1, size.width - 2, size.height - 2, backgroundColor); + M5.Lcd.drawRect(position.x + 2, position.y + 2, size.width - 4, size.height - 4, backgroundColor); + } } } - isUpdated = false; + isInvalidated = false; } diff --git a/src/Button.h b/src/Button.h index b49b05d..658d066 100644 --- a/src/Button.h +++ b/src/Button.h @@ -16,10 +16,13 @@ namespace Codingfield { void Draw() override; protected: Color backgroundColor = BLACK; + bool backgroundColorUpdated = true; Color textColor = BLACK; std::string text; + std::string oldText; std::string title; - + std::string oldTitle; + bool wasSelected = false; }; } } diff --git a/src/ButtonInfoBar.cpp b/src/ButtonInfoBar.cpp index 67e36c9..f7cdb77 100644 --- a/src/ButtonInfoBar.cpp +++ b/src/ButtonInfoBar.cpp @@ -4,41 +4,58 @@ using namespace Codingfield::UI; void ButtonInfoBar::SetButtonAText(const std::string& t) { if(btnAText != t) { + oldBtnAText = btnAText; btnAText = t; - isUpdated = true; } } void ButtonInfoBar::SetButtonBText(const std::string& t) { if(btnBText != t) { + oldBtnBText = btnBText; btnBText = t; - isUpdated = true; } } void ButtonInfoBar::SetButtonCText(const std::string& t) { if(btnCText != t) { + oldBtnCText = btnCText; btnCText = t; - isUpdated = true; } } void ButtonInfoBar::Draw() { if(IsHidden()) return; - bool oldIsUpdated = isUpdated; + bool oldIsInvalidated = isInvalidated; Bar::Draw(); - if(oldIsUpdated) { - M5.Lcd.setTextColor(BLACK); - + if(oldIsInvalidated || (oldBtnAText != btnAText)) { M5.Lcd.setTextDatum(TC_DATUM); + M5.Lcd.setTextColor(color); + M5.Lcd.drawString(oldBtnAText.c_str(), (size.width/6), position.y + 5); + M5.Lcd.setTextColor(BLACK); M5.Lcd.drawString(btnAText.c_str(), (size.width/6), position.y + 5); + oldBtnAText = btnAText; + } + + if(oldIsInvalidated || (oldBtnBText != btnBText)) { M5.Lcd.setTextDatum(TC_DATUM); + M5.Lcd.setTextColor(color); + M5.Lcd.drawString(oldBtnBText.c_str(), size.width/2, position.y + 5); + M5.Lcd.setTextColor(BLACK); M5.Lcd.drawString(btnBText.c_str(), size.width/2, position.y + 5); + oldBtnBText = btnBText; + } + + if(oldIsInvalidated || (oldBtnCText != btnCText)) { M5.Lcd.setTextDatum(TC_DATUM); + M5.Lcd.setTextColor(color); + M5.Lcd.drawString(oldBtnCText.c_str(), ((size.width/3)*2) + (size.width/6), position.y + 5); + M5.Lcd.setTextColor(BLACK); M5.Lcd.drawString(btnCText.c_str(), ((size.width/3)*2) + (size.width/6), position.y + 5); + + oldBtnCText = btnCText; } - isUpdated = false; + isInvalidated = false; } diff --git a/src/ButtonInfoBar.h b/src/ButtonInfoBar.h index 890fe4c..7af2824 100644 --- a/src/ButtonInfoBar.h +++ b/src/ButtonInfoBar.h @@ -15,8 +15,11 @@ namespace Codingfield { private: Color color = WHITE; std::string btnAText; + std::string oldBtnAText; std::string btnBText; + std::string oldBtnBText; std::string btnCText; + std::string oldBtnCText; }; } } diff --git a/src/Screen.cpp b/src/Screen.cpp index 9deaf1f..9d76095 100644 --- a/src/Screen.cpp +++ b/src/Screen.cpp @@ -8,11 +8,11 @@ void Screen::Draw() { M5.Lcd.setTextSize(1); if(IsHidden()) return; - if(isUpdated) + if(isInvalidated) M5.Lcd.fillScreen(color); for(Widget* w : children) w->Draw(); - isUpdated = false; + isInvalidated = false; } diff --git a/src/StatusBar.cpp b/src/StatusBar.cpp index df8ff4a..132611e 100644 --- a/src/StatusBar.cpp +++ b/src/StatusBar.cpp @@ -9,40 +9,31 @@ extern const uint8_t image_data_wifi3[]; void StatusBar::SetWifiStatus(const StatusBar::WifiStatuses status) { if(wifiStatus != status) { + oldWifiStatus = wifiStatus; wifiStatus = status; - SetUpdateFlag(); } } void StatusBar::SetUptime(const uint32_t t) { if(uptime != t) { + oldUptime = uptime; uptime = t; - SetUpdateFlag(); } } void StatusBar::SetDateTime(const std::string& t) { if(dateTime != t) { + oldDateTime = dateTime; dateTime = t; - SetUpdateFlag(); } } void StatusBar::Draw() { if(IsHidden()) return; - bool oldIsUpdated = isUpdated; + bool wasInvalidated = isInvalidated; Bar::Draw(); - if(oldIsUpdated) { - M5.Lcd.setTextColor(BLACK); - - M5.Lcd.setTextDatum(TL_DATUM); - String s = String("UP:") + String(uptime) + String("h"); - M5.Lcd.drawString(s.c_str(), 1, 5); - - M5.Lcd.setTextDatum(TC_DATUM); - M5.Lcd.drawString(dateTime.c_str(), 160, 5); - + if(wasInvalidated || (wifiStatus != oldWifiStatus)) { const uint8_t* wifibmp = image_data_wifi0; switch(wifiStatus) { case WifiStatuses::Weak: wifibmp = image_data_wifi1; break; @@ -53,7 +44,37 @@ void StatusBar::Draw() { wifibmp = image_data_wifi0; break; } - M5.Lcd.drawBitmap(295,0, wifibmp, 25,25, BLACK); + + M5.Lcd.setBitmapColor(BLACK, color); + M5.Lcd.pushImage(295, 0, 25, 25, const_cast(wifibmp), false); + + oldWifiStatus = wifiStatus; + } + + if(wasInvalidated || (oldDateTime != dateTime)) { + M5.Lcd.setTextDatum(TC_DATUM); + + M5.Lcd.setTextColor(color); + M5.Lcd.drawString(oldDateTime.c_str(), 160, 5); + + M5.Lcd.setTextColor(BLACK); + M5.Lcd.drawString(dateTime.c_str(), 160, 5); + + oldDateTime = dateTime; + } + + if(wasInvalidated || (oldUptime != uptime)) { + M5.Lcd.setTextDatum(TL_DATUM); + + M5.Lcd.setTextColor(color); + String s = String("UP:") + String(oldUptime) + String("h"); + M5.Lcd.drawString(s.c_str(), 1, 5); + + M5.Lcd.setTextColor(BLACK); + s = String("UP:") + String(uptime) + String("h"); + M5.Lcd.drawString(s.c_str(), 1, 5); + + oldUptime = uptime; } - isUpdated = false; + isInvalidated = false; } diff --git a/src/StatusBar.h b/src/StatusBar.h index ecb05d8..b691905 100644 --- a/src/StatusBar.h +++ b/src/StatusBar.h @@ -6,7 +6,7 @@ namespace Codingfield { namespace UI { class StatusBar : public Bar { public: - enum class WifiStatuses {No_signal, Weak, Medium, Full}; + enum class WifiStatuses {Unknown, No_signal, Weak, Medium, Full}; StatusBar() : Bar() {} StatusBar(Widget* parent, Point position, int32_t height) : Bar(parent, position, height) {} void Draw() override; @@ -16,9 +16,12 @@ namespace Codingfield { private: Color color = WHITE; - WifiStatuses wifiStatus; - uint32_t uptime; + WifiStatuses wifiStatus = WifiStatuses::No_signal; + WifiStatuses oldWifiStatus = WifiStatuses::Unknown; + uint32_t uptime = 0; + uint32_t oldUptime = UINT32_MAX; std::string dateTime; + std::string oldDateTime; }; } } diff --git a/src/UpDownButton.cpp b/src/UpDownButton.cpp index 0caee4f..ee1da4d 100644 --- a/src/UpDownButton.cpp +++ b/src/UpDownButton.cpp @@ -9,8 +9,10 @@ UpDownButton::UpDownButton(Widget* parent) : Button(parent) { void UpDownButton::Draw() { if(IsHidden()) return; - if(isUpdated) { - Button::Draw(); + bool wasInvalidated = isInvalidated; + Button::Draw(); + + if(wasInvalidated) { if(controlsEnabled) { M5.Lcd.setTextDatum(MC_DATUM); M5.Lcd.setTextColor(textColor); @@ -18,8 +20,6 @@ void UpDownButton::Draw() { M5.Lcd.drawString("+", position.x + (size.width - (size.width/6)), position.y + (size.height/2)); } } - - isUpdated = false; } void UpDownButton::EnableControls() { @@ -32,29 +32,25 @@ void UpDownButton::DisableControls() { void UpDownButton::OnButtonAPressed() { if(upCallback != nullptr) { - if(downCallback(this)) - SetUpdateFlag(); + downCallback(this); } } void UpDownButton::OnButtonBPressed() { if(applyCallback != nullptr) { - if(applyCallback(this)) - SetUpdateFlag(); + applyCallback(this); } } void UpDownButton::OnButtonBLongPush() { if(cancelCallback != nullptr) { - if(cancelCallback(this)) - SetUpdateFlag(); + cancelCallback(this); } } void UpDownButton::OnButtonCPressed() { if(upCallback != nullptr) { - if(upCallback(this)) - SetUpdateFlag(); + upCallback(this); } } diff --git a/src/Widget.cpp b/src/Widget.cpp index 59b0ce8..45de321 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -11,11 +11,11 @@ Widget::Widget(Widget* parent, Point position, Size size) : parent{parent}, pos parent->AddChild(this); } -void Widget::SetUpdateFlag() { +void Widget::Invalidate() { for(auto c : children) { - c->SetUpdateFlag(); + c->Invalidate(); } - isUpdated = true; + isInvalidated = true; } void Widget::SetSize(const Size& s) { diff --git a/src/Widget.h b/src/Widget.h index 512c2d4..ab8e5f2 100644 --- a/src/Widget.h +++ b/src/Widget.h @@ -1,7 +1,10 @@ #pragma once +#include + #include "Point.h" #include "Size.h" + #include namespace Codingfield { @@ -25,10 +28,20 @@ namespace Codingfield { void SetPosition(const Point& p); /* The widget will be visible (drawn) the next time Draw() will be called */ - void Show() { SetUpdateFlag(); isVisible = true; } + void Show() { + if (!isVisible) { + Invalidate(); + isVisible = true; + } + } /* The widget will not be visible (not drawn) the next time Draw() will be called */ - void Hide() { SetUpdateFlag(); isVisible = false; } + void Hide() { + if (isVisible) { + Invalidate(); + isVisible = false; + } + } bool IsVisible() const { return isVisible;} bool IsHidden() const { return !isVisible;} @@ -57,20 +70,20 @@ namespace Codingfield { /* Enables/disables controls on the button */ virtual void EnableControls() {} virtual void DisableControls() {} + void Invalidate(); protected: virtual void OnSizeUpdated() { } virtual void OnPositionUpdated() { } - void SetUpdateFlag(); Point position; Size size; Widget* parent = nullptr; std::vector children; bool isSelected = false; - bool isUpdated = true; bool isVisible = true; bool isEditable = false; + bool isInvalidated = true; }; } } diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index 39a283c..0841921 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -1,5 +1,4 @@ #include "WidgetMosaic.h" -#include using namespace Codingfield::UI; WidgetMosaic::WidgetMosaic(Widget* parent, Point position, Size size, int32_t nbColumns, int32_t nbRows) : Widget(parent, position, size), @@ -14,19 +13,14 @@ WidgetMosaic::WidgetMosaic(int32_t nbColumns, int32_t nbRows) : WidgetMosaic(nul void WidgetMosaic::Draw() { if(IsHidden()) return; - if(isUpdated) { + if(isInvalidated) { M5.Lcd.fillRect(position.x, position.y, size.width, size.height, BLACK); } - if(!zoomOnSelected) { - for(Widget* w : children) - w->Draw(); - } - else { - selectedWidget->Draw(); - } + for(Widget* w : children) + w->Draw(); - isUpdated = false; + isInvalidated = false; } void WidgetMosaic::AddChild(Widget* widget) { @@ -37,7 +31,7 @@ void WidgetMosaic::AddChild(Widget* widget) { int32_t position = ((children.size()-1) % (nbRows*nbColumns)); widget->SetPosition(ComputeWidgetPosition(widgetSize, position)); - SetUpdateFlag(); + Invalidate(); } const Widget* WidgetMosaic::GetSelected() const { @@ -47,10 +41,15 @@ const Widget* WidgetMosaic::GetSelected() const { void WidgetMosaic::ZoomOnSelected(bool enabled) { bool oldValue = zoomOnSelected; if(selectedWidget != nullptr && enabled) { + for(auto* w : children) + w->Hide(); + zoomOnSelected = true; Size widgetSize = ComputeWidgetSize(1,1); selectedWidget->SetSize(widgetSize); selectedWidget->SetPosition(ComputeWidgetPosition(widgetSize, 0)); + selectedWidget->Show(); + selectedWidget->Invalidate(); if(selectedWidget->IsEditable()) { selectedWidget->EnableControls(); } @@ -62,15 +61,16 @@ void WidgetMosaic::ZoomOnSelected(bool enabled) { Size widgetSize = ComputeWidgetSize(); widget->SetSize(widgetSize); widget->SetPosition(ComputeWidgetPosition(widgetSize, index)); + widget->Show(); if(selectedWidget->IsEditable()) { selectedWidget->DisableControls(); } } } - if(oldValue != zoomOnSelected) { + if(selectedWidget != nullptr && oldValue != zoomOnSelected) { zoomOnSelectedCallback(selectedWidget, zoomOnSelected); - SetUpdateFlag(); + Invalidate(); } } @@ -87,6 +87,7 @@ void WidgetMosaic::OnButtonAPressed() { } void WidgetMosaic::OnButtonBPressed() { + if(selectedWidget == nullptr) return; if(zoomOnSelected) { ZoomOnSelected(false); selectedWidget->OnButtonBPressed();