GridScreen

Beschreibung

GridScreens sind Screens, bei denen Elemente in einem Raster mit Zeilen und Spalten eingeordnet werden können. Die Anzahl an Zeilen und Spalten kann angepasst werden.

Funktionen

GridScreen (const uint8_t col, const uint8_t row, const Color& background)

Destructor ()

bool

add (Element* element, const uint16_t posX, const uint16_t posY, const uint16_t sizeX, const uint16_t sizeY)

bool

add (const AddElement& element)

GridScreen&

operator <<(const AddElement& element)

void

loop (Inputs& input)

void

draw () override

void

setResolution (int16_t height, int16_t width)

uint8_t

getRow () const

uint8_t

getCol () const

AddElement

Variablen und Konstanten

const Color

color_background

uint8_t

row

uint8_t

col

std::vector<std::unique_ptr<Element>>

elements

std::vector<uint8_t>

matrix

Macros

CREATE_GRID_SCREEN (TML, screenID, col, row, color, …)

CREATE_GRID_SCREEN_WITH_SIDEBAR (TML, screenID, sitebarID, col, row, color, …)

CREATE_GRID_SCREEN_SIDEBAR (TML, sitebarID, size, side, col, row, color, …)

Funktionen Beschreibung

GridScreen(const uint8_t col, const uint8_t row, const Color& background)

1GridScreen::GridScreen(const uint8_t col, const uint8_t row, const Color& background):
2    color_background(Color(background)),
3    row(row),
4    col(col),
5    elements(),
6    matrix(row*col, UINT8_MAX)
7{
8    //elements.reserve(row*col);
9}

Der Konstruktor wird bei der Erzeugung eines neuen Objekts der GridScreen Klasse aufgerufen. Als Parameter nimmt der Konstruktor die Anzahl an Spalten (col) und Zeilen (row), die der GridScreen haben soll, sowie die Hintergrundfarbe (background), die der GridScreen haben soll. In der Initialisierungsliste wird die Variable color_background nach dem Color Funktionsaufruf von background mit diesem initalisiert. „row“ und „col“ werden mit den korrespondierenden Parametern initialisiert. Außerdem erfolgt die Initialisierung von elements und von „matrix“. Bei letzterem erfolgt die Multiplikation der Spalten- und Reihenanzahl, um die Anzahl an Felder innerhalb des Grids zu speichern, sowie die Angabe von UINT8_MAX, welches später nützlich ist, um feststellen zu können, ob ein Feld bereits belegt ist.

virtual ~GridScreen()

bool add(Element* element, const uint16_t posX, const uint16_t posY, const uint16_t sizeX, const uint16_t sizeY)

 1bool GridScreen::add(Element* element, const uint16_t posX, const uint16_t posY, const uint16_t sizeX, const uint16_t sizeY) {
 2
 3    if (!display) {
 4        LOGGER_ERROR("first add this Screen to TML, befor you add an element!")
 5        return false;
 6    }
 7
 8    if (!element) {
 9        LOGGER_ERROR("element ist NULL")
10        return false;
11    }
12
13    if (col == 0 || row == 0) {
14        // Fehlerbehandlung für ungültige Werte von col und row
15        LOGGER_ERROR("col or wor are NULL")
16        return false;
17    }
18
19    if (posX + sizeX > col || posY + sizeY > row) {
20        LOGGER_ERROR("Element are outsite of the Screen")
21        return false;
22    }
23
24    // Check for overlap with other elements 
25    // LOGGER("Prüfe ob überlappunen mit anderen Elementen auftreten")
26    for (int x = posX; x < posX + sizeX; x++) {
27        for (int y = posY; y < posY + sizeY; y++) {
28            // If any of the cells are already occupied, we have an overlap
29            if (matrix[y * col + x] != UINT8_MAX) {
30                LOGGER_ERROR("Element overlapping an existing element")
31                return false;
32            }
33        }
34    }

Diese Funktion ist für das Hinzufügen von neuen Elementen auf dem Display zuständig. Als Parameter nimmt sie das hinzuzufügende Element (element), die X- und Y-Koordinaten ( posX und posY), bei denen das Element platziert werden soll, sowie die Größe in X- und Y-Richtung (sizeX und sizeY) bzw. die Höhe und Breite des Elements. Dabei beziehen sich die Angaben zu X- und Y-Koordinaten nicht etwa auf z.B. die Pixel des physischen Displays, sondern auf die Felder auf dem GridScreen. So hätte bei einem GridScreen mit 3x3 Feldern das linke obere Feld die X-Koordinate 0 und Y-Koordinate 0, das mittlere obere Feld die X-Koordinate 1 und die Y-Koordinate 0 usw.

    if (!display) {
        LOGGER_ERROR("first add this Screen to TML, befor you add an element!")
        return false;
    }

Sollte grid ein nullptr sein, wird false zurückgegeben, da der aktuellen Screen noch keinem TouchMenuLib Objekt zugewiesen wurde und kein Display zurückgekommen ist.

    if (!element) {
        LOGGER_ERROR("element ist NULL")
        return false;
    }

Sollte der übergebene übergebene Zeiger auf kein Element zeigen, wird false zurückgegeben, da kein Element auf das Display hinzugefügt werden kann.

    if (col == 0 || row == 0) {
        // Fehlerbehandlung für ungültige Werte von col und row
        LOGGER_ERROR("col or wor are NULL")
        return false;
    }

Sollte col == 0 oder row == 0 gelten, wird ebenfalls false zurückgegeben, da es keinen Platz gibt, auf dem ein Element hinzugefügt werden kann, da Elemente mindestens eine Zelle groß sein müssen.

    if (posX + sizeX > col || posY + sizeY > row) {
        LOGGER_ERROR("Element are outsite of the Screen")
        return false;
    }

Ist posX + sizeX > col oder posY + sizeY > row, befindet sich das Element außerhalb des gültigen Rasters, weswegen false zurückgegeben wird.

    for (int x = posX; x < posX + sizeX; x++) {
        for (int y = posY; y < posY + sizeY; y++) {
            // If any of the cells are already occupied, we have an overlap
            if (matrix[y * col + x] != UINT8_MAX) {
                LOGGER_ERROR("Element overlapping an existing element")
                return false;
            }
        }
    }

Dieser Codeblock wird genutzt, um zu testen, ob in den Feldern, in denen man ein Element hinzufügen möchte, bereits ein Element vorhanden ist. Also ob mehrere Elemente überlappen würden. Da ein Element mehr als ein Feld belegen kann (indem sizeX oder sizeY > 1 sind), sind hierfür zwei for-Schleifen notwendig. Die äußere Schleife läuft von dem Wert der X-Koordinate, bei der das Element beginnt (posX) bis zum Wert der X-Koordinate, bei der das Element endet (posX + sizeX). Die innere Schleife läuft von dem Wert der Y-Koordinate, bei der das Element beginnt (posY) bis zum Wert der Y-Koordinate, bei der das Element endet (posY + sizeY). Es wird getestet, ob in der „matrix“ an der entsprechenden Stelle bereits ein Wert gespeichert ist. Ist dies der Fall, wird false zurückgegeben.

    const uint16_t columnSpacing = width / col;
    const uint16_t rowSpacing = height / row;

    // Calculate the size and position of the element
    uint16_t x = columnSpacing * posX + offsetX;
    uint16_t y = rowSpacing * posY + offsetY;
    uint16_t w = columnSpacing * sizeX;
    uint16_t h = rowSpacing * sizeY;
Hier werden einige Variablen definiert, die den darauffolgenden Code leserlicher machen. Zuerst werden die Maße des GridScreens berechnet:

columnSpacing gibt an, wie breit jedes Feld des Rasters ist. Dafür wird die Breite des Screens (width) durch die Anzahl der Spalten dividiert.
rowSpacing gibt an, wie hoch jedes Feld des Rasters ist. Dafür wird die Höhe des Screens (height) durch die Anzahl der Zeilen dividiert.
x gibt die tatsächliche X-Koordinate auf dem (Screen) an.
y gibt die tatsächliche Y-Koordinate auf dem (Screen) an.
w gibt die tatsächliche Breite des Elements auf dem (Screen) an.
h gibt die tatsächliche Höhe des Elements auf dem (Screen) an.
    if (!element->setSize(w, h, display->getRotation())) {
        LOGGER_ERROR("Element ist mit der aktuellen Größe icht kompatibel")
        delete element;
        element = new Textbox_Blank("symb:Crosslines", COLOR_RED);
        element->setSize(w, h, display->getRotation());
    }

Die setSize Funktion des element wird aufgerufen. Dabei wird neben w und h auch das Ergebnis des Funktionsaufruf von „getRotation“ des Objektes „display“ übergeben. Sollte der Funktionsaufruf false zurückgeben, lässt sich das Element nicht dem Display hinzufügen. Weswegen element gelöscht wird und stattdessen eine Fehlermeldung in Form einer Textbox_blank ausgibt.

    element->setPosition(x, y);
    element->setDisplay(display);

Es folgt der Aufruf der „setPosition“ Funktion, um dem Element die zuvor bestimmten X- und Y-Koordinaten zuzuweisen, auf welchen es platziert werden soll, sowie der Funktionsaufruf von „setDisplay“, um das Display festzulegen, auf welchem das Element platziert werden soll.

    // save the element in elements and depending on the enlarge in matrix
    std::unique_ptr<Element> uptr (element);

Danach wird ein Smart Pointer in der Form eines unique_ptr's erstellt (uptr), der auf element zeigt. Dieser „besitzt“ und managt den element und entsorgt diesen, wenn er out of scope geht. Anschließend wird der unique_ptr ans Ende von „elements“ gepusht.

    for (int x = posX; x < posX+sizeX; x++) {
        for (int y = posY; y < posY+sizeY; y++) {
            // LOGGER(elements.size()-1)
            matrix[y*col + x] = elements.size() - 1;
        }
    }

Dieser Codeblock funktioniert ähnlich wie der von Zeile 22 bis 26. Nur wird hier an den betroffenen „matrix“ Positionen ein Wert gespeichert (die Größe von „elements“ -1), welcher kennzeichnet, dass das Feld vom element besetzt ist.

    element = nullptr;  // now uptr is owning the resource 
    return true;

Zum Schluss wird element nullptr zugewiesen, da uptr nun die Resource verwaltet. Die gesamte Funktion gibt nun true zurück, das Element wurde erfolgreich dem Display hinzugefügt.

bool add(const AddElement& element)

1bool GridScreen::add(const AddElement& element) {
2    return add (element.element, element.posX, element.posY, element.sizeX, element.sizeY);
3}

Diese Funktion nimmt als Parameter ein AddElement& element und ruft die add Funktion mit den entsprechenden Werten auf.

GridScreen& operator<<(const AddElement& element)

1GridScreen& GridScreen::operator<<(const AddElement& element) {
2    add (element.element, element.posX, element.posY, element.sizeX, element.sizeY);
3    return *this;
4}

Dieser Überladungsoperator nimmt als Parameter ein AddElement& element und ruft die add Funktion mit den entsprechenden Werten auf und gibt anschließend eine Referenz auf das aktuelle GridScreen Objekt zurück.

void loop(Inputs& input) override

 1void GridScreen::loop(Inputs& input) {
 2    if (input.updateTouchPoint && input.isTouched) {
 3
 4        const uint16_t x = input.touchX;
 5        const uint16_t y = input.touchY;
 6
 7        if (x < offsetX || y < offsetY || x > offsetX + width || y > offsetY + height) {
 8            // LOGGER_PATTERN("Touchpunkt (_/_) liegt außerhalb dieses Gridscreens", x, y)
 9            return;
10        }
11
12        const uint8_t grid_x = (x-offsetX) / (width / col);
13        const uint8_t grid_y = (y-offsetY) / (height / row);
14
15        if (grid_x >= col || grid_y >= row) {
16            // LOGGER_PATTERN("Feld (_/_) liegt außerhalb dieses Gridscreens", grid_x, grid_y)
17            return;
18        }
19        
20        // LOGGER("")
21        // LOGGER_PATTERN("Aktualisiere Element im Feld _/_ (Berührt bei [_,_] mit höhe _/_ und spalten: _/_)", grid_x, grid_y, (x-offsetX), (y-offsetY), width, height, col, row)
22
23        uint8_t e = matrix[col*grid_y + grid_x];
24        if (e != UINT8_MAX) elements[e]->setTouch(input);
25    }
26
27    for (const auto& element : elements) {
28        if (element) element->loop(input);
29    }
30}

Diese Funktion dient dazu, den Zustand des aktuellen Objektes zu aktualisieren, um auf Veränderungen und Benutzereingaben zu reagieren.

    if (input.updateTouchPoint && input.isTouched) {

        const uint16_t x = input.touchX;
        const uint16_t y = input.touchY;

Sollten input.updateTouchPoint und input.isTouched gelten (also sollte der Benutzer das Display an einer neuen Stelle berühren), werden in x und y die Koordinaten der Berührung gespeichert.

        if (x < offsetX || y < offsetY || x > offsetX + width || y > offsetY + height) {
            // LOGGER_PATTERN("Touchpunkt (_/_) liegt außerhalb dieses Gridscreens", x, y)
            return;
        }

In diesem Codeabschnitt wird geprüft, ob sich der Berührungspunkt außerhalb des aktuellen GridScreen befindet. Wenn ja, wird die Verarbeitung abgebrochen.

        const uint8_t grid_x = (x-offsetX) / (width / col);
        const uint8_t grid_y = (y-offsetY) / (height / row);

Anschließend werden mit grid_x und grid_y die Breite und Höhe eines Gitterfeldes bestimmt.

        if (grid_x >= col || grid_y >= row) {
            // LOGGER_PATTERN("Feld (_/_) liegt außerhalb dieses Gridscreens", grid_x, grid_y)
            return;
        }

Nun wird geprüft, ob das Gitterfeld außerhalb des GridScreens liegt. Wenn ja, wird die Verarbeitung abgebrochen.

        uint8_t e = matrix[col*grid_y + grid_x];
        if (e != UINT8_MAX) elements[e]->setTouch(input);
    }

Nach allen Tests, können wir nun sicher sein, dass der Berührungspunkt auch innerhalb eines Feldes des GridScreens liegt. Deshalb wird mit e das exakte Feld berechnet, in welchem der Touchinput geschieht. Wenn e != UINT_MAX gilt, wird auf elements[e] die entsprechende setTouched Funktion aufgerufen, um die Toucheingabe letztendlich zu verarbeiten.

    for (const auto& element : elements) {
        if (element) element->loop(input);
    }

Nach der Verarbeitung der Berührungspunkte läuft eine for Schleife über alle elements, um die jeweiligen loop Funktionen aufzurufen, um auch bei den einzelnen Elementen auf mögliche Änderungen zu reagieren.

void draw() override

 1void GridScreen::draw() {
 2    // LOGGER_PATTERN("Zeichne GridScreeen an (_, _) mit sizeX=_, sizeY_ in der Hindergrundfarbe _", offsetX, offsetY, width, height, color_background.toString())
 3    display->rect(offsetX, offsetY, width, height, 0, 0, Color(0,0,0), color_background);
 4
 5    #ifdef TML_DEBUG
 6
 7    // Abstand zwichen den Linien
 8    const uint16_t columnSpacing = width / (col);
 9    const uint16_t rowSpacing = height / (row);
10    
11    // Vertikale Linien zeichnen -> Spalten
12    for (int i = 1; i <= col; i++) {
13        int x = offsetX + i * columnSpacing;
14        display->line(x, offsetY, x, offsetY + height, !color_background);
15    }
16
17    // Horizontale Linien zeichnen -> Zeilen
18    for (int j = 1; j <= row; j++) {
19        int y = offsetY + j * rowSpacing;
20        display->line(offsetX, y, offsetX + width, y, !color_background);
21    }
22    #endif
23
24    for (const auto& element : elements) {
25        if (element) {
26            element->draw();
27        }
28    }
29}
Diese Funktion überschreibt die „draw“ Funktion der „Screen“ Klasse.

Zuerst wird die rect Funktion des „display“ Objektes aufgerufen. Dabei wird „background_color“ als Argument übergeben. Dadurch wird das Display in die Hintergrundfarbe gefärbt.
Anschließend werden zwei Variablen definiert, die den darauffolgenden Code leserlicher machen:

columnSpacing gibt an, wie breit jedes Feld des Rasters ist. Dafür wird die Breite des Screens (“width“) durch die Anzahl der Spalten dividiert.
rowSpacing gibt an, wie hoch jedes Feld des Rasters ist. Dafür wird die Höhe des Screens (“height“) durch die Anzahl der Zeilen dividiert.
    for (int i = 1; i <= col; i++) {
        int x = offsetX + i * columnSpacing;
        display->line(x, offsetY, x, offsetY + height, !color_background);
    }

In diesem Codeabschnitt werden die vertikalen Linien auf dem Display gezeichnet, sodass die gewünschte Anzahl an Spalten entstehen. Die for-Schleife läuft genau so häufig, wie unter „col“ angegeben. Bei jedem Durchlauf wird die Variable x erstellt, in der gespeichert wird, durch welche X-Koordinate die aktuelle Linie verlaufen soll. Die Linien sollen immer am Ende eines jeden Rasterfeldes gezogen werden. Mit dem „line“ Funktionsaufruf des „display“ wird diese Linie letztendlich gezeichnet. Dabei wird sie zwischen den Koordinaten (x;0) bis nach (x;height) gezeichnet (also von einen Bildschirmrand zum anderen). Dabei wird die Linie in die Komplementärfarbe des Hintergrundes (“color_background <gridvcolor_background>“) gefärbt, um eine gute Sichtbarkeit zu gewährleisten.

    for (int j = 1; j <= row; j++) {
        int y = offsetY + j * rowSpacing;
        display->line(offsetX, y, offsetX + width, y, !color_background);
    }
    #endif

In diesem Codeabschnitt werden die horizontalen Linien auf dem Display gezeichnet, sodass die gewünschte Anzahl an Zeilen entstehen. Die for-Schleife läuft genau so häufig, wie unter „row“ angegeben. Bei jedem Durchlauf wird die Variable y erstellt, in der gespeichert wird, durch welche Y-Koordinate die aktuelle Linie verlaufen soll. Die Linien sollen immer am unteren Ende eines jeden Rasterfeldes gezogen werden. Mit dem „line“ Funktionsaufruf des „display“ wird diese Linie letztendlich gezeichnet. Dabei wird sie zwischen den Koordinaten (0;y) bis nach (0;width) gezeichnet (also von einen Bildschirmrand zum anderen). Dabei wird die Linie in die Komplementärfarbe des Hintergrundes (“color_background <gridvcolor_background>“) gefärbt, um eine gute Sichtbarkeit zu gewährleisten.

    for (const auto& element : elements) {
        if (element) {
            element->draw();
        }
    }

Schlussendlich werden durch die foreach-Schleife sämtliche Elemente aus „elements“ durch den Funktionsaufruf von „draw“ auf das Grid gezeichnet.

void setResolution(int16_t height, int16_t width)

1uint8_t GridScreen::getRow() const { return row; }
2uint8_t GridScreen::getCol() const { return col; }

Die Funktion „setResolution“ in der „Screen“ Klasse wird aufgerufen, um die Auflösung des Displays festzulegen. Dabei werden die Höhe (h) und Breite (w) des Displays als Parameter übergeben.

uint8_t getRow() const

1    uint8_t getCol() const;

Gibt die Anzahl an Zeilen (“row“) des Displays zurück.

uint8_t getCol() const

1

Gibt die Anzahl an Spalten (col) des Displays zurück.

struct AddElement

 1struct AddElement {
 2    Element* element;
 3    const uint16_t posX;
 4    const uint16_t posY;
 5    const uint16_t sizeX;
 6    const uint16_t sizeY;
 7
 8     AddElement(Element* elem, uint16_t x, uint16_t y, uint16_t sizeX, uint16_t sizeY)
 9        : element(elem), posX(x), posY(y), sizeX(sizeX), sizeY(sizeY) {}
10};

Diese Struktur wird verwendet, um Informationen über ein Element und seiner Position und Größe innerhalb eines GridScreens zu speichern.

    Element* element;
    const uint16_t posX;
    const uint16_t posY;
    const uint16_t sizeX;
    const uint16_t sizeY;

Dabei ist element der Pointer auf das Element, welches dem GridScreen hinzugefügt werden soll, posX und posY die X- bzw. Y-Koordinate, bei welchen das Element platziert werden soll und sizeX und sizeY die Breite bzw. Höhe des Elements

     AddElement(Element* elem, uint16_t x, uint16_t y, uint16_t sizeX, uint16_t sizeY)
        : element(elem), posX(x), posY(y), sizeX(sizeX), sizeY(sizeY) {}

Mit dem Konstruktor werden die oben genannten Parameter initialisiert.

Variablen und Konstanten Beschreibung

const Color color_background

Speichert die Hintergrundfarbe eines GridScreens.

uint8_t row

Gibt an, wie viele Reihen ein GridScreen hat.

uint8_t col

Gibt an, wie viele Spalten ein GridScreen hat.

std::vector<std::unique_ptr<Element>> elements

Speichert alle „Elemente“, die sich auf einem GridScreen befinden.

std::vector<uint8_t> matrix

Speichert, aus wie vielen Kästchen/ Feldern ein GridScreen zusammengesetzt ist. Beim Konstruktoraufruf werden alle Felder auf UINT8_MAX gesetzt (als Platzhalter, um nicht benutzte Felder leichter identifizieren zu können).

Macro Beschreibung

CREATE_GRID_SCREEN(TML, screenID, col, row, color, …)

 1#define CREATE_GRID_SCREEN(TML, screenID, col, row, color, ...) \
 2    /* Check parameters for correctness */ \
 3    static_assert(std::is_integral<decltype(screenID)>::value && screenID >= 0 && screenID <= UINT8_MAX, "screenID must be of type uint8_t"); \
 4    static_assert(std::is_integral<decltype(col)>::value && col >= 0 && col <= UINT8_MAX, "col must be of type uint8_t"); \
 5    static_assert(std::is_integral<decltype(row)>::value && row >= 0 && row <= UINT8_MAX, "row must be of type uint8_t"); \
 6    static_assert(std::is_same<decltype(color), Color>::value, "color must be of type Color"); \
 7    static_assert(std::is_same<decltype(TML), TouchMenuLib>::value, "TML must be of type TouchMenuLib"); \
 8    /* TODO: überprüfen, ob screenID bereits genutzt wurde*/ \
 9    \
10    GridScreen* screen##screenID = new GridScreen(col, row, color); \
11    TML.add(screenID, screen##screenID); \
12    std::vector<AddElement> elements##screenID {__VA_ARGS__}; \
13    for (const auto& element : elements##screenID) { \
14        screen##screenID->add(element); \
15    }

Dieses Macro vereinfacht die Erstellung eines Objektes der GridScreen Klasse, indem ein Raster erstellt wird. Dabei ist TML eine Instanz der TouchMenuLib Klasse, screenID eine einzigarte UINT8_MAX Zahl, die zur Identifizierung des Screens genutzt wird, col die Anzahl der Spalten, die das Gitter haben soll, row die Anzahl der Zeilen, die das Gitter haben soll und color die Hintergrundfarbe.

CREATE_GRID_SCREEN_WITH_SITEBAR(TML, screenID, sitebarID, col, row, color, …)

 1#define CREATE_GRID_SCREEN_WITH_SITEBAR(TML, screenID, sitebarID, col, row, color, ...) \
 2    /* Check parameters for correctness */ \
 3    static_assert(std::is_integral<decltype(screenID)>::value && screenID >= 0 && screenID <= UINT8_MAX, "screenID must be of type uint8_t"); \
 4    static_assert(std::is_integral<decltype(sitebarID)>::value && sitebarID >= 0 && sitebarID <= UINT8_MAX, "sitebarID must be of type uint8_t"); \
 5    static_assert(std::is_integral<decltype(col)>::value && col >= 0 && col <= UINT8_MAX, "col must be of type uint8_t"); \
 6    static_assert(std::is_integral<decltype(row)>::value && row >= 0 && row <= UINT8_MAX, "row must be of type uint8_t"); \
 7    static_assert(std::is_same<decltype(color), Color>::value, "color must be of type Color"); \
 8    static_assert(std::is_same<decltype(TML), TouchMenuLib>::value, "TML must be of type TouchMenuLib"); \
 9    /* TODO: überprüfen, ob screenID bereits genutzt wurde*/ \
10    \
11    GridScreen* screen##screenID = new GridScreen(col, row, color); \
12    TML.add(screenID, screen##screenID, sitebarID); \
13    std::vector<AddElement> elements##screenID {__VA_ARGS__}; \
14    for (const auto& element : elements##screenID) { \
15        screen##screenID->add(element); \
16    }

Dieses Macro vereinfacht die Erstellung eines Objektes der GridScreen Klasse, indem ein Raster mit einer Sidebar am Bildschirmrand erstellt wird. Dabei ist TML eine Instanz der TouchMenuLib Klasse, screenID eine einzigarte UINT8_MAX Zahl, die zur Identifizierung des Screens genutzt wird, col die Anzahl der Spalten, die das Gitter haben soll, row die Anzahl der Zeilen, die das Gitter haben soll und color die Hintergrundfarbe.

CREATE_GRID_SCREEN_SITEBAR(TML, sitebarID, size, side, col, row, color, …)

 1#define CREATE_GRID_SCREEN_SITEBAR(TML, sitebarID, size, side, col, row, color, ...) \
 2    /* Check parameters for correctness */ \
 3    static_assert(std::is_integral<decltype(sitebarID)>::value && sitebarID >= 0 && sitebarID <= UINT8_MAX, "sitebarID must be of type uint8_t"); \
 4    static_assert(std::is_integral<decltype(col)>::value && col >= 0 && col <= UINT8_MAX, "col must be of type uint8_t"); \
 5    static_assert(std::is_integral<decltype(row)>::value && row >= 0 && row <= UINT8_MAX, "row must be of type uint8_t"); \
 6    static_assert(std::is_integral<decltype(side)>::value && side >= 0 && side <= UINT8_MAX, "side must be of type uint8_t"); \
 7    static_assert(std::is_integral<decltype(size)>::value && size >= 0 && size <= UINT16_MAX, "size must be of type uint16_t"); \
 8    static_assert(std::is_same<decltype(color), Color>::value, "color must be of type Color"); \
 9    static_assert(std::is_same<decltype(TML), TouchMenuLib>::value, "TML must be of type TouchMenuLib"); \
10    /* TODO: überprüfen, ob screenID bereits genutzt wurde*/ \
11    \
12    GridScreen* sitebar##sitebarID = new GridScreen(col, row, color); \
13    TML.addSitebar(sitebarID, sitebar##sitebarID, size, side); \
14    std::vector<AddElement> sitebar_elements##sitebarID {__VA_ARGS__}; \
15    for (const auto& element : sitebar_elements##sitebarID) { \
16        sitebar##sitebarID->add(element); \
17    }

Dieses Macro vereinfacht die Erstellung eines Objektes der GridScreen Klasse, indem eine Sidebar erstellt wird Dabei ist TML eine Instanz der TouchMenuLib Klasse, screenID eine einzigarte UINT8_MAX Zahl, die zur Identifizierung des Screens genutzt wird, col die Anzahl der Spalten, die das Gitter haben soll, row die Anzahl der Zeilen, die das Gitter haben soll und color die Hintergrundfarbe.