TFT Input Placeholder

Hallo, ich würde gerne auf meinen TFT Display ein input feld mit placeholder anzeigen, so in der art:

Unbenannt-1

für das TFT Display verwende ich folgende library: Adafruit_ST7796S_kbv
mit einem LCD Display lässt sich diese funktion druch

    lcd.cursor();
    lcd.blink();

abbrufen. gibt es eine einfache möglichkeit so etwas mit einem TFT Display umzusetzen?
Ich bin noch blutiger anfänger :grimacing:

Wie wollen Sie die Uhrzeit denn eingeben?

Vielleicht hilft dieser Code:

#include <LiquidCrystal.h>

// Initialize the library with the numbers of the interface pins
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  // Set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
}

void loop() {
  // Clear the display
  lcd.clear();

  // Set the cursor to the first column of the first row
  lcd.setCursor(0, 0);

  // Print a message to the LCD
  lcd.print("Enter your time");

  // Set the cursor to the first column of the second row
  lcd.setCursor(0, 1);

  // Turn on the cursor and make it blink
  lcd.cursor();
  lcd.blink();

  // Wait for user input (you can use any method you prefer)
  // ...
}

Ich denke aber, dass du für lcd.xxx die Bibliothek LiquidCrystal brauchst.

Mit der lib nicht.
Die hat dafür keine Funktion.
Du kannst höchstens sowas selbst programmieren - dann scheiterts aber an Deiner Aufgabenstellung.

Moin @s0n11c3,

es hängt davon ab, was Du unter einer "einfachen Lösung" verstehst :wink:

Wie @my_xy_projekt schon schrieb, so simpel wie bei einem LCD ist es deshalb nicht, weil

  • ein LCD zeichenbasierend und
  • ein TFT-Display pixelbasierend ist.

Mit anderen Worten: Beim LCD werden Zeichen i.d.R. in einem festen Raster und mit vorgegebenen Zeichengrößen dargestellt, auf dem TFT können Zeichen an (weitgehend) beliebiger Stelle mit unterschiedlicher Zeichengröße dargestellt werden.

Andererseits ist so ein Blink-Cursor auch keine große Tat ... Wenn man einmal heraustüftelt, wo bei welcher Zeile die Unterkante der Zeichen ist und wie breit das einzelne Zeichen ist, kann man das mit überschaubarem Aufwand programmieren.

ec2021

P.S.: Gesagt, getan ... Hier ein Beispiel(!), wie so etwas aussehen kann:

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#define TFT_DC 9
#define TFT_CS 10
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

#define LEFT 26
#define TOP  120
#define ZEICHENBREITE 18

struct CursorType {
    int x;
    int y;
    int length;
    int width;
    int Position = 0;
    uint16_t color; 
    void Draw();
    void Delete();
};

/*
void CursorType::Draw(){
    tft.drawLine(CursorType::x+Position,
                 CursorType::y,
                 CursorType::x+Position+CursorType::length,
                 CursorType::y,
                 CursorType::color);

}
*/

void CursorType::Draw(){
    tft.fillRect(CursorType::x+Position,
                 CursorType::y,
                 CursorType::length,
                 CursorType::width,
                 CursorType::color);

}


void CursorType::Delete(){
   uint16_t oldCol = CursorType::color;
   CursorType::color = ILI9341_BLACK;
   CursorType::Draw();
   CursorType::color = oldCol;
}  



CursorType myCursor;


void Cursor(int Stelle){
  static unsigned long lastChange = 0;
  static boolean Paint = true;
  static int lastStelle = 0;
  if (lastStelle != Stelle) {
      myCursor.Position = lastStelle*ZEICHENBREITE;
      myCursor.Delete();
      lastChange = 0;
      lastStelle = Stelle;
      Paint = true;
  }
  if (millis()-lastChange > 300){
    myCursor.Position = lastStelle*ZEICHENBREITE;
    lastChange = millis();
    if (Paint) {
       myCursor.Draw();
    } else {
       myCursor.Delete();
    }
    Paint = !Paint;
  }
 }



void setup() {
  tft.begin();
  tft.setCursor(LEFT, TOP);
  tft.setTextColor(ILI9341_RED);
  tft.setTextSize(3);
  
  myCursor.x = LEFT;
  myCursor.y = TOP+30;
  myCursor.length = 14;
  myCursor.width = 3;
  myCursor.color = ILI9341_RED;

  tft.println("00:00");

}

int Stelle = 0;
unsigned long lastChange = 0;

void loop() {
   Cursor(Stelle);
   if (millis()-lastChange > 3000){
     lastChange = millis();
     Stelle++;
     if (Stelle == 2) Stelle++;  // Hier ist der Doppelpunkt 
     if (Stelle > 4) Stelle = 0;
   }
   
 }

Und hier zum Anschauen in der Simulation:

https://wokwi.com/projects/360465437090001921

Die ausgeklammerte Alternative "CursorType::Draw()" benutzt tft.drawLine() und zeichnet eine einfache Linie, die verwendete Version benutzt tft.fillRect() und zeichnet ein gefülltes Rechteck als Cursor.

Das ganze ist keine Library sondern nur ein Beispiel für eine Umsetzung!

Den ganzen Blinkkram (void Cursor()) könnte man noch als Funktion in die Struktur integrieren, dann liessen sich mehrere Cursor parallel anzeigen, man könnte die Intialisierung verbessern und usw. ... und natürlich eine eigene Klasse daraus erstellen ...

meinst nicht, das Du es jetzt übertreibst? :rofl:

Ich überleg grad ob bei Oli Kraus da evtl. schon was fertiges bei ist.

Nö ... dauert ja nicht lange, übt und macht Spaß ...

Schöne Grüße!
ec2021

Oli hat das nicht (in keiner hab was gefunden), TFT_eSPi und LCDWIKI auch nicht, wahrscheinlich wegen manchen Zeichen was nach unten ragen.

So, hier noch eine kleine "Verbesserung"; das ganze funktioniert jetzt auch bei Änderungen der textSize:

/*
  Beispiel für 
  https://forum.arduino.cc/t/tft-input-placeholder/1107597

  2023-03-28
  ec2021

*/

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#define TFT_DC 9
#define TFT_CS 10
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

#define LEFT 50
#define TOP  80
#define ZEICHENBREITE 24

struct CursorType {
    int x;
    int y;
    int textSize = 1;
    int width;
    int Position = 0;
    uint16_t color; 
    void Draw();
    void Delete();
};


void CursorType::Draw(){
    tft.fillRect(CursorType::x+CursorType::Position*(CursorType::textSize)*6,
                 CursorType::y+(CursorType::textSize +1)*6,
                 CursorType::textSize * 6,
                 CursorType::width,
                 CursorType::color);

}


void CursorType::Delete(){
   uint16_t oldCol = CursorType::color;
   CursorType::color = ILI9341_BLACK;
   CursorType::Draw();
   CursorType::color = oldCol;
}  



CursorType myCursor;


void Cursor(int Stelle){
  static unsigned long lastChange = 0;
  static boolean Paint = true;
  static int lastStelle = 0;
  if (lastStelle != Stelle) {
      myCursor.Position = lastStelle;
      myCursor.Delete();
      lastChange = 0;
      lastStelle = Stelle;
      Paint = true;
  }
  if (millis()-lastChange > 300){
    myCursor.Position = lastStelle;
    lastChange = millis();
    if (Paint) {
       myCursor.Draw();
    } else {
       myCursor.Delete();
    }
    Paint = !Paint;
  }
 }


// --------------------------------------
//     Hier die textSize anpassen ...
int textSize = 6;
// --------------------------------------

void setup() {
  tft.begin();
  tft.setCursor(LEFT, TOP);
  tft.setTextColor(ILI9341_RED);
  tft.setTextSize(textSize);   
                         // 4 -> y = TOP +30    ZEICHENBREITE = 24
                         // 3 -> y = TOP +24    ZEICHENBREITE = 18
                         // 2 -> y = TOP +18    ZEICHENBREITE = 12
                         // 1 -> y = TOP +12    ZEICHENBREITE = 6
  
  myCursor.x = LEFT;
  myCursor.y = TOP;
  myCursor.textSize = textSize;
  myCursor.width = 3;
  myCursor.color = ILI9341_RED;

  tft.println("00:00");

}

int Stelle = 0;
unsigned long lastChange = 0;

void loop() {
   Cursor(Stelle);
   if (millis()-lastChange > 3000){
     lastChange = millis();
     Stelle++;
     if (Stelle == 2) Stelle++;  // Hier ist der Doppelpunkt 
     if (Stelle > 4) Stelle = 0;
   }
   
 }

Siehe https://wokwi.com/projects/360474038082242561

Damit Schluss für heute :wink:

ec2021

Es gibt im Code diese Stelle zum Anpassen der Zeichengröße (1 ... 6):

// --------------------------------------
//     Hier die textSize anpassen ...
int textSize = 6;
// --------------------------------------

Da würde ich eher als Verbesserung vorschlagen, wie man sicherstellen kann, dass auf dem Display nur 1 Cursor zu sehen ist
:wink:

Ist auch nur für Mulitasking-User gedacht ... :wink:

(Wäre aber auch dann hilfreich, wenn man z.B. eine Anwendung für zwei Nutzer schreibt, die parallel Eingaben tätigen können.)

ec2021

Ich habe das Ganze mal aus dem Hauptsketch ausgelagert und zusammengefasst:

Dies sind die Anteile zum Einrichten/Initialisieren des tft-Displays und der Cursor-Routinen:

/*
  Initialisierung des TFT Displays
  und die Routinen zur Cursordarstellung

  2023-03-29
  by ec2021

  Diese Datei als "TFTCursor.h" im Verzeichnis des 
  Hauptsketches ablegen und im Hauptsketch durch

        #include "TFTCursor.h"

  einbinden.

  Verfügbare Funktionen:
  --------------------------------------------------------------------------------------------
  void setAll(int atX, int atY, uint16_t col, int tSize, unsigned long bTime);
  --------------------------------------------------------------------------------------------
      Setzen der x,y-Startposition des zu markierenden Textes
      der Farbe, der Textgröße und der Blinkzeit
      mit einem Aufruf

  --------------------------------------------------------------------------------------------
  void setCursor(int atX, int atY);
  --------------------------------------------------------------------------------------------
      Setzen der x,y-Startposition des Textes

  --------------------------------------------------------------------------------------------
  void setTextColor(uint16_t col);
  --------------------------------------------------------------------------------------------
      Setzen der Cursor-Farbe (kann auch von der tatsächlich verwendeten Textfarbe abweichen!)

  --------------------------------------------------------------------------------------------
  void setTextSize(int tSize);
  --------------------------------------------------------------------------------------------
      Setzen der Textgröße
      Damit werden Höhe, Breite und Länge des Cursors angepasst
      ebenso wie der Wert, um den der Cursor pro Stelle versetzt wird 

  --------------------------------------------------------------------------------------------
  void setBlinkTime(unsigned long bTime);
  --------------------------------------------------------------------------------------------
      Setzen der Blinkzeit in [ms], Default-Wert ist 300 ms

  --------------------------------------------------------------------------------------------
  void Blink(int Stelle);
  --------------------------------------------------------------------------------------------
      Diese Funktion läßt den Cursor in einem Takt von 300 ms (default) blinken
      Die Blinkgeschwindigkeit kann mit setBlinkTime(); geändert werden.
      WICHTIG. Diese Funktion benutzt die millis()-Funktion; sie muss also 
      in der loop() regelmäßig aufgerufen werden; blockierende Funktionen
      behindern das Blinken!

*/

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#define TFT_DC 9
#define TFT_CS 10
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);


struct CursorType {
   public :
    void setAll(int atX, int atY, uint16_t col, int tSize, unsigned long bTime);
    void setCursor(int atX, int atY);
    void setTextColor(uint16_t col);
    void setTextSize(int tSize);
    void setBlinkTime(unsigned long bTime);
    void Blink(int Stelle);
  private:  
    int x;
    int y;
    int charWidth;
    int charHeight;
    int barHeight;
    uint16_t color; 
    int Position = 0;
    unsigned long BlinkTime = 300;
    void Draw();
    void Delete();
};

void CursorType::setCursor(int atX, int atY){
  CursorType::x = atX;
  CursorType::y = atY;
}

void CursorType::setTextColor(uint16_t col){
  CursorType::color = col;
}

void CursorType::setTextSize(int tSize){
  CursorType::charWidth     = 6*tSize;
  CursorType::charHeight    = 6*(tSize+1);
  CursorType::barHeight     = tSize+1;
  switch(tSize) {
    case 1 : CursorType::charHeight -= 3;
         break;
    case 2 : CursorType::charHeight -= 2;
         break;
    case 4 :
    case 5 :
    case 6 : CursorType::charHeight    += 2;
         break;
    default: ;     
  }
}

void CursorType::setBlinkTime(unsigned long bTime){
  CursorType::BlinkTime = bTime;
}


void CursorType::setAll(int atX, int atY, uint16_t col, int tSize, unsigned long bTime){
  CursorType::setCursor(atX, atY);
  CursorType::setTextColor(col);
  CursorType::setTextSize(tSize);
  CursorType::setBlinkTime(bTime);
}

void CursorType::Draw(){
    tft.fillRect(CursorType::x+CursorType::Position*CursorType::charWidth,
                 CursorType::y+CursorType::charHeight,
                 CursorType::charWidth,
                 CursorType::barHeight,
                 CursorType::color);

}


void CursorType::Delete(){
   uint16_t oldCol = CursorType::color;
   CursorType::color = ILI9341_BLACK;
   CursorType::Draw();
   CursorType::color = oldCol;
}  

void CursorType::Blink(int Stelle){
  static unsigned long lastChange = 0;
  static boolean Paint = true;
  static int lastStelle = 0;
  if (lastStelle != Stelle) {
      CursorType::Position = lastStelle;
      CursorType::Delete();
      lastChange = 0;
      lastStelle = Stelle;
      Paint = true;
  }
  if (millis()-lastChange > CursorType::BlinkTime){
    CursorType::Position = lastStelle;
    lastChange = millis();
    if (Paint) {
       CursorType::Draw();
    } else {
       CursorType::Delete();
    }
    Paint = !Paint;
  }
 }

Hier eine Beispielanwendung dazu:

/*
  Beispiel für 
  https://forum.arduino.cc/t/tft-input-placeholder/1107597

  2023-03-29
  ec2021

*/

#include "TFTCursor.h"

#define LEFT 50
#define TOP  80

CursorType myCursor;

void Print(String txt, int atX, int atY, int tSize, uint16_t col){
        tft.setCursor(atX, atY);
        tft.setTextColor(col);
        tft.setTextSize(tSize);   
        tft.println(txt);
        //myCursor.setAll(atX, atY,col, tSize, 0); // oder einzeln:
        myCursor.setCursor(atX, atY);
        myCursor.setTextColor(col);
        myCursor.setTextSize(tSize);   
        myCursor.setBlinkTime(500);
};

int Stelle   = 0;
int count    = 1;
int textSize = 1;
unsigned long lastChange = 0;

void setup() {
  tft.begin();
  tft.setRotation(0);
  Print("00:00",LEFT, TOP, textSize, ILI9341_RED);
}


void loop() {
   myCursor.Blink(Stelle);
   if (millis()-lastChange > 3000){
     lastChange = millis();
     Stelle++;
     if (Stelle == 2) Stelle++;  // Hier ist der Doppelpunkt 
     if (Stelle > 4)  Stelle = 0;
     count++;
     if (!(count % 5)) {
        Print("00:00",LEFT, TOP, textSize, ILI9341_BLACK);
        textSize++;
        if (textSize > 6) textSize = 1;
        Stelle = 0;
        count = 1;
        if (textSize < 3) Print("00:00",LEFT, TOP, textSize, ILI9341_RED);
                     else Print("00:00",LEFT, TOP, textSize, ILI9341_YELLOW);
     }
   }  

}

Das obere File wird in das Verzeichnis des Hauptsketches kopiert und im letzteren per #include eingebunden.

  • Siehe auch Beschreibung/Anleitung im File TFTCursor.h
  • Beachten: Im Hauptsketch die #includes anpassen (wie im Beispiel)
  • Die Anwendung funktioniert auch bei Änderung der Bildschirm-Ausrichtung per tft.setRotation()!

Zum Testen siehe: https://wokwi.com/projects/360540106292135937

Gruß
ec2021

Schön, was ist wenn ich nehme andere Schriftart als Standard?
ZB. 32 Punkt. Die Standard Schriftart schon bei Size 3 ist nicht die schönste :wink:

Lässt sich machen, ist aber aufwendiger, da die Position einzelner Zeichen dann davon abhängen, was zuvor im String steht. Außerdem besteht die Möglichkeit, dass das Eingabefeld "jittert", wenn man es als kompletten String ausgibt.

Bin aber schon dran :wink:

Man kann sich per

tft.getTextBounds(stringText, x,y, &x1, &y1, &w, &h);

Höhe und Breite einzelner Schriftzeichen oder eines kompletten Strings holen. Daraus lässt sich die x,y-Schreibposition des Balken für jede Stelle des Strings ableiten.

Heute wird's aber nichts mehr.

Alternativ kann man natürlich einfach jeden Eingabecharakter höchst selbst positionieren und ausgeben. Dann ist das darunter Blinken einfacher ...

Gruß
ec2021

Wegen mir must du das nicht machen ;), benutze so wie so nicht ADA Libs sind zu langsam,
Sollte ich das mal brauchen dann wird was produciert.
Trotzt dem schön :wave: :wave: :wave:

Gerne, habe ich mit schon gedacht.

Aber es hat mich eben gereizt... :wink:

Gruß
ec2021

1 Like

Und wird bestimmt von Einigen benutzt, wen nicht Heute dann in der Zukunft :wink:

Und hier ist der erste Wurf:

Erster Wurf (formal nicht korrekt)
/*
  Initialisierung des TFT Displays
  und die Routinen zur Cursordarstellung

  2023-03-30
  by ec2021

  Diese Datei als "TFTCursor.h" im Verzeichnis des 
  Hauptsketches ablegen und im Hauptsketch durch

        #include "TFTCursor.h"

  einbinden.

  Die Routinen wurden so angepasst, dass sie sowohl mit Systemschrift wie auch Proportional-
  schriften aus der ADAFruit-Bibliothek funktionieren. Sie können mit verschiedenen Textgrößen
  wie auch unterschiedlicher Display-Rotation verwendet werden.

  Bekannte Einschränkung: 
       Der Text ***muss*** in eine Zeile passen, sonst erscheint der Cursor 
       an einer falschen Stelle.

  Für den Anwender verfügbare Funktionen:
   
  --------------------------------------------------------------------------------------------
  void setCursorColor(uint16_t col);
  --------------------------------------------------------------------------------------------
      Setzen der Cursor-Farbe (kann also von der verwendeten Textfarbe abweichen!)
  
  --------------------------------------------------------------------------------------------
  void setbkColor(uint16_t col);
  --------------------------------------------------------------------------------------------
      Setzen der Hintergrund-Farbe, mit der zuvor geschriebener Text gelöscht wird.
      Das "Übermalen" des Textes mit einem Rechteck in Hintergrundfarbe ist bei
      Verwendung von Proportionalschrift die sicherste Methode, um Artefakte zu
      vermeiden.
  
  --------------------------------------------------------------------------------------------
  void print(String aTxt,int atX, int atY, uint16_t col);
  --------------------------------------------------------------------------------------------
      Gibt den Text an der Stelle atX, atY in der Farbe col aus.
      Mit diesem Aufruf werden automatisch alle Parameter gesetzt bzw. ermittelt,
      die für das "Unterstreichen" mit dem Cursor erforderlich sind.

      Bei erneutem Aufruf von print() wird der vorherigen Text auf dem TFT-Schirm gelöscht.     
      Dabei wird die Hintergrundfarbe angewandt, Defaultwert ist Schwarz. Weicht die
      verwendete Hintergrund davon ab, kann dies mit setBkColor() angepasst werden.

  --------------------------------------------------------------------------------------------
  void setBlinkTime(unsigned long bTime);
  --------------------------------------------------------------------------------------------
      Setzen der Blinkzeit in [ms], Default-Wert ist 300 ms

  --------------------------------------------------------------------------------------------
  void Blink(int Stelle);
  --------------------------------------------------------------------------------------------
      Diese Funktion läßt den Cursor in einem Takt von 300 ms (default) blinken
      Die Blinkgeschwindigkeit kann mit setBlinkTime(); geändert werden.
      WICHTIG. Diese Funktion benutzt die millis()-Funktion; sie muss also 
      in der loop() regelmäßig aufgerufen werden; blockierende Funktionen
      behindern das Blinken!

  --------------------------------------------------------------------------------------------
  void ClearBlink();    
  --------------------------------------------------------------------------------------------
      Um sicherzugehen, dass nach einem Wechsel der Print-Position keine "Reste" zurückbleiben,
      empfiehlt es sich, vor einem erneuten Print zuerst ClearBlink() aufzurufen.

*/

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#define TFT_DC 9
#define TFT_CS 10
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

struct cDataType {
   int gap = 0;
   int width = 0;
   int height = 0;
};

cDataType getCharData(char aChar){
  int16_t  x1, y1;
  uint16_t w, h;
  cDataType c;
  if (!aChar) return c;
  tft.getTextBounds(String(aChar), 10,180, &x1, &y1, &c.width, &c.height);
  tft.getTextBounds(String(aChar)+String(aChar), 10,180, &x1, &y1, &w, &h);
  c.gap = (w - 2 * c.width)/2;
  return c;
}

struct CursorType {
   public :
    void print(String aTxt,int atX, int atY, uint16_t col);
    void setCursorColor(uint16_t col);
    void setBkColor(uint16_t col);
    void setBlinkTime(unsigned long bTime);
    void Blink(int aPos);
    void ClearBlink();
    boolean FixedFontInUse = true;
  private:  
    int x;
    int y;
    String txt = "";
    cDataType cData;
    int xOffset = 0;
    int yPos = 0;
    int barHeight = 0;;
    int barWidth = 0;
    uint16_t color; 
    uint16_t bkColor = ILI9341_BLACK;
    int Position = 0;
    unsigned long BlinkTime = 300;
    void Draw();
    void Delete();
    void getOffsets();    
    void setCursor(int atX, int atY);
};

void CursorType::print(String aTxt,int atX, int atY, uint16_t col){
  int16_t  x1, y1;
  uint16_t w, h;
  if (CursorType::txt != "") {
      tft.getTextBounds(CursorType::txt, CursorType::x, CursorType::y, &x1, &y1, &w, &h);
      tft.fillRect(x1,y1,w,h,CursorType::bkColor);      
  }

  CursorType::txt = aTxt;
  CursorType::x = atX;
  CursorType::y = atY;
  tft.setTextColor(col);
  tft.setCursor(CursorType::x, CursorType::y);
  tft.print(CursorType::txt);
  tft.getTextBounds(CursorType::txt, CursorType::x, CursorType::y, &x1, &y1, &w, &h);
  CursorType::yPos   = y1+2+h; 
}

void CursorType::setCursor(int atX, int atY){
  CursorType::x = atX;
  CursorType::y = atY;
}

void CursorType::setCursorColor(uint16_t col){
  CursorType::color = col;
}

void CursorType::setBkColor(uint16_t col){
  CursorType::bkColor = col;
}

void CursorType::setBlinkTime(unsigned long bTime){
  CursorType::BlinkTime = bTime;
}

void CursorType::getOffsets(){
  int16_t  x1, y1;
  uint16_t w, h;
  int xOff = 0;
  char prevC, actC;
  cDataType c; 
  CursorType::barWidth  = 3;
  CursorType::barHeight = 3;
  CursorType::xOffset   = 0;
  int len = CursorType::txt.length();
  
  if (!len) return;

  if (CursorType::Position < 1)   CursorType::Position = 1;  
  if (CursorType::Position > len) CursorType::Position = len;  
  
  if (len == 1 || CursorType::Position == 1) {
    c = getCharData(CursorType::txt.charAt(0));
    xOff =  -c.gap/2; 
    CursorType::barWidth  = c.width+ c.gap;
  } else {
    String sTxt = CursorType::txt.substring(0,CursorType::Position-1);
    tft.getTextBounds(sTxt, CursorType::x,CursorType::y, &x1, &y1, &w, &h);
    xOff = w + x1 - CursorType::x;
  
    if (CursorType::Position>1) {
      prevC = CursorType::txt.charAt(CursorType::Position-2);
      c = getCharData(prevC);
      xOff += c.gap;
    }  

    actC = CursorType::txt.charAt(CursorType::Position-1);
    c = getCharData(actC);
    xOff += c.gap;
    CursorType::barWidth  = c.width + c.gap;
  }  
  CursorType::barHeight = 3;
  CursorType::xOffset = xOff;
}

void CursorType::Draw(){
   int width;
   int xPos;
   CursorType::getOffsets();
   xPos  = CursorType::x + CursorType::xOffset;
   tft.fillRect( xPos,
                 CursorType::yPos,
                 CursorType::barWidth,
                 CursorType::barHeight,
                 CursorType::color);

}

void CursorType::Delete(){
   uint16_t oldCol = CursorType::color;
   CursorType::color = CursorType::bkColor;
   CursorType::Draw();
   CursorType::color = oldCol;
}  

void CursorType::Blink(int aPos){
  static unsigned long lastChange = 0;
  static boolean Paint = true;
  static int lastPos = 0;
  if (lastPos != aPos)  {
      CursorType::Position = lastPos;
      CursorType::Delete();
      lastChange = 0;
      lastPos = aPos;
      Paint = true;
  }
  if (millis()-lastChange > CursorType::BlinkTime){
    CursorType::Position = lastPos;
    lastChange = millis();
    if (Paint) {
       CursorType::Draw();
    } else {
       CursorType::Delete();
    }
    Paint = !Paint;
  }
 }

void CursorType::ClearBlink(){
      CursorType::Delete();
  }

Beispielprogramm dazu:

/*
  Beispiel für
  https://forum.arduino.cc/t/tft-input-placeholder/1107597

  2023-03-30
  ec2021

*/

#include "TFTCursor.h"
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans24pt7b.h>

#define LEFT 10
#define TOP  100

CursorType myCursor;

int Pos   = 1;
unsigned long lastChange = 0;

constexpr int NoOfStrings = 3;
int StrNo       = 0;
String txt[NoOfStrings] = {"00:00", "aBcDe", "01:2a3@4"};


void setup() {
  Serial.begin(115200);
  Serial.println("Start ...");
  tft.begin();
  tft.setRotation(3);
  tft.setFont(&FreeSans24pt7b);
  tft.setTextSize(1);

  myCursor.setCursorColor(ILI9341_YELLOW);
  myCursor.setBlinkTime(300);
  myCursor.print(txt[0], LEFT, TOP, ILI9341_RED);
}

void ChangeText() {
  Pos = 1;
  StrNo++;
  if (StrNo >= NoOfStrings) StrNo = 0;
  switch (StrNo) {
    case 0 :  myCursor.print(txt[StrNo], LEFT + 80, TOP, ILI9341_RED);
      break;
    case 1 :  myCursor.print(txt[StrNo], LEFT + 40, TOP + 30, ILI9341_GREEN);
      break;
    case 2 :  myCursor.print(txt[StrNo], LEFT, TOP - 20, ILI9341_BLUE);
      break;
    default :  myCursor.print(txt[StrNo], LEFT, TOP, ILI9341_YELLOW);

  }
}

void loop() {
  myCursor.Blink(Pos);
  if (millis() - lastChange > 1400) {
    myCursor.ClearBlink();
    lastChange = millis();
    Pos++;
    if (Pos > txt[StrNo].length()) ChangeText();
  }
}

Funktioniert mit Proportionalschrift und Systemfont sowie mit verschiedenen Schriftgrößen und den vier möglichen "Rotationen"...

Siehe hier : https://wokwi.com/projects/360609042954214401

Die Position des Cursors und seine Breite wird über die getTextBounds() errechnet. Das geht vermutlich alles noch etwas eleganter, aber ich bin erstmal zufrieden ... :wink:

Im Hauptsketch braucht man Folgendes;

 // Deklaration
 CursorType myCursor;

 // Vorbereitung
  myCursor.setCursorColor(ILI9341_YELLOW);
  myCursor.setBlinkTime(300);
 
 // Print des Strings, z.B. txt = "00:00" o.ä.
  myCursor.print(txt, LEFT, TOP, ILI9341_RED);

 // Regelmäßger Aufruf der Blinkroutine mit Angabe der
 //  mit Cursor zu unterlegenden Position (1 .. n)
  myCursor.Blink(Pos);

 // Vor erneutem Aufruf der Print-Routine, hiermit eventuelle 
 // Reste des Cursors löschen
  myCursor.ClearBlink();

Anleitung siehe im oberen File ...

Gruß
ec2021

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.