Number_Counter¶
Beschreibung¶
Diese Klasse kann Zahlen in einem Intervall hoch- und runterzählen. Sie erbt von Number_Input.
Beispielbild¶
Dieses Bild dient nur der Veranschaulichung, wie Objekte dieser Klasse aussehen können.
new Number_Counter("symb:TriangleLeft", "symb:TriangleRight", COLOR_YELLOW, true, TML_empty_slider, &numberValue)
Funktionen¶
inline |
Number_Counter (const char* item1, const char* item2, const Color& color, const bool show_number, const std::function<void(int)> slider_callback, ExternalNumberValue* value=nullptr) |
inline |
|
inline void |
draw () |
inline void |
setTouch (Inputs& input) override |
inline bool |
checkSize (uint16_t sizeX, uint16_t sizeY, uint8_t rotation) override |
inline void |
drawItems () |
inline void |
loop (Inputs& input) override |
Variablen und Konstanten¶
const „Color“ |
|
const uint16_t |
showNumber = false |
const uint16_t |
padding = 5 |
const uint16_t |
borderStrength = 4 |
const uint16_t |
borderRadius = 6 |
const uint16_t |
triangleDistance = 1.5*padding + borderStrength |
const uint16_t |
|
uint16_t |
|
bool |
isVertical = false |
const uint16_t |
time = 200 |
bool |
activateAnimation = true |
unsigned long |
timerTriang1 = 0 |
unsigned long |
timerTriang2 = 0 |
Funktionen Beschreibung¶
inline Number_Counter(const char* item1, const char* item2, const Color& color, const bool show_number, const std::function<void(int)> slider_callback, ExternalNumberValue* value=nullptr)¶
1 inline Number_Counter(const char* item1, const char* item2, const Color& color, const bool show_number, const std::function<void(int)> slider_callback, ExternalNumberValue* value=nullptr):
2 NumberInput(slider_callback, value),
3 color(color),
4 item1(Display::createItem(item1, color.getItemColor() + Color(color.getItemColor(), -50))), //Second color is by default a funkier variant of the main item colour
5 item2(Display::createItem(item2, color.getSecondaryItemColor() + Color(color.getSecondaryItemColor(), -50))),
6 showNumber(show_number)
7 {}
Dieser Konstruktor nimmt als Parameter die zwei Strings, die die Items item1 und item2 repräsentieren sollen, die auf dem Objekt als Eingabetaste
angezeigt werden sollen, eine Farbe color für die Items, showNumber um anzugeben, ob der vom Slider eingestellte Zahlenwert angezeigt werden soll oder nicht,
eine Callback Funktion slider_callback (siehe slider_callback) und einem value, der angibt, in welchem Zustand sich das Objekt befindet.
In der Initialisierungsliste erfolgt ein Funktionsaufruf vom NumberInput Konstruktor und die Initialisierung von
color und showNumber mit den entsprechenden Parametern. item1 wird initialsiert, indem mit
createItem ein Item aus dem item1 String erstelt wird. Das zweite Argument setzt sich zusammen aus der Hauptitemfarbe color.getItemColor
und der Farbe, die aus dem Color Konstruktoraufruf entsteht. Die -50 wird als Argument übergeben, um eine veränderte Farbe als die Ursprungsfarbe zu erhalten.
Für item2 läuft die Initialisierung ähnlich ab, nur wird hier stattdessen die Zweititemfarbe color.getSecondaryItemColor als Basis verwendet.
inline ~Number_Counter()¶
1 inline ~Number_Counter() {
2 delete item1;
3 delete item2;
4 }
Der Destruktor wird bei der Zerstörung eines Number_Counter Objektes aufgerufen, löscht item1 und item2 und
gibt den allokierten Speicherplatz frei.
inline void draw()¶
1 inline void draw() {
2
3 display->rect_center(posX + sizeX/2, posY + sizeY/2, sizeX - 2*padding, sizeY - 2*padding, borderStrength, borderRadius, color.getBorderColor(), color);
4 if (showNumber) {
5 char buffer[8];
6 snprintf(buffer, sizeof(buffer), "%u", value);
7 display->text_center(posX + sizeX/2, posY + sizeY/2, fontSize, buffer, color.getSecondaryBorderColor());
8 }
9
10 drawItems();
11 }
Diese Funktion ist für das Zeichnen des Objektes verantwortlich.
display->rect_center(posX + sizeX/2, posY + sizeY/2, sizeX - 2*padding, sizeY - 2*padding, borderStrength, borderRadius, color.getBorderColor(), color);
Zuerst wird mit rect_center ein Rechteck gezeichnet, das als Grundlage für das Objekt dient.
if (showNumber) {
char buffer[8];
snprintf(buffer, sizeof(buffer), "%u", value);
display->text_center(posX + sizeX/2, posY + sizeY/2, fontSize, buffer, color.getSecondaryBorderColor());
}
Sollte showNumber true sein, so soll der aktuelle Wert auf dem Objekt angezeigt werden.
Zunächst wird value mit snprintf in einen Puffer (buffer) konvertiert, ehe mit rect_center der Wert in der Mitte des Objektes angezeigt wird.
drawItems();
Zum Schluss werden die drawItems die beiden Items item1 und item2
auf dem Rechteck gezeichnet. Das Number_Counter Objekt ist fertig.
inline void setTouch(Inputs& input) override¶
1 inline void setTouch(Inputs& input) override {
2
3 uint16_t rect1_x1, rect1_y1, rect1_x2, rect1_y2;
4 uint16_t rect2_x1, rect2_y1, rect2_x2, rect2_y2;
5 auto tmp = value;
6
7 if (isVertical) {
8 rect1_x1 = posX + padding;
9 rect1_y1 = posY + sizeY - triangleHight - triangleDistance;
10 rect1_x2 = posX + sizeX - padding;
11 rect1_y2 = posY + sizeY - padding;
12
13 rect2_x1 = rect1_x1;
14 rect2_y1 = posY + padding;
15 rect2_x2 = rect1_x2;
16 rect2_y2 = posY + triangleHight + triangleDistance;
17 } else {
18 rect1_x1 = posX + padding;
19 rect1_y1 = posY + padding;
20 rect1_x2 = posX + triangleHight + triangleDistance;
21 rect1_y2 = posY + sizeY - padding;
22
23 rect2_x1 = posX + sizeX - triangleHight - triangleDistance;
24 rect2_y1 = rect1_y1;
25 rect2_x2 = posX + sizeX - triangleDistance;
26 rect2_y2 = rect1_y2;
27 }
28
29 if (input.touchX >= rect1_x1 && input.touchX <= rect1_x2 && input.touchY >= rect1_y1 && input.touchY <= rect1_y2) {
30 LOGGER("Dreieck 1 Berührt");
31 if (value > minValue) {
32 value = std::max(value - static_cast<int>(steps), minValue);
33 if (activateAnimation) timerTriang2 = millis();
34 }
35 }
36
37 if (input.touchX >= rect2_x1 && input.touchX <= rect2_x2 && input.touchY >= rect2_y1 && input.touchY <= rect2_y2) {
38 LOGGER("Dreieck 2 Berührt");
39 if (value < maxValue) {
40 value = std::min(value + static_cast<int>(steps), maxValue);
41 if (activateAnimation) timerTriang1 = millis();
42 }
43 }
44
45 if (value != tmp) {
46 callback(value);
47 if (externalValue) externalValue->setValue(value);
48 draw();
49 }
50 }
Diese Funktion testet, ob die beiden Eingabetasten berührt werden bzw. welche von ihnen berührt wird.
uint16_t rect1_x1, rect1_y1, rect1_x2, rect1_y2;
uint16_t rect2_x1, rect2_y1, rect2_x2, rect2_y2;
auto tmp = value;
Zuerst werden einige Variablen deklariert, um die Positionen der beiden Eingabetasten zu speichern. Dabei sind rect1_x1, rect1_y1, rect1_x2 und rect1_y2 die Koordinaten
für die erste Eingabetaste und rect2_x1, rect2_y1, rect2_x2 und rect2_y2 für die zweite Eingabetaste. Um die Positionen zu bestimmen, benötigt man nur zwei Koordinaten,
da jede Koordinate einen der beiden äußersten Ränder einer Eingabetaste repräsentiert. Wichtig ist nur, dass eventuelle Berührungen innerhalb dieser beiden Ränder stattfinden.
In tmp wird der aktuelle Wert von value zwischengespeichert.
if (isVertical) {
rect1_x1 = posX + padding;
rect1_y1 = posY + sizeY - triangleHight - triangleDistance;
rect1_x2 = posX + sizeX - padding;
rect1_y2 = posY + sizeY - padding;
rect2_x1 = rect1_x1;
rect2_y1 = posY + padding;
rect2_x2 = rect1_x2;
rect2_y2 = posY + triangleHight + triangleDistance;
Sollte isVertical true sein, ist das Number_Counter Objekt vertikal ausgerichtet und
dementsprechend werden die oberste und untersten Ränder der Eingabetasten bestimmt.
} else {
rect1_x1 = posX + padding;
rect1_y1 = posY + padding;
rect1_x2 = posX + triangleHight + triangleDistance;
rect1_y2 = posY + sizeY - padding;
rect2_x1 = posX + sizeX - triangleHight - triangleDistance;
rect2_y1 = rect1_y1;
rect2_x2 = posX + sizeX - triangleDistance;
rect2_y2 = rect1_y2;
}
Andernfalls ist das Number_Counter Objekt horizontal ausgerichtet und dementsprechend werden die linkesten und rechtesten Ränder der Eingabetasten bestimmt.
if (input.touchX >= rect1_x1 && input.touchX <= rect1_x2 && input.touchY >= rect1_y1 && input.touchY <= rect1_y2) {
LOGGER("Dreieck 1 Berührt");
if (value > minValue) {
value = std::max(value - static_cast<int>(steps), minValue);
if (activateAnimation) timerTriang2 = millis();
}
}
Nun wird geprüft, ob der Touchinput (input.touchX input.touchY) innerhalb des ersten Eingabefelds liegt.
Wenn ja, wird die erste Eingabetaste berührt. Die erste Eingabetaste soll bei Berührung den value um eins verringern, deswegen wird geprüft,
ob value nicht schon den minimalsten Wert (minValue) angenommen hat.
Wenn also value > minValue gilt, wird value um steps verringert (außer dieser Wert wäre geringer als
minValue, dann würde value stattdessen den Wert von minValue annehmen).
Sollte activateAnimation gelten, so wird im timerTriang1 die Millisekunden seit Start
(siehe millis() ) speichert.
if (input.touchX >= rect2_x1 && input.touchX <= rect2_x2 && input.touchY >= rect2_y1 && input.touchY <= rect2_y2) {
LOGGER("Dreieck 2 Berührt");
if (value < maxValue) {
value = std::min(value + static_cast<int>(steps), maxValue);
if (activateAnimation) timerTriang1 = millis();
}
}
Für die zweite Eingabetaste funktioniert der Ablauf ähnlich wie bei der ersten Eingabetaste, nur sind hier sämtliche Verwendungen von min und max getauscht.
if (value != tmp) {
callback(value);
if (externalValue) externalValue->setValue(value);
draw();
}
Sollte sich value im Laufe der Funktion verändert haben, so wird die Callbackfunktion aufgerufen. Sollte es einen externalValue geben, so ersetzt dieser den Wert von value mit setvalue. Anschließend wird das veränderte Objekt gezeichnet.
inline bool checkSize(uint16_t sizeX, uint16_t sizeY, uint8_t rotation) override¶
1 inline bool checkSize(uint16_t sizeX, uint16_t sizeY, uint8_t rotation) override {
2 double x = 1.2;
3 if (showNumber) x = 1.4;
4
5 if (sizeY >= x * sizeX) {
6 isVertical = true;
7 // triangleHight = min((uint16_t)(sizeY/2 - (showNumber*12) - borderDistance), sizeX);
8 triangleHight = (sizeY-triangleDistance)/2 - padding - borderStrength;
9 if (showNumber) triangleHight -= fontSize*10;
10
11 item1->setResolution(sizeX - 2*padding - 2*borderStrength, triangleHight);
12 item2->setResolution(sizeX - 2*padding - 2*borderStrength, triangleHight);
13
14 LOGGER("Slider ist Vertikal")
15 return true;
16 }
17
18 if (sizeX >= x * sizeY) {
19 LOGGER("Slider ist Horrizontal")
20 // triangleHight = min((uint16_t)(sizeX/2- (showNumber*12) - borderDistance), sizeY);
21 triangleHight = (sizeX-triangleDistance)/2 - padding - borderStrength;
22 if (showNumber) triangleHight -= fontSize*10;
23
24 item1->setResolution(triangleHight, sizeY - 2*padding - 2*borderStrength);
25 item2->setResolution(triangleHight, sizeY - 2*padding - 2*borderStrength);
26
27 return true;
28 }
29
30 LOGGER_PATTERN("ERROR: Slider ist nicht Lang genug mit (_, _)", sizeX, sizeY)
31 return false;
32 }
Diese Funktion testet, ob die Höhe (sizeX) und Breite (sizeY) eines Objektes in einem bestimmten Größenverhältnis zueinander stehen, um das Objekt zeichnen zu können.
double x = 1.2;
if (showNumber) x = 1.4;
if (sizeY >= x * sizeX) {
isVertical = true;
Wenn ein Objekt mindestens 1.2 mal so hoch ist, wie es breit ist, dann ist das Objekt vertikal und zeichenbar. Sollte showNumber gelten, so muss ein Objekt 1.4 mal so hoch sein, wie es breit ist.
triangleHight = (sizeY-triangleDistance)/2 - padding - borderStrength;
if (showNumber) triangleHight -= fontSize*10;
Anschließend wird triangleHeight berechnet. Sollte showNumber gelten, wird triangleHeight um das zehnfache von fontSize reduziert, um Platz für das Anzeigen von value zu schaffen.
item1->setResolution(sizeX - 2*padding - 2*borderStrength, triangleHight);
item2->setResolution(sizeX - 2*padding - 2*borderStrength, triangleHight);
Danach wird mit setResolution die Auflösung von item1 und item2 bestimmt, welche optimal an das Objekt angepasst ist.
if (sizeX >= x * sizeY) {
Wenn ein Objekt mindestens 1.2 mal so breit ist, wie es hoch ist, dann ist das Objekt horizontal und zeichenbar. Sollte showNumber gelten, so muss ein Objekt 1.4 mal so breit sein, wie es hoch ist. Der restliche Ablauf ähnelt dem aus den vorherigen Absätzen stark, weswegen dieser nicht näher erläutert wird.
return false;
Sollte keine der beiden Bedingungen erfüllt sein, ist das Objekt nicht zeichenbar, da das Seitenverhältnis nicht stimmt.
inline void drawItems()¶
1 inline void drawItems() {
2
3 uint16_t x1, y1, x2, y2;
4 if (isVertical) {
5 x1 = posX + 0.5*sizeX;
6 y1 = posY + triangleDistance + item1->getHeight()/2;
7
8 x2 = posX + 0.5*sizeX;
9 y2 = posY + sizeY - triangleDistance - item2->getHeight()/2 ;
10 } else {
11 x1 = posX + triangleDistance + item1->getWith()/2;
12 y1 = posY + 0.5*sizeY;
13 x2 = posX + sizeX - triangleDistance - item2->getWith()/2;
14 y2 = posY + 0.5*sizeY;
15 }
16
17 LOGGER_PATTERN("Zeichne Slider _ mit item1: (_/_) in der größe _,_ und item2: (_/_) in der größe _,_", isVertical? "vertikal":"Horrizontal", x1, y1, item1->getWith(), item1->getHeight(), x2, y2, item2->getWith(), item2->getHeight())
18
19 if ((isVertical && timerTriang1 == 0) || (!isVertical && timerTriang2 == 0)) item1->drawOn(x1, y1, display);
20 else item1->drawOff(x1, y1, display);
21
22 if ((isVertical && timerTriang2 == 0) || (!isVertical && timerTriang1 == 0)) item2->drawOn(x2, y2, display);
23 else item2->drawOff(x2, y2, display);
24 }
Diese Funktion wird verwendet, um item1 und item2 zu zeichnen.
uint16_t x1, y1, x2, y2;
x1 und y1 stehen für die X- bzw Y-Koordinate, bei denen item1 und x2 und y2 für die X- bzw Y-Koordinate,
bei denen item2 gezeichnet werden soll.
if (isVertical) {
x1 = posX + 0.5*sizeX;
y1 = posY + triangleDistance + item1->getHeight()/2;
x2 = posX + 0.5*sizeX;
y2 = posY + sizeY - triangleDistance - item2->getHeight()/2 ;
} else {
x1 = posX + triangleDistance + item1->getWith()/2;
y1 = posY + 0.5*sizeY;
x2 = posX + sizeX - triangleDistance - item2->getWith()/2;
y2 = posY + 0.5*sizeY;
}
Je nachdem, ob das Objekt vertikal oder horizontal ausgerichtet ist (isVertical), werden die Positionen unterschiedlich berechnet.
if ((isVertical && timerTriang1 == 0) || (!isVertical && timerTriang2 == 0)) item1->drawOn(x1, y1, display);
else item1->drawOff(x1, y1, display);
Wenn diese Bedingungen gelten, wird für item1 die drawOn Funktion aufgerufen, da das Item auf der inkrementierenden Seite dargestellt werden soll,
andernfalls wird es auf der dekrementierenden Seite gezeichnet.
if ((isVertical && timerTriang2 == 0) || (!isVertical && timerTriang1 == 0)) item2->drawOn(x2, y2, display);
else item2->drawOff(x2, y2, display);
Wenn diese Bedingungen gelten, wird für item2 die drawOn Funktion aufgerufen, da das Item auf der inkrementierenden Seite dargestellt werden soll,
andernfalls wird es auf der dekrementierenden Seite gezeichnet.
inline void loop (Inputs& input) override¶
1 inline void loop (Inputs& input) override {
2 NumberInput::loop(input);
3 // LOGGER_PATTERN("LOOP - _ < _", timerTriang2, millis() + time)
4
5 if (!activateAnimation) return;
6
7 if (timerTriang1 != 0 && millis() - timerTriang1 >= time) {
8 LOGGER("Setzte Dreieck 1 zurück")
9 timerTriang1 = 0;
10 drawItems();
11 }
12
13 if (timerTriang2 != 0 && millis() - timerTriang2 >= time) {
14 LOGGER("Setzte Dreieck 2 zurück")
15 timerTriang2 = 0;
16 drawItems();
17 }
18 }
Diese Funktion dient dazu, den Zustand des aktuellen Objektes zu aktualisieren.
NumberInput::loop(input);
Zuerst wird loop Funktion mit input aufgerufen, um diese auszuführen.
if (!activateAnimation) return;
Sollte activateAnimation false sein, wird die Funktion abgebrochen, weil keine Änderungen vorgenommen werden müssen.
if (timerTriang1 != 0 && millis() - timerTriang1 >= time) {
LOGGER("Setzte Dreieck 1 zurück")
timerTriang1 = 0;
drawItems();
}
Hier wird timerTriang1 zurückgesetzt und die drawItems Funktion aufgerufen, wenn ein Timer gestartet wurde und seit Timerstart mehr Millisekunden abgalaufen sind, als in time angegeben. Die Animation ist abgelaufen
if (timerTriang2 != 0 && millis() - timerTriang2 >= time) {
LOGGER("Setzte Dreieck 2 zurück")
timerTriang2 = 0;
drawItems();
}
Hier wird timerTriang2 zurückgesetzt und die drawItems Funktion aufgerufen, wenn ein Timer gestartet wurde und seit Timerstart mehr Millisekunden abgalaufen sind, als in time angegeben. Die Animation ist abgelaufen.
Variablen und Konstanten Beschreibung¶
const Color color¶
Gibt die Farbe eines Items an.
Item* item1¶
Eines von zwei möglichen Items, die auf Objekten der number_counter Klasse angezeigt werden können.
Dieses Item ist die grafische Darstellung einer Eingabetaste, die beim Betätigen value reduzieren soll.
Item* item2¶
Eines von zwei möglichen Items, die auf Objekten der number_counter Klasse angezeigt werden können.
Dieses Item ist die grafische Darstellung einer Eingabetaste, die beim Betätigen value erhöhen soll.
const bool showNumber = false¶
Gibt an, ob die eingestellte Zahl die (siehe value)
auf dem Bildschirm angezeigt werden (showNumber = true) oder versteckt bleiben soll (showNumber = false)
const uint16_t padding = 5¶
Gibt den Abstand zur Umrandung an.
const uint16_t borderStrength = 4¶
Gibt die Breite der Umrandung an.
const uint16_t borderRadius = 6¶
Gibt den Radius der Umrandung (bzw. den Krümmungsgrad) an.
const uint16_t triangleDistance = 1.5*padding + borderStrength¶
Gibt an, wie weit die beiden Eingabetasten (mit denen die Werte des Sliders verändert werden können) auseinander sein sollen.
const uint8_t fontSize = 3¶
Gibt die Schriftgröße an.
uint16_t triangleHeight¶
Gibt an, wie hoch die beiden Eingabetasten (mit denen die Werte des Sliders verändert werden können) sind.
bool isVertical = false¶
Gibt an, ob das Objekt horizontal oder vertikal ist.
const uint16_t time = 200¶
Gibt die Animationsdauer an.
bool activateAnimation = true¶
Gibt an, ob gerade eine Animation ausgeführt wird.
unsigned long timerTriang1 = 0¶
Speichert einen Timer für item1.
unsigned long timerTriang2 = 0¶
Speichert einen Timer für item2.