From cfe7801874d03f98dbcb6e85bed720161cbca11b Mon Sep 17 00:00:00 2001 From: JF Date: Sun, 18 Mar 2018 14:15:22 +0100 Subject: [PATCH 01/14] Bump version to 0.2.0 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 964f4e6..b3a9e6a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=cfGUI -version=0.1 +version=0.2.0 author=Jean-François Milants maintainer=Jean-François Milants sentence=cfGUI From 995950d19620b0219e0a5bb336106ff3ca772b6d Mon Sep 17 00:00:00 2001 From: JF Date: Sun, 18 Mar 2018 16:30:50 +0100 Subject: [PATCH 02/14] Dynamically compute the size and the position of the widgets inside WidgetMosaic The number of rows and columns is configurable at construction time --- src/WidgetMosaic.cpp | 43 +++++++++++++++++++++++++++++++------------ src/WidgetMosaic.h | 9 ++++++++- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index 8a99a26..417b676 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -1,8 +1,10 @@ #include "WidgetMosaic.h" - +#include using namespace Codingfield::UI; -WidgetMosaic::WidgetMosaic(Widget* parent, Point position, Size size) : Widget(parent, position, size) { +WidgetMosaic::WidgetMosaic(Widget* parent, Point position, Size size, int32_t nbColumns, int32_t nbRows) : Widget(parent, position, size), + nbColumns {nbColumns}, + nbRows {nbRows} { } @@ -13,16 +15,12 @@ void WidgetMosaic::Draw() { void WidgetMosaic::AddChild(Widget* widget) { Widget::AddChild(widget); - widget->SetSize(Size(100,88)); - switch(children.size()) { - case 1: widget->SetPosition(Point(5,30)); break; - case 2: widget->SetPosition(Point(110,30)); break; - case 3: widget->SetPosition(Point(215,30)); break; - case 4: widget->SetPosition(Point(5,122)); break; - case 5: widget->SetPosition(Point(110,122)); break; - case 6: widget->SetPosition(Point(215,122)); break; - default: break; - } + + Size widgetSize = ComputeWidgetSize(); + widget->SetSize(widgetSize); + + int32_t position = ((children.size()-1) % (nbRows*nbColumns)); + widget->SetPosition(ComputeWidgetPosition(widgetSize, position)); } void WidgetMosaic::OnButtonAPressed() { @@ -44,3 +42,24 @@ void WidgetMosaic::OnButtonCPressed() { children[indexSelected]->SetSelected(true); } } + +Size WidgetMosaic::ComputeWidgetSize() { + Size widgetSize; + widgetSize.width = (size.width - ((2*border) + ((nbColumns-1)*border))) / nbColumns; + widgetSize.height = (size.height - ((2*border) + ((nbRows-1)*border))) / nbRows; + + String str = String(widgetSize.width) + "x" + String(widgetSize.height); + Serial.println("Mosaic size = " + str); + return widgetSize; +} + +Point WidgetMosaic::ComputeWidgetPosition(const Size& widgetSize, int32_t position) { + Point widgetPosition; + + int32_t row = ((int32_t)position / (int32_t)nbColumns); + int32_t column = position % nbColumns; + widgetPosition.x = ((column)*border) + (column * widgetSize.width) + this->position.x; + widgetPosition.y = ((row)*border) + (row * widgetSize.height) + this->position.y; + + return widgetPosition; +} diff --git a/src/WidgetMosaic.h b/src/WidgetMosaic.h index b7a133d..a3fa395 100644 --- a/src/WidgetMosaic.h +++ b/src/WidgetMosaic.h @@ -1,12 +1,13 @@ #pragma once #include "Widget.h" +#include "Point.h" namespace Codingfield { namespace UI { class WidgetMosaic : public Widget { public: - WidgetMosaic(Widget* parent, Point position, Size size); + WidgetMosaic(Widget* parent, Point position, Size size, int32_t nbColumns = 3, int32_t nbRows = 2); void Draw() override; virtual void AddChild(Widget* widget) override; virtual void OnButtonAPressed() override; @@ -14,6 +15,12 @@ namespace Codingfield { virtual void OnButtonCPressed() override; private: int32_t indexSelected = 0; + int32_t border = 5; + int32_t nbRows; + int32_t nbColumns; + + Size ComputeWidgetSize(); + Point ComputeWidgetPosition(const Size& widgetSize, int32_t position); }; } } From 834322f7a5822d0dfca3e5d8781226044a27f48a Mon Sep 17 00:00:00 2001 From: JF Date: Sun, 18 Mar 2018 16:31:50 +0100 Subject: [PATCH 03/14] Remove debug code --- src/WidgetMosaic.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index 417b676..b71e3be 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -47,9 +47,6 @@ Size WidgetMosaic::ComputeWidgetSize() { Size widgetSize; widgetSize.width = (size.width - ((2*border) + ((nbColumns-1)*border))) / nbColumns; widgetSize.height = (size.height - ((2*border) + ((nbRows-1)*border))) / nbRows; - - String str = String(widgetSize.width) + "x" + String(widgetSize.height); - Serial.println("Mosaic size = " + str); return widgetSize; } From 9cec641137467213924406eb8e46d5a4dbdbdf10 Mon Sep 17 00:00:00 2001 From: JF Date: Tue, 20 Mar 2018 20:45:05 +0100 Subject: [PATCH 04/14] Button : add title If no title is specified, the text is positionned at the center of the button. It it is specified, the text is positionned on the top, and the title on the bottom. --- src/Button.cpp | 14 +++++++++++++- src/Button.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Button.cpp b/src/Button.cpp index 086b87b..d9cbeac 100644 --- a/src/Button.cpp +++ b/src/Button.cpp @@ -34,6 +34,13 @@ void Codingfield::UI::Button::SetText(const std::string& t) { } } +void Codingfield::UI::Button::SetTitle(const std::string& t) { + if(title != t) { + title = t; + isUpdated = true; + } +} + void Codingfield::UI::Button::Draw() { if(isUpdated) { M5.Lcd.setTextColor(textColor); @@ -42,7 +49,12 @@ void Codingfield::UI::Button::Draw() { M5.Lcd.fillRect(position.x, position.y, size.width, size.height, backgroundColor); M5.Lcd.setTextDatum(MC_DATUM); M5.Lcd.setTextColor(textColor); - M5.Lcd.drawString(text.c_str(), position.x + (size.width/2), position.y + (size.height/2)); + if(title.empty()) + M5.Lcd.drawString(text.c_str(), position.x + (size.width/2), position.y + (size.height/2)); + else { + M5.Lcd.drawString(text.c_str(), position.x + (size.width/2), position.y + (size.height/3)); + M5.Lcd.drawString(title.c_str(), position.x + (size.width/2), position.y + ((size.height/3)*2)); + } if(isSelected) { M5.Lcd.drawRect(position.x, position.y, size.width, size.height, RED); diff --git a/src/Button.h b/src/Button.h index 767300e..cd6020f 100644 --- a/src/Button.h +++ b/src/Button.h @@ -12,11 +12,13 @@ namespace Codingfield { void SetTextColor(Color c); virtual void SetSelected(bool s) override; void SetText(const std::string& t); + void SetTitle(const std::string& t); void Draw() override; private: Color backgroundColor = BLACK; Color textColor = BLACK; std::string text; + std::string title; }; } From 893941b3a67aa11cf4173e46fcfb51a64a54311c Mon Sep 17 00:00:00 2001 From: JF Date: Tue, 20 Mar 2018 21:07:11 +0100 Subject: [PATCH 05/14] Add widget ButtonInfoBar This widget display the function of the 3 buttons on a bar (intended to be placed on the bottom on the screen). Update example. --- examples/simpleExample/main.cpp | 11 ++++++--- src/ButtonInfoBar.cpp | 44 +++++++++++++++++++++++++++++++++ src/ButtonInfoBar.h | 21 ++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 src/ButtonInfoBar.cpp create mode 100644 src/ButtonInfoBar.h diff --git a/examples/simpleExample/main.cpp b/examples/simpleExample/main.cpp index 908212b..4867f40 100644 --- a/examples/simpleExample/main.cpp +++ b/examples/simpleExample/main.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include #include @@ -9,7 +9,7 @@ using namespace Codingfield::UI; Screen screen(Size(320, 240), BLACK); StatusBar* topBar; -Bar* bottomBar; +ButtonInfoBar* bottomBar; Codingfield::UI::Button* button0; // M5STack should really use namespaces to avoid name clashes Codingfield::UI::Button* button1; Codingfield::UI::Button* button2; @@ -23,7 +23,7 @@ void setup() { M5.begin(); topBar = new StatusBar(&screen, Point(0,0), 25); - bottomBar = new Bar(&screen, Point(0, screen.GetSize().height-25), 25); + bottomBar = new ButtonInfoBar(&screen, Point(0, screen.GetSize().height-25), 25); mosaic = new WidgetMosaic(&screen, Point(5,30), Size(320, 190)); focus = mosaic; @@ -32,6 +32,7 @@ void setup() { button0->SetBackgroundColor(BLUE); button0->SetTextColor(WHITE); button0->SetText("16C"); + button0->SetTitle("Fridge"); button1 = new Codingfield::UI::Button(mosaic); button1->SetBackgroundColor(ORANGE); button1->SetTextColor(BLACK); @@ -56,6 +57,10 @@ void setup() { topBar->SetUptime(0); topBar->SetWifiStatus(StatusBar::WifiStatuses::No_signal); + bottomBar->SetButtonAText("<"); + bottomBar->SetButtonBText("Select"); + bottomBar->SetButtonCText(">"); + screen.Draw(); } diff --git a/src/ButtonInfoBar.cpp b/src/ButtonInfoBar.cpp new file mode 100644 index 0000000..c23367d --- /dev/null +++ b/src/ButtonInfoBar.cpp @@ -0,0 +1,44 @@ +#include "ButtonInfoBar.h" + +using namespace Codingfield::UI; + +void ButtonInfoBar::SetButtonAText(const std::string& t) { + if(btnAText != t) { + btnAText = t; + isUpdated = true; + } +} + +void ButtonInfoBar::SetButtonBText(const std::string& t) { + if(btnBText != t) { + btnBText = t; + isUpdated = true; + } +} + +void ButtonInfoBar::SetButtonCText(const std::string& t) { + if(btnCText != t) { + btnCText = t; + isUpdated = true; + } +} + +void ButtonInfoBar::Draw() { + bool oldIsUpdated = isUpdated; + Bar::Draw(); + + if(oldIsUpdated) { + M5.Lcd.setTextColor(BLACK); + M5.Lcd.setTextSize(2); + + M5.Lcd.setTextDatum(TC_DATUM); + M5.Lcd.drawString(btnAText.c_str(), (size.width/6), position.y + 5); + + M5.Lcd.setTextDatum(TC_DATUM); + M5.Lcd.drawString(btnBText.c_str(), size.width/2, position.y + 5); + + M5.Lcd.setTextDatum(TC_DATUM); + M5.Lcd.drawString(btnCText.c_str(), ((size.width/3)*2) + (size.width/6), position.y + 5); + } + isUpdated = false; +} diff --git a/src/ButtonInfoBar.h b/src/ButtonInfoBar.h new file mode 100644 index 0000000..9aeae8e --- /dev/null +++ b/src/ButtonInfoBar.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Bar.h" + +namespace Codingfield { + namespace UI { + class ButtonInfoBar : public Bar { + public: + ButtonInfoBar(Widget* parent, Point position, int32_t height) : Bar(parent, position, height) {} + void Draw() override; + void SetButtonAText(const std::string& t); + void SetButtonBText(const std::string& t); + void SetButtonCText(const std::string& t); + private: + Color color = WHITE; + std::string btnAText; + std::string btnBText; + std::string btnCText; + }; + } +} From db675ea66fe5349148a80b4b5b527deeb82fc2d9 Mon Sep 17 00:00:00 2001 From: JF Date: Wed, 21 Mar 2018 19:51:50 +0100 Subject: [PATCH 06/14] Widgets : Show() / Hide() + use FreeFonts --- src/Bar.cpp | 1 + src/Button.cpp | 16 +- src/ButtonInfoBar.cpp | 2 +- src/Free_Fonts.h | 377 ++++++++++++++++++++++++++++++++++++++++++ src/Screen.cpp | 5 + src/StatusBar.cpp | 8 +- src/Widget.cpp | 10 +- src/Widget.h | 11 +- src/WidgetMosaic.cpp | 7 + 9 files changed, 424 insertions(+), 13 deletions(-) create mode 100644 src/Free_Fonts.h diff --git a/src/Bar.cpp b/src/Bar.cpp index 91df31f..d793cf5 100644 --- a/src/Bar.cpp +++ b/src/Bar.cpp @@ -3,6 +3,7 @@ using namespace Codingfield::UI; void Bar::Draw() { + if(IsHidden()) return; if(isUpdated) M5.Lcd.fillRect(position.x, position.y, size.width, size.height, WHITE); diff --git a/src/Button.cpp b/src/Button.cpp index d9cbeac..a10d2ad 100644 --- a/src/Button.cpp +++ b/src/Button.cpp @@ -1,4 +1,5 @@ #include "Button.h" +#include "Free_Fonts.h" using namespace Codingfield::UI; @@ -23,36 +24,41 @@ void Codingfield::UI::Button::SetTextColor(Color c) { void Codingfield::UI::Button::SetSelected(bool s) { if(isSelected != s) { isSelected = s; - isUpdated = true; + SetUpdateFlag(); } } void Codingfield::UI::Button::SetText(const std::string& t) { if(text != t) { this->text = t; - isUpdated = true; + SetUpdateFlag(); } } void Codingfield::UI::Button::SetTitle(const std::string& t) { if(title != t) { title = t; - isUpdated = true; + SetUpdateFlag(); } } void Codingfield::UI::Button::Draw() { + if(IsHidden()) return; if(isUpdated) { M5.Lcd.setTextColor(textColor); - M5.Lcd.setTextSize(2); M5.Lcd.fillRect(position.x, position.y, size.width, size.height, backgroundColor); M5.Lcd.setTextDatum(MC_DATUM); M5.Lcd.setTextColor(textColor); - if(title.empty()) + 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); + } else { + M5.Lcd.setFreeFont(FF22); M5.Lcd.drawString(text.c_str(), position.x + (size.width/2), position.y + (size.height/3)); + M5.Lcd.setFreeFont(FF21); M5.Lcd.drawString(title.c_str(), position.x + (size.width/2), position.y + ((size.height/3)*2)); } diff --git a/src/ButtonInfoBar.cpp b/src/ButtonInfoBar.cpp index c23367d..67e36c9 100644 --- a/src/ButtonInfoBar.cpp +++ b/src/ButtonInfoBar.cpp @@ -24,12 +24,12 @@ void ButtonInfoBar::SetButtonCText(const std::string& t) { } void ButtonInfoBar::Draw() { + if(IsHidden()) return; bool oldIsUpdated = isUpdated; Bar::Draw(); if(oldIsUpdated) { M5.Lcd.setTextColor(BLACK); - M5.Lcd.setTextSize(2); M5.Lcd.setTextDatum(TC_DATUM); M5.Lcd.drawString(btnAText.c_str(), (size.width/6), position.y + 5); diff --git a/src/Free_Fonts.h b/src/Free_Fonts.h new file mode 100644 index 0000000..d701aec --- /dev/null +++ b/src/Free_Fonts.h @@ -0,0 +1,377 @@ +// Attach this header file to your sketch to use the GFX Free Fonts. You can write +// sketches without it, but it makes referencing them easier. + +// This calls up ALL the fonts but they only get loaded if you actually +// use them in your sketch. +// +// No changes are needed to this header file unless new fonts are added to the +// library "Fonts/GFXFF" folder. +// +// To save a lot of typing long names, each font can easily be referenced in the +// sketch in three ways, either with: +// +// 1. Font file name with the & in front such as &FreeSansBoldOblique24pt7b +// an example being: +// +// tft.setFreeFont(&FreeSansBoldOblique24pt7b); +// +// 2. FF# where # is a number determined by looking at the list below +// an example being: +// +// tft.setFreeFont(FF32); +// +// 3. An abbreviation of the file name. Look at the list below to see +// the abbreviations used, for example: +// +// tft.setFreeFont(FSSBO24) +// +// Where the letters mean: +// F = Free font +// M = Mono +// SS = Sans Serif (double S to distinguish is form serif fonts) +// S = Serif +// B = Bold +// O = Oblique (letter O not zero) +// I = Italic +// # = point size, either 9, 12, 18 or 24 +// +// Setting the font to NULL will select the GLCD font: +// +// tft.setFreeFont(NULL); // Set font to GLCD + +#define LOAD_GFXFF + +#ifdef LOAD_GFXFF // Only include the fonts if LOAD_GFXFF is defined in User_Setup.h + +// Use these when printing or drawing text in GLCD and high rendering speed fonts +#define GFXFF 1 +#define GLCD 0 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +// Use the following when calling setFont() +// +// Reserved for GLCD font // FF0 +// + +#define TT1 &TomThumb + +#define FM9 &FreeMono9pt7b +#define FM12 &FreeMono12pt7b +#define FM18 &FreeMono18pt7b +#define FM24 &FreeMono24pt7b + +#define FMB9 &FreeMonoBold9pt7b +#define FMB12 &FreeMonoBold12pt7b +#define FMB18 &FreeMonoBold18pt7b +#define FMB24 &FreeMonoBold24pt7b + +#define FMO9 &FreeMonoOblique9pt7b +#define FMO12 &FreeMonoOblique12pt7b +#define FMO18 &FreeMonoOblique18pt7b +#define FMO24 &FreeMonoOblique24pt7b + +#define FMBO9 &FreeMonoBoldOblique9pt7b +#define FMBO12 &FreeMonoBoldOblique12pt7b +#define FMBO18 &FreeMonoBoldOblique18pt7b +#define FMBO24 &FreeMonoBoldOblique24pt7b + +#define FSS9 &FreeSans9pt7b +#define FSS12 &FreeSans12pt7b +#define FSS18 &FreeSans18pt7b +#define FSS24 &FreeSans24pt7b + +#define FSSB9 &FreeSansBold9pt7b +#define FSSB12 &FreeSansBold12pt7b +#define FSSB18 &FreeSansBold18pt7b +#define FSSB24 &FreeSansBold24pt7b + +#define FSSO9 &FreeSansOblique9pt7b +#define FSSO12 &FreeSansOblique12pt7b +#define FSSO18 &FreeSansOblique18pt7b +#define FSSO24 &FreeSansOblique24pt7b + +#define FSSBO9 &FreeSansBoldOblique9pt7b +#define FSSBO12 &FreeSansBoldOblique12pt7b +#define FSSBO18 &FreeSansBoldOblique18pt7b +#define FSSBO24 &FreeSansBoldOblique24pt7b + +#define FS9 &FreeSerif9pt7b +#define FS12 &FreeSerif12pt7b +#define FS18 &FreeSerif18pt7b +#define FS24 &FreeSerif24pt7b + +#define FSI9 &FreeSerifItalic9pt7b +#define FSI12 &FreeSerifItalic12pt7b +#define FSI19 &FreeSerifItalic18pt7b +#define FSI24 &FreeSerifItalic24pt7b + +#define FSB9 &FreeSerifBold9pt7b +#define FSB12 &FreeSerifBold12pt7b +#define FSB18 &FreeSerifBold18pt7b +#define FSB24 &FreeSerifBold24pt7b + +#define FSBI9 &FreeSerifBoldItalic9pt7b +#define FSBI12 &FreeSerifBoldItalic12pt7b +#define FSBI18 &FreeSerifBoldItalic18pt7b +#define FSBI24 &FreeSerifBoldItalic24pt7b + +#define FF0 NULL //ff0 reserved for GLCD +#define FF1 &FreeMono9pt7b +#define FF2 &FreeMono12pt7b +#define FF3 &FreeMono18pt7b +#define FF4 &FreeMono24pt7b + +#define FF5 &FreeMonoBold9pt7b +#define FF6 &FreeMonoBold12pt7b +#define FF7 &FreeMonoBold18pt7b +#define FF8 &FreeMonoBold24pt7b + +#define FF9 &FreeMonoOblique9pt7b +#define FF10 &FreeMonoOblique12pt7b +#define FF11 &FreeMonoOblique18pt7b +#define FF12 &FreeMonoOblique24pt7b + +#define FF13 &FreeMonoBoldOblique9pt7b +#define FF14 &FreeMonoBoldOblique12pt7b +#define FF15 &FreeMonoBoldOblique18pt7b +#define FF16 &FreeMonoBoldOblique24pt7b + +#define FF17 &FreeSans9pt7b +#define FF18 &FreeSans12pt7b +#define FF19 &FreeSans18pt7b +#define FF20 &FreeSans24pt7b + +#define FF21 &FreeSansBold9pt7b +#define FF22 &FreeSansBold12pt7b +#define FF23 &FreeSansBold18pt7b +#define FF24 &FreeSansBold24pt7b + +#define FF25 &FreeSansOblique9pt7b +#define FF26 &FreeSansOblique12pt7b +#define FF27 &FreeSansOblique18pt7b +#define FF28 &FreeSansOblique24pt7b + +#define FF29 &FreeSansBoldOblique9pt7b +#define FF30 &FreeSansBoldOblique12pt7b +#define FF31 &FreeSansBoldOblique18pt7b +#define FF32 &FreeSansBoldOblique24pt7b + +#define FF33 &FreeSerif9pt7b +#define FF34 &FreeSerif12pt7b +#define FF35 &FreeSerif18pt7b +#define FF36 &FreeSerif24pt7b + +#define FF37 &FreeSerifItalic9pt7b +#define FF38 &FreeSerifItalic12pt7b +#define FF39 &FreeSerifItalic18pt7b +#define FF40 &FreeSerifItalic24pt7b + +#define FF41 &FreeSerifBold9pt7b +#define FF42 &FreeSerifBold12pt7b +#define FF43 &FreeSerifBold18pt7b +#define FF44 &FreeSerifBold24pt7b + +#define FF45 &FreeSerifBoldItalic9pt7b +#define FF46 &FreeSerifBoldItalic12pt7b +#define FF47 &FreeSerifBoldItalic18pt7b +#define FF48 &FreeSerifBoldItalic24pt7b + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Now we define "s"tring versions for easy printing of the font name so: +// tft.println(sFF5); +// will print +// Mono bold 9 +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define sFF0 "GLCD" +#define sTT1 "Tom Thumb" +#define sFF1 "Mono 9" +#define sFF2 "Mono 12" +#define sFF3 "Mono 18" +#define sFF4 "Mono 24" + +#define sFF5 "Mono bold 9" +#define sFF6 "Mono bold 12" +#define sFF7 "Mono bold 18" +#define sFF8 "Mono bold 24" + +#define sFF9 "Mono oblique 9" +#define sFF10 "Mono oblique 12" +#define sFF11 "Mono oblique 18" +#define sFF12 "Mono oblique 24" + +#define sFF13 "Mono bold oblique 9" +#define sFF14 "Mono bold oblique 12" +#define sFF15 "Mono bold oblique 18" +#define sFF16 "Mono bold oblique 24" // Full text line is too big for 480 pixel wide screen + +#define sFF17 "Sans 9" +#define sFF18 "Sans 12" +#define sFF19 "Sans 18" +#define sFF20 "Sans 24" + +#define sFF21 "Sans bold 9" +#define sFF22 "Sans bold 12" +#define sFF23 "Sans bold 18" +#define sFF24 "Sans bold 24" + +#define sFF25 "Sans oblique 9" +#define sFF26 "Sans oblique 12" +#define sFF27 "Sans oblique 18" +#define sFF28 "Sans oblique 24" + +#define sFF29 "Sans bold oblique 9" +#define sFF30 "Sans bold oblique 12" +#define sFF31 "Sans bold oblique 18" +#define sFF32 "Sans bold oblique 24" + +#define sFF33 "Serif 9" +#define sFF34 "Serif 12" +#define sFF35 "Serif 18" +#define sFF36 "Serif 24" + +#define sFF37 "Serif italic 9" +#define sFF38 "Serif italic 12" +#define sFF39 "Serif italic 18" +#define sFF40 "Serif italic 24" + +#define sFF41 "Serif bold 9" +#define sFF42 "Serif bold 12" +#define sFF43 "Serif bold 18" +#define sFF44 "Serif bold 24" + +#define sFF45 "Serif bold italic 9" +#define sFF46 "Serif bold italic 12" +#define sFF47 "Serif bold italic 18" +#define sFF48 "Serif bold italic 24" + +#else // LOAD_GFXFF not defined so setup defaults to prevent error messages + +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// Free fonts are not loaded in User_Setup.h so we must define all as font 1 +// to prevent compile error messages +// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +#define GFXFF 1 +#define GLCD 1 +#define FONT2 2 +#define FONT4 4 +#define FONT6 6 +#define FONT7 7 +#define FONT8 8 + +#define FF0 1 +#define FF1 1 +#define FF2 1 +#define FF3 1 +#define FF4 1 +#define FF5 1 +#define FF6 1 +#define FF7 1 +#define FF8 1 +#define FF9 1 +#define FF10 1 +#define FF11 1 +#define FF12 1 +#define FF13 1 +#define FF14 1 +#define FF15 1 +#define FF16 1 +#define FF17 1 +#define FF18 1 +#define FF19 1 +#define FF20 1 +#define FF21 1 +#define FF22 1 +#define FF23 1 +#define FF24 1 +#define FF25 1 +#define FF26 1 +#define FF27 1 +#define FF28 1 +#define FF29 1 +#define FF30 1 +#define FF31 1 +#define FF32 1 +#define FF33 1 +#define FF34 1 +#define FF35 1 +#define FF36 1 +#define FF37 1 +#define FF38 1 +#define FF39 1 +#define FF40 1 +#define FF41 1 +#define FF42 1 +#define FF43 1 +#define FF44 1 +#define FF45 1 +#define FF46 1 +#define FF47 1 +#define FF48 1 + +#define FM9 1 +#define FM12 1 +#define FM18 1 +#define FM24 1 + +#define FMB9 1 +#define FMB12 1 +#define FMB18 1 +#define FMB24 1 + +#define FMO9 1 +#define FMO12 1 +#define FMO18 1 +#define FMO24 1 + +#define FMBO9 1 +#define FMBO12 1 +#define FMBO18 1 +#define FMBO24 1 + +#define FSS9 1 +#define FSS12 1 +#define FSS18 1 +#define FSS24 1 + +#define FSSB9 1 +#define FSSB12 1 +#define FSSB18 1 +#define FSSB24 1 + +#define FSSO9 1 +#define FSSO12 1 +#define FSSO18 1 +#define FSSO24 1 + +#define FSSBO9 1 +#define FSSBO12 1 +#define FSSBO18 1 +#define FSSBO24 1 + +#define FS9 1 +#define FS12 1 +#define FS18 1 +#define FS24 1 + +#define FSI9 1 +#define FSI12 1 +#define FSI19 1 +#define FSI24 1 + +#define FSB9 1 +#define FSB12 1 +#define FSB18 1 +#define FSB24 1 + +#define FSBI9 1 +#define FSBI12 1 +#define FSBI18 1 +#define FSBI24 1 + +#endif // LOAD_GFXFF diff --git a/src/Screen.cpp b/src/Screen.cpp index a694762..9deaf1f 100644 --- a/src/Screen.cpp +++ b/src/Screen.cpp @@ -1,8 +1,13 @@ #include "Screen.h" +#include "Free_Fonts.h" using namespace Codingfield::UI; void Screen::Draw() { + M5.Lcd.setFreeFont(FF21); + M5.Lcd.setTextSize(1); + + if(IsHidden()) return; if(isUpdated) M5.Lcd.fillScreen(color); diff --git a/src/StatusBar.cpp b/src/StatusBar.cpp index 44b8557..df8ff4a 100644 --- a/src/StatusBar.cpp +++ b/src/StatusBar.cpp @@ -10,31 +10,31 @@ extern const uint8_t image_data_wifi3[]; void StatusBar::SetWifiStatus(const StatusBar::WifiStatuses status) { if(wifiStatus != status) { wifiStatus = status; - isUpdated = true; + SetUpdateFlag(); } } void StatusBar::SetUptime(const uint32_t t) { if(uptime != t) { uptime = t; - isUpdated = true; + SetUpdateFlag(); } } void StatusBar::SetDateTime(const std::string& t) { if(dateTime != t) { dateTime = t; - isUpdated = true; + SetUpdateFlag(); } } void StatusBar::Draw() { + if(IsHidden()) return; bool oldIsUpdated = isUpdated; Bar::Draw(); if(oldIsUpdated) { M5.Lcd.setTextColor(BLACK); - M5.Lcd.setTextSize(2); M5.Lcd.setTextDatum(TL_DATUM); String s = String("UP:") + String(uptime) + String("h"); diff --git a/src/Widget.cpp b/src/Widget.cpp index c94f49b..67edd8c 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -1,12 +1,20 @@ #include "Widget.h" + using namespace Codingfield::UI; Widget::Widget(Widget* parent) : Widget(parent, Point(0,0), Size(0,0)) { - + } Widget::Widget(Widget* parent, Point position, Size size) : parent{parent}, position{position}, size {size} { if(parent != nullptr) parent->AddChild(this); } + +void Widget::SetUpdateFlag() { + for(auto c : children) { + c->SetUpdateFlag(); + } + isUpdated = true; +} diff --git a/src/Widget.h b/src/Widget.h index efea336..9898a8e 100644 --- a/src/Widget.h +++ b/src/Widget.h @@ -12,14 +12,18 @@ namespace Codingfield { Widget(Widget* parent); Widget(Widget* parent, Point position, Size size); - Widget(Point position, Size size) : Widget(nullptr, position, size) {} - + void SetParent(Widget* parent) { this->parent = parent; } const Size& GetSize() const { return size; } void SetSize(const Size& s) { size = s; } const Point& GetPosition() const {return position; } void SetPosition(const Point& p) {position = p; } + void Show() { SetUpdateFlag(); isVisible = true; } + void Hide() { SetUpdateFlag(); isVisible = false; } + bool IsVisible() const { return isVisible;} + bool IsHidden() const { return !isVisible;} + virtual void AddChild(Widget* widget) { children.push_back(widget); } virtual void Draw() = 0; @@ -39,6 +43,9 @@ namespace Codingfield { std::vector children; bool isSelected = false; bool isUpdated = true; + bool isVisible = true; + + void SetUpdateFlag(); }; } diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index b71e3be..eaebf5a 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -9,8 +9,14 @@ WidgetMosaic::WidgetMosaic(Widget* parent, Point position, Size size, int32_t nb } void WidgetMosaic::Draw() { + if(IsHidden()) return; + if(isUpdated) + M5.Lcd.fillRect(position.x, position.y, size.width, size.height, BLACK); + for(Widget* w : children) w->Draw(); + + isUpdated = false; } void WidgetMosaic::AddChild(Widget* widget) { @@ -21,6 +27,7 @@ void WidgetMosaic::AddChild(Widget* widget) { int32_t position = ((children.size()-1) % (nbRows*nbColumns)); widget->SetPosition(ComputeWidgetPosition(widgetSize, position)); + SetUpdateFlag(); } void WidgetMosaic::OnButtonAPressed() { From d44eae928385642c6bb52dac97b9680919c2e82f Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 24 Mar 2018 13:38:47 +0100 Subject: [PATCH 07/14] Improve WidgetMosaic, add AppScreen AppScreen is a screen containing up to 3 widgets : top bar, bottom bar and centre widget. WidgetMosaic is now able to zoom on one of its sub-widget. --- src/AppScreen.cpp | 39 ++++++++++++++++++++++++++++ src/AppScreen.h | 21 +++++++++++++++ src/Bar.h | 1 + src/ButtonInfoBar.h | 1 + src/StatusBar.h | 2 ++ src/Widget.cpp | 11 +++++++- src/Widget.h | 13 ++++++---- src/WidgetMosaic.cpp | 61 +++++++++++++++++++++++++++++++++++++++++--- src/WidgetMosaic.h | 7 +++++ 9 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 src/AppScreen.cpp create mode 100644 src/AppScreen.h diff --git a/src/AppScreen.cpp b/src/AppScreen.cpp new file mode 100644 index 0000000..279afb6 --- /dev/null +++ b/src/AppScreen.cpp @@ -0,0 +1,39 @@ +#include "AppScreen.h" +#include + +using namespace Codingfield::UI; + +AppScreen::AppScreen(Size size, Color color) : Screen(size, color) { + +} + +AppScreen::AppScreen(Size size, Color color, Bar* topBar, Bar* bottomBar, Widget* centreWidget) : Screen(size, color), topBar{topBar}, bottomBar{bottomBar}, centreWidget{centreWidget} { + if(topBar != nullptr) { + topBar->SetParent(this); + topBar->SetPosition(Point(0,0)); + topBar->SetSize(Size(this->GetSize().width, barHeight)); + AddChild(topBar); + } + + if(bottomBar != nullptr) { + bottomBar->SetParent(this); + bottomBar->SetPosition(Point(0, this->GetSize().height-barHeight)); + bottomBar->SetSize(Size(this->GetSize().width, barHeight)); + AddChild(bottomBar); + } + + if(centreWidget != nullptr) { + Point centerPosition; + centerPosition.y = position.y + ((topBar != nullptr) ? topBar->GetSize().height : 0) + padding; + centerPosition.x = position.x + padding; + + Size centerSize; + centerSize.height = size.height - (((bottomBar != nullptr) ? bottomBar->GetSize().height : 0) + ((topBar != nullptr) ? topBar->GetSize().height : 0) + padding); + centerSize.width = size.width; + + centreWidget->SetParent(this); + centreWidget->SetPosition(centerPosition); + centreWidget->SetSize(centerSize); + AddChild(centreWidget); + } +} diff --git a/src/AppScreen.h b/src/AppScreen.h new file mode 100644 index 0000000..eee778a --- /dev/null +++ b/src/AppScreen.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace Codingfield { + namespace UI { + class Bar; + class AppScreen : public Screen { + public: + AppScreen(Size size, Color color); + AppScreen(Size size, Color color, Bar* topBar, Bar* bottomBar, Widget* centreWidget); + + private: + Bar* topBar = nullptr; + Bar* bottomBar = nullptr; + Widget* centreWidget = nullptr; + int32_t padding = 5; + int32_t barHeight = 25; + }; + } +} diff --git a/src/Bar.h b/src/Bar.h index 2dfde13..d83fd17 100644 --- a/src/Bar.h +++ b/src/Bar.h @@ -6,6 +6,7 @@ namespace Codingfield { namespace UI { class Bar : public Widget { public: + Bar() : Widget(nullptr, Point(), Size()) {} Bar(Widget* parent, Point position, int32_t height) : Widget(parent, position, Size(parent->GetSize().width, height)) {} void Draw() override; private: diff --git a/src/ButtonInfoBar.h b/src/ButtonInfoBar.h index 9aeae8e..890fe4c 100644 --- a/src/ButtonInfoBar.h +++ b/src/ButtonInfoBar.h @@ -6,6 +6,7 @@ namespace Codingfield { namespace UI { class ButtonInfoBar : public Bar { public: + ButtonInfoBar() : Bar() {} ButtonInfoBar(Widget* parent, Point position, int32_t height) : Bar(parent, position, height) {} void Draw() override; void SetButtonAText(const std::string& t); diff --git a/src/StatusBar.h b/src/StatusBar.h index f489d28..ecb05d8 100644 --- a/src/StatusBar.h +++ b/src/StatusBar.h @@ -7,11 +7,13 @@ namespace Codingfield { class StatusBar : public Bar { public: enum class WifiStatuses {No_signal, Weak, Medium, Full}; + StatusBar() : Bar() {} StatusBar(Widget* parent, Point position, int32_t height) : Bar(parent, position, height) {} void Draw() override; void SetWifiStatus(const WifiStatuses status); void SetUptime(const uint32_t t); void SetDateTime(const std::string& t); + private: Color color = WHITE; WifiStatuses wifiStatus; diff --git a/src/Widget.cpp b/src/Widget.cpp index 67edd8c..59b0ce8 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -4,7 +4,6 @@ using namespace Codingfield::UI; Widget::Widget(Widget* parent) : Widget(parent, Point(0,0), Size(0,0)) { - } Widget::Widget(Widget* parent, Point position, Size size) : parent{parent}, position{position}, size {size} { @@ -18,3 +17,13 @@ void Widget::SetUpdateFlag() { } isUpdated = true; } + +void Widget::SetSize(const Size& s) { + size = s; + OnSizeUpdated(); +} + +void Widget::SetPosition(const Point& p) { + position = p; + OnPositionUpdated(); +} diff --git a/src/Widget.h b/src/Widget.h index 9898a8e..e79aaf3 100644 --- a/src/Widget.h +++ b/src/Widget.h @@ -14,10 +14,10 @@ namespace Codingfield { void SetParent(Widget* parent) { this->parent = parent; } const Size& GetSize() const { return size; } - void SetSize(const Size& s) { size = s; } + void SetSize(const Size& s); const Point& GetPosition() const {return position; } - void SetPosition(const Point& p) {position = p; } + void SetPosition(const Point& p); void Show() { SetUpdateFlag(); isVisible = true; } void Hide() { SetUpdateFlag(); isVisible = false; } @@ -28,13 +28,16 @@ namespace Codingfield { virtual void Draw() = 0; - virtual void OnButtonAPressed() { } + virtual void OnSizeUpdated() { } + virtual void OnPositionUpdated() { } + virtual void OnButtonAPressed() { } virtual void OnButtonBPressed() { } - + virtual void OnButtonBLongPush() { } virtual void OnButtonCPressed() { } - virtual void SetSelected(bool s) { } + virtual void SetSelected(bool s) { + } protected: Point position; diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index eaebf5a..11e1184 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -8,13 +8,23 @@ WidgetMosaic::WidgetMosaic(Widget* parent, Point position, Size size, int32_t nb } +WidgetMosaic::WidgetMosaic(int32_t nbColumns, int32_t nbRows) : WidgetMosaic(nullptr, Point(), Size(), nbColumns, nbRows) { + +} + void WidgetMosaic::Draw() { if(IsHidden()) return; - if(isUpdated) + if(isUpdated) { M5.Lcd.fillRect(position.x, position.y, size.width, size.height, BLACK); + } - for(Widget* w : children) - w->Draw(); + if(!zoomOnSelected) { + for(Widget* w : children) + w->Draw(); + } + else { + selectedWidget->Draw(); + } isUpdated = false; } @@ -30,27 +40,72 @@ void WidgetMosaic::AddChild(Widget* widget) { SetUpdateFlag(); } +const Widget* WidgetMosaic::GetSelected() const { + return selectedWidget; +} + +void WidgetMosaic::ZoomOnSelected(bool enabled) { + bool oldValue = zoomOnSelected; + if(selectedWidget != nullptr && enabled) { + zoomOnSelected = true; + Size widgetSize = ComputeWidgetSize(1,1); + selectedWidget->SetSize(widgetSize); + selectedWidget->SetPosition(ComputeWidgetPosition(widgetSize, 0)); + } else { + zoomOnSelected = false; + + for(auto index = 0; index < children.size(); index++) { + Widget* widget = children.at(index); + Size widgetSize = ComputeWidgetSize(); + widget->SetSize(widgetSize); + widget->SetPosition(ComputeWidgetPosition(widgetSize, index)); + } + } + + if(oldValue != zoomOnSelected) + SetUpdateFlag(); +} + void WidgetMosaic::OnButtonAPressed() { + if(zoomOnSelected) + return selectedWidget->OnButtonAPressed(); + if(indexSelected > 0) { children[indexSelected]->SetSelected(false); indexSelected--; children[indexSelected]->SetSelected(true); + selectedWidget = children[indexSelected]; } } void WidgetMosaic::OnButtonBPressed() { + if(zoomOnSelected) + return selectedWidget->OnButtonBPressed(); + ZoomOnSelected(true); +} + +void WidgetMosaic::OnButtonBLongPush() { + ZoomOnSelected(false); } void WidgetMosaic::OnButtonCPressed() { + if(zoomOnSelected) + return selectedWidget->OnButtonCPressed(); + if(indexSelected < children.size()-1) { children[indexSelected]->SetSelected(false); indexSelected++; children[indexSelected]->SetSelected(true); + selectedWidget = children[indexSelected]; } } Size WidgetMosaic::ComputeWidgetSize() { + return ComputeWidgetSize(nbColumns, nbRows); +} + +Size WidgetMosaic::ComputeWidgetSize(int32_t nbColumns, int32_t nbRows) { Size widgetSize; widgetSize.width = (size.width - ((2*border) + ((nbColumns-1)*border))) / nbColumns; widgetSize.height = (size.height - ((2*border) + ((nbRows-1)*border))) / nbRows; diff --git a/src/WidgetMosaic.h b/src/WidgetMosaic.h index a3fa395..5e4a436 100644 --- a/src/WidgetMosaic.h +++ b/src/WidgetMosaic.h @@ -7,12 +7,15 @@ namespace Codingfield { namespace UI { class WidgetMosaic : public Widget { public: + WidgetMosaic(int32_t nbColumns = 3, int32_t nbRows = 2); WidgetMosaic(Widget* parent, Point position, Size size, int32_t nbColumns = 3, int32_t nbRows = 2); void Draw() override; virtual void AddChild(Widget* widget) override; virtual void OnButtonAPressed() override; virtual void OnButtonBPressed() override; + virtual void OnButtonBLongPush() override; virtual void OnButtonCPressed() override; + const Widget* GetSelected() const; private: int32_t indexSelected = 0; int32_t border = 5; @@ -20,7 +23,11 @@ namespace Codingfield { int32_t nbColumns; Size ComputeWidgetSize(); + Size ComputeWidgetSize(int32_t nbColumns, int32_t nbRows); Point ComputeWidgetPosition(const Size& widgetSize, int32_t position); + Widget* selectedWidget = nullptr; + bool zoomOnSelected = false; + void ZoomOnSelected(bool enabled); }; } } From 6528426797ef7c7443514004d1abf2af684fcf53 Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 24 Mar 2018 17:36:48 +0100 Subject: [PATCH 08/14] Add callback on WidgetMosaic Add callback function is called when the mosaic goes from full to zoom view and vice versa. --- src/AppScreen.cpp | 20 ++++++++++++++++++++ src/AppScreen.h | 5 +++++ src/Button.h | 2 +- src/Widget.h | 11 +++++++++-- src/WidgetMosaic.cpp | 18 +++++++++++++++++- src/WidgetMosaic.h | 3 +++ 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/AppScreen.cpp b/src/AppScreen.cpp index 279afb6..27c6c71 100644 --- a/src/AppScreen.cpp +++ b/src/AppScreen.cpp @@ -37,3 +37,23 @@ AppScreen::AppScreen(Size size, Color color, Bar* topBar, Bar* bottomBar, Widget AddChild(centreWidget); } } + +void AppScreen::OnButtonAPressed() { + if(centreWidget != nullptr) + centreWidget->OnButtonAPressed(); +} + +void AppScreen::OnButtonBPressed() { + if(centreWidget != nullptr) + centreWidget->OnButtonBPressed(); +} + +void AppScreen::OnButtonBLongPush() { + if(centreWidget != nullptr) + centreWidget->OnButtonBLongPush(); +} + +void AppScreen::OnButtonCPressed() { + if(centreWidget != nullptr) + centreWidget->OnButtonCPressed(); +} diff --git a/src/AppScreen.h b/src/AppScreen.h index eee778a..da0a2b0 100644 --- a/src/AppScreen.h +++ b/src/AppScreen.h @@ -10,6 +10,11 @@ namespace Codingfield { AppScreen(Size size, Color color); AppScreen(Size size, Color color, Bar* topBar, Bar* bottomBar, Widget* centreWidget); + virtual void OnButtonAPressed() override; + virtual void OnButtonBPressed() override; + virtual void OnButtonBLongPush() override; + virtual void OnButtonCPressed() override; + private: Bar* topBar = nullptr; Bar* bottomBar = nullptr; diff --git a/src/Button.h b/src/Button.h index cd6020f..b49b05d 100644 --- a/src/Button.h +++ b/src/Button.h @@ -14,7 +14,7 @@ namespace Codingfield { void SetText(const std::string& t); void SetTitle(const std::string& t); void Draw() override; - private: + protected: Color backgroundColor = BLACK; Color textColor = BLACK; std::string text; diff --git a/src/Widget.h b/src/Widget.h index e79aaf3..6836e27 100644 --- a/src/Widget.h +++ b/src/Widget.h @@ -23,6 +23,9 @@ namespace Codingfield { void Hide() { SetUpdateFlag(); isVisible = false; } bool IsVisible() const { return isVisible;} bool IsHidden() const { return !isVisible;} + bool IsEditable() const { return isEditable; } + bool ContainsValue() const { return containsValue; } + virtual void AddChild(Widget* widget) { children.push_back(widget); } @@ -36,8 +39,10 @@ namespace Codingfield { virtual void OnButtonBLongPush() { } virtual void OnButtonCPressed() { } - virtual void SetSelected(bool s) { - } + virtual void SetSelected(bool s) { } + virtual void EnableControls() {} + virtual void DisableControls() {} + protected: Point position; @@ -47,6 +52,8 @@ namespace Codingfield { bool isSelected = false; bool isUpdated = true; bool isVisible = true; + bool isEditable = false; + bool containsValue = false; void SetUpdateFlag(); }; diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index 11e1184..e1adc64 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -51,6 +51,9 @@ void WidgetMosaic::ZoomOnSelected(bool enabled) { Size widgetSize = ComputeWidgetSize(1,1); selectedWidget->SetSize(widgetSize); selectedWidget->SetPosition(ComputeWidgetPosition(widgetSize, 0)); + if(selectedWidget->IsEditable()) { + selectedWidget->EnableControls(); + } } else { zoomOnSelected = false; @@ -59,11 +62,16 @@ void WidgetMosaic::ZoomOnSelected(bool enabled) { Size widgetSize = ComputeWidgetSize(); widget->SetSize(widgetSize); widget->SetPosition(ComputeWidgetPosition(widgetSize, index)); + if(selectedWidget->IsEditable()) { + selectedWidget->DisableControls(); + } } } - if(oldValue != zoomOnSelected) + if(oldValue != zoomOnSelected) { + zoomOnSelectedCallback(selectedWidget, zoomOnSelected); SetUpdateFlag(); + } } void WidgetMosaic::OnButtonAPressed() { @@ -122,3 +130,11 @@ Point WidgetMosaic::ComputeWidgetPosition(const Size& widgetSize, int32_t positi return widgetPosition; } + +bool WidgetMosaic::IsZoomOnSelected() const { + return zoomOnSelected; +} + +void WidgetMosaic::SetZoomOnSelectedCallback(std::functionfunc) { + zoomOnSelectedCallback = func; +} diff --git a/src/WidgetMosaic.h b/src/WidgetMosaic.h index 5e4a436..bddb63a 100644 --- a/src/WidgetMosaic.h +++ b/src/WidgetMosaic.h @@ -16,6 +16,8 @@ namespace Codingfield { virtual void OnButtonBLongPush() override; virtual void OnButtonCPressed() override; const Widget* GetSelected() const; + bool IsZoomOnSelected() const; + void SetZoomOnSelectedCallback(std::function func); private: int32_t indexSelected = 0; int32_t border = 5; @@ -28,6 +30,7 @@ namespace Codingfield { Widget* selectedWidget = nullptr; bool zoomOnSelected = false; void ZoomOnSelected(bool enabled); + std::function zoomOnSelectedCallback = nullptr; }; } } From 69d52e82f8718f882ab7204609ab54f96d663e9d Mon Sep 17 00:00:00 2001 From: JF Date: Mon, 26 Mar 2018 21:01:58 +0200 Subject: [PATCH 09/14] WidgetMosaic : return to mosaic with btn B In zoom mode, btn B return to mosaic mode --- src/WidgetMosaic.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/WidgetMosaic.cpp b/src/WidgetMosaic.cpp index e1adc64..39a283c 100644 --- a/src/WidgetMosaic.cpp +++ b/src/WidgetMosaic.cpp @@ -87,13 +87,18 @@ void WidgetMosaic::OnButtonAPressed() { } void WidgetMosaic::OnButtonBPressed() { - if(zoomOnSelected) - return selectedWidget->OnButtonBPressed(); + if(zoomOnSelected) { + ZoomOnSelected(false); + selectedWidget->OnButtonBPressed(); + return; + } ZoomOnSelected(true); } void WidgetMosaic::OnButtonBLongPush() { + if(zoomOnSelected) + selectedWidget->OnButtonBLongPush(); ZoomOnSelected(false); } From 5efd53fc3b8cefb331724287f560e68598e6d101 Mon Sep 17 00:00:00 2001 From: JF Date: Wed, 28 Mar 2018 20:43:48 +0200 Subject: [PATCH 10/14] Add widget UpDownButton --- src/UpDownButton.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++ src/UpDownButton.h | 36 +++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/UpDownButton.cpp create mode 100644 src/UpDownButton.h diff --git a/src/UpDownButton.cpp b/src/UpDownButton.cpp new file mode 100644 index 0000000..0caee4f --- /dev/null +++ b/src/UpDownButton.cpp @@ -0,0 +1,75 @@ +#include "UpDownButton.h" +#include "Free_Fonts.h" +using namespace Codingfield::UI; + +UpDownButton::UpDownButton(Widget* parent) : Button(parent) { + isEditable = true; +} + +void UpDownButton::Draw() { + if(IsHidden()) return; + + if(isUpdated) { + Button::Draw(); + if(controlsEnabled) { + M5.Lcd.setTextDatum(MC_DATUM); + M5.Lcd.setTextColor(textColor); + M5.Lcd.drawString("-", position.x + (size.width/6), position.y + (size.height/2)); + M5.Lcd.drawString("+", position.x + (size.width - (size.width/6)), position.y + (size.height/2)); + } + } + + isUpdated = false; +} + +void UpDownButton::EnableControls() { + controlsEnabled = true; +} + +void UpDownButton::DisableControls() { + controlsEnabled = false; +} + +void UpDownButton::OnButtonAPressed() { + if(upCallback != nullptr) { + if(downCallback(this)) + SetUpdateFlag(); + } +} + +void UpDownButton::OnButtonBPressed() { + if(applyCallback != nullptr) { + if(applyCallback(this)) + SetUpdateFlag(); + } +} + +void UpDownButton::OnButtonBLongPush() { + if(cancelCallback != nullptr) { + if(cancelCallback(this)) + SetUpdateFlag(); + } +} + +void UpDownButton::OnButtonCPressed() { + if(upCallback != nullptr) { + if(upCallback(this)) + SetUpdateFlag(); + } +} + +void UpDownButton::SetUpCallback(std::function callback) { + upCallback = callback; +} + +void UpDownButton::SetDownCallback(std::function callback) { + downCallback = callback; +} + +void UpDownButton::SetApplyCallback(std::function callback) { + applyCallback = callback; +} + +void UpDownButton::SetCancelCallback(std::function callback) { + cancelCallback = callback; +} diff --git a/src/UpDownButton.h b/src/UpDownButton.h new file mode 100644 index 0000000..5104780 --- /dev/null +++ b/src/UpDownButton.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Button.h" + +namespace Codingfield { + namespace UI { + class UpDownButton : public Button { + public: + UpDownButton(Widget* parent); + virtual void Draw(); + + virtual void EnableControls() override; + virtual void DisableControls() override; + + virtual void OnButtonAPressed() override; + virtual void OnButtonBPressed() override; + virtual void OnButtonBLongPush() override; + virtual void OnButtonCPressed() override; + + void SetUpCallback(std::function callback); + void SetDownCallback(std::function callback); + void SetApplyCallback(std::function callback); + void SetCancelCallback(std::function callback); + + private: + bool controlsEnabled = false; + int32_t currentValue = 0; + int32_t oldValue = 0; + + std::function upCallback = nullptr; + std::function downCallback = nullptr; + std::function applyCallback = nullptr; + std::function cancelCallback = nullptr; + }; + } +} From 67880f27030725d428bc6f10bd2739ca1c8a8744 Mon Sep 17 00:00:00 2001 From: JF Date: Wed, 28 Mar 2018 21:21:48 +0200 Subject: [PATCH 11/14] Clean and command Widget --- src/Widget.h | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/Widget.h b/src/Widget.h index 6836e27..512c2d4 100644 --- a/src/Widget.h +++ b/src/Widget.h @@ -7,44 +7,62 @@ namespace Codingfield { namespace UI { using Color = uint32_t; + + /* Base class for all widgets + * A widget is an object that has a parent widget, children, a position and a size, + * and is able to draw itself on the screen.*/ class Widget { public: Widget(Widget* parent); Widget(Widget* parent, Point position, Size size); void SetParent(Widget* parent) { this->parent = parent; } + const Size& GetSize() const { return size; } void SetSize(const Size& s); const Point& GetPosition() const {return position; } void SetPosition(const Point& p); + /* The widget will be visible (drawn) the next time Draw() will be called */ void Show() { SetUpdateFlag(); isVisible = true; } + + /* The widget will not be visible (not drawn) the next time Draw() will be called */ void Hide() { SetUpdateFlag(); isVisible = false; } + bool IsVisible() const { return isVisible;} bool IsHidden() const { return !isVisible;} - bool IsEditable() const { return isEditable; } - bool ContainsValue() const { return containsValue; } + /* Returns true if the value of the widget can be edited via buttons */ + bool IsEditable() const { return isEditable; } + /* Add a widget as a child of this one */ virtual void AddChild(Widget* widget) { children.push_back(widget); } + /* Draw the widget. Implemented by concrete implementations of Widget */ virtual void Draw() = 0; - virtual void OnSizeUpdated() { } - virtual void OnPositionUpdated() { } - + /* These methods must be called by the application when buttons are pressed + * They allow the implementation of Widget to react to user actions on + * physical buttons. */ virtual void OnButtonAPressed() { } virtual void OnButtonBPressed() { } virtual void OnButtonBLongPush() { } virtual void OnButtonCPressed() { } + /* This method must be called by the application to specify is this instance + * of Widget is selected or not. */ virtual void SetSelected(bool s) { } + + /* Enables/disables controls on the button */ virtual void EnableControls() {} virtual void DisableControls() {} - protected: + virtual void OnSizeUpdated() { } + virtual void OnPositionUpdated() { } + void SetUpdateFlag(); + Point position; Size size; Widget* parent = nullptr; @@ -53,10 +71,6 @@ namespace Codingfield { bool isUpdated = true; bool isVisible = true; bool isEditable = false; - bool containsValue = false; - - void SetUpdateFlag(); }; - } } From 10ff4b660819167d3b1c4b32cdbac2a95a4207dc Mon Sep 17 00:00:00 2001 From: JF Date: Wed, 28 Mar 2018 21:22:24 +0200 Subject: [PATCH 12/14] Update example Update example with new functionality (UpDownButton, callbacks,...) --- examples/simpleExample/main.cpp | 146 ++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 36 deletions(-) diff --git a/examples/simpleExample/main.cpp b/examples/simpleExample/main.cpp index 4867f40..b5cf699 100644 --- a/examples/simpleExample/main.cpp +++ b/examples/simpleExample/main.cpp @@ -1,32 +1,44 @@ #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include using namespace Codingfield::UI; -Screen screen(Size(320, 240), BLACK); +AppScreen* screen; StatusBar* topBar; ButtonInfoBar* bottomBar; -Codingfield::UI::Button* button0; // M5STack should really use namespaces to avoid name clashes +Codingfield::UI::Button* button0; Codingfield::UI::Button* button1; -Codingfield::UI::Button* button2; +Codingfield::UI::UpDownButton* button2; Codingfield::UI::Button* button3; Codingfield::UI::Button* button4; Codingfield::UI::Button* button5; WidgetMosaic* mosaic; Widget* focus; +int32_t editButtonValue = 0; +int32_t editOldButtonValue = 0; + void setup() { M5.begin(); - topBar = new StatusBar(&screen, Point(0,0), 25); - bottomBar = new ButtonInfoBar(&screen, Point(0, screen.GetSize().height-25), 25); + // Instanciate and configure all widgets + topBar = new StatusBar(); + bottomBar = new ButtonInfoBar(); + mosaic = new WidgetMosaic(3, 2); + screen = new AppScreen(Size(320, 240), BLACK, topBar, bottomBar, mosaic); - mosaic = new WidgetMosaic(&screen, Point(5,30), Size(320, 190)); - focus = mosaic; + // Give the focus to the main screen + focus = screen; button0 = new Codingfield::UI::Button(mosaic); button0->SetBackgroundColor(BLUE); @@ -37,10 +49,10 @@ void setup() { button1->SetBackgroundColor(ORANGE); button1->SetTextColor(BLACK); button1->SetText("50%"); - button2 = new Codingfield::UI::Button(mosaic); + button2 = new Codingfield::UI::UpDownButton(mosaic); // Up/Down button button2->SetBackgroundColor(YELLOW); button2->SetTextColor(BLACK); - button2->SetText("3h15"); + button2->SetText("0"); button3 = new Codingfield::UI::Button(mosaic); button3->SetBackgroundColor(PURPLE); button3->SetTextColor(WHITE); @@ -58,10 +70,62 @@ void setup() { topBar->SetWifiStatus(StatusBar::WifiStatuses::No_signal); bottomBar->SetButtonAText("<"); - bottomBar->SetButtonBText("Select"); + bottomBar->SetButtonBText("SELECT"); bottomBar->SetButtonCText(">"); - screen.Draw(); + // Callback called by the mosaic when it changes mode (mosaic/zoom on 1 widget) + // We use it to update the bottom bar. + mosaic->SetZoomOnSelectedCallback([bottomBar](Widget* widget, bool edit) { + if(edit) { + if(widget->IsEditable()){ + bottomBar->SetButtonAText("-"); + bottomBar->SetButtonBText("APPLY"); + bottomBar->SetButtonCText("+"); + } else { + bottomBar->SetButtonAText(""); + bottomBar->SetButtonBText("BACK"); + bottomBar->SetButtonCText(""); + } + } else { + bottomBar->SetButtonAText("<"); + bottomBar->SetButtonBText("SELECT"); + bottomBar->SetButtonCText(">"); + } + }); + + // Configure callback to be called when the user wants to increment the value + // of button2 + button2->SetUpCallback([&editButtonValue](UpDownButton* w) { + editButtonValue++; + w->SetText(String(editButtonValue).c_str()); + return true; + }); + + // Configure callback to be called when the user wants to decrement the value + // of button2 + button2->SetDownCallback([&editButtonValue](UpDownButton* w) { + editButtonValue--; + w->SetText(String(editButtonValue).c_str()); + return true; + }); + + // Configure callback to be called when the user wants to apply the value + // of button2 + button2->SetApplyCallback([&editButtonValue, &editOldButtonValue](UpDownButton* w) { + editOldButtonValue = editButtonValue; + return false; + }); + + // Configure callback to be called when the user wants to cancel modification + // of the value of button2 + button2->SetCancelCallback([&editButtonValue](UpDownButton* w) { + editButtonValue = editOldButtonValue; + w->SetText(String(editButtonValue).c_str()); + return true; + }); + + // Draw the screen and all its children + screen->Draw(); } uint32_t loopCount = 0; @@ -69,16 +133,16 @@ uint32_t temperature = 10; uint32_t percent = 0; std::string state = "BUSY"; int32_t wifiStatusIndex; -int rssi = -100; -int seconds = 0; std::vector wifiStatus {StatusBar::WifiStatuses::No_signal, StatusBar::WifiStatuses::Weak, StatusBar::WifiStatuses::Medium, StatusBar::WifiStatuses::Full}; int32_t uptimeHours=0; +bool longPush = false; void loop() { M5.update(); + // Update values displayd on the screen (status bar, buttons,...) if((loopCount % 50) == 0) { if(temperature < 100) temperature++; @@ -99,43 +163,53 @@ void loop() { topBar->SetUptime(uptimeHours); char strftime_buf[64]; - if(seconds < 60) - seconds++; - else - seconds = 0; - snprintf(strftime_buf, 64, "%02d:%02d:%02d", 1, 2, seconds); + snprintf(strftime_buf, 64, "%02d:%02d:%02d", 12, 14, 59); topBar->SetDateTime(strftime_buf); - - if(rssi < 0) - rssi++; - else - rssi = -100; - if(rssi >= -55) - topBar->SetWifiStatus(StatusBar::WifiStatuses::Full); - else if(rssi >= -75) - topBar->SetWifiStatus(StatusBar::WifiStatuses::Medium); - else if(rssi >= -85) - topBar->SetWifiStatus(StatusBar::WifiStatuses::Weak); - else - topBar->SetWifiStatus(StatusBar::WifiStatuses::No_signal); } if((loopCount % 100) == 0) { if(state == "BUSY") state = "IDLE"; else state = "BUSY"; button5->SetText(state); + + auto rssi =WiFi.RSSI(); + if(rssi >= -55) { + topBar->SetWifiStatus(StatusBar::WifiStatuses::Full); + } else if(rssi >= -75) { + topBar->SetWifiStatus(StatusBar::WifiStatuses::Medium); + } else if(rssi >= -85) { + topBar->SetWifiStatus(StatusBar::WifiStatuses::Weak); + } else { + topBar->SetWifiStatus(StatusBar::WifiStatuses::No_signal); + } } - if(M5.BtnA.wasPressed()) + // Notify the widgets that physical buttons are pressed + if(M5.BtnA.wasPressed()) { focus->OnButtonAPressed(); + } - if(M5.BtnB.wasPressed()) - focus->OnButtonBPressed(); + if(M5.BtnB.pressedFor(1000)) { + if(!longPush) { + focus->OnButtonBLongPush(); + longPush = true; + } + } + else if(M5.BtnB.wasReleased()) { + if(!longPush) { + focus->OnButtonBPressed(); + } + else { + longPush = false; + } + } - if(M5.BtnC.wasPressed()) + if(M5.BtnC.wasPressed()) { focus->OnButtonCPressed(); + } - screen.Draw(); + // Redraw the screen + screen->Draw(); loopCount++; delay(10); From 3b6371ee567bacb56fa959982e4a6ec1f31340b8 Mon Sep 17 00:00:00 2001 From: JF002 Date: Sun, 1 Apr 2018 19:06:13 +0200 Subject: [PATCH 13/14] Update README.md --- README.md | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9738b9c..d1dbf27 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,39 @@ # cfGUI A simple GUI library for M5Stack (ESP32) -This library is intended to run on M5Stack development board (http://www.m5stack.com/). However, it should be easy to port it to any other board. +This library is intended to run on M5Stack development board (http://www.m5stack.com/), base on ESP32. + +However, it should be easy to port it to any other board with an LCD screen and 3 physical buttons. + It's composed of a simple hierarchy of graphical widget that can be drawn on screen. The following widgets have been created so far: - - Screen + - Screen and AppScreen (screen with a top bar and a bottom bar) - Bar - - StatusBar (with uptime, current time and wifi signal) - - Button - - Mosaic of widge + - StatusBar (Bar with uptime, clock and Wifi signal) + - ButtonInfoBar (display the function of the physical button) + - Button, UpDownButton + - Mosaic of widgets ![Example picture of cfGUI running on a M5Stack board](https://mastodon.codingfield.com/media/oXYl3M6SVqcpn2iefrs) - + +# How To (Platform.io) +To use this library with your platform.io project, simply clone or download the library into the directory 'lib' of your project. +Then, you just need to include the headers (e.g. #include ) and write some code. + +Look at examples if you need some inspiration ;-) # Todo - - Remove hard-coded values to make the lib more flexible + - Remove hard-coded values to make the lib more flexible - Add new widgets - Better 'focus' management +# Changelog +## 0.2.0 + - Change default font (looks better) + - New widgets (AppScreen, StatusBar, ButtonInfoBar, UpDownButton) + - Improved mosaic (the size and position of the widget is not hard-coded anymore) + - Global improvements + +## 0.1.0 + - First version of the library, with basic functionalities From 25958c32b676b6b314e8ed579bd3e366edae42f5 Mon Sep 17 00:00:00 2001 From: JF002 Date: Sun, 1 Apr 2018 19:21:08 +0200 Subject: [PATCH 14/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1dbf27..106e36a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The following widgets have been created so far: - Button, UpDownButton - Mosaic of widgets -![Example picture of cfGUI running on a M5Stack board](https://mastodon.codingfield.com/media/oXYl3M6SVqcpn2iefrs) +![Example picture of cfGUI running on a M5Stack board](https://mastodon.codingfield.com/system/media_attachments/files/000/207/740/original/dbacf24f45561e5c.jpg) # How To (Platform.io) To use this library with your platform.io project, simply clone or download the library into the directory 'lib' of your project.