Skip to content

Instantly share code, notes, and snippets.

@macbedn
Last active February 7, 2026 19:46
Show Gist options
  • Select an option

  • Save macbedn/8b4ade51924d00ff0894bbea992e356b to your computer and use it in GitHub Desktop.

Select an option

Save macbedn/8b4ade51924d00ff0894bbea992e356b to your computer and use it in GitHub Desktop.
mod - playlist widget v-tom wersja 7.5-8.2

modyfikacja PLAYLIST WIDGET - paginacja zamiast przewijania całego ekranu

przetestowane dla:

  • v-tom 7.13 (powinno działać z v-tom 7.5-8.2)
  • ekranu 480x320 ST7796 (dla ILI9488 też powinno działać)

należy wymienić cały obecny kod PLAYLIST WIDGET na poniższy oraz wprowadzić zmianę w mytheme.h

mytheme.h

#define COLOR_PL_CURRENT    250,  250,  250 // kolor wybranego na playliście streamu
#define COLOR_PL_CURRENT_BG    0, 0,  0     // zerowanie do tła
#define COLOR_PL_CURRENT_FILL    0, 0,  0   // zerowanie do tła
#define COLOR_PLAYLIST_0    165,  165,  165 // kolor pozostałych linii playlisty, następne kolory nieużywane w kodzie
#define COLOR_PLAYLIST_1    165,  165,  165
#define COLOR_PLAYLIST_2    165,  165,  165
#define COLOR_PLAYLIST_3    165,  165,  165
#define COLOR_PLAYLIST_4    165,  165,  165

widgets.cpp

// =================================
// PLAYLIST WIDGET - FINALNA WERSJA 
// =================================

#define MAX_PL_PAGE_ITEMS 15 
static String _plCache[MAX_PL_PAGE_ITEMS];
static int16_t _plLoadedPage = -1;
static int16_t _plLastGlobalPos = -1;
static uint32_t _plLastDrawTime = 0; 

void PlayListWidget::init(ScrollWidget *current) {
  Widget::init({0, 0, 0, WA_LEFT}, 0, 0);

  #ifndef DSP_LCD
    _plItemHeight = playlistConf.widget.textsize * (CHARHEIGHT - 1) + playlistConf.widget.textsize * 4;
    
    _plTtemsCount = (dsp.height() - 2) / _plItemHeight;
    if (_plTtemsCount < 1) _plTtemsCount = 1;
    if (_plTtemsCount > MAX_PL_PAGE_ITEMS) _plTtemsCount = MAX_PL_PAGE_ITEMS;

    uint16_t contentHeight = _plTtemsCount * _plItemHeight;
    _plYStart = (dsp.height() - contentHeight) / 2;

  #else
    _plTtemsCount = PLMITEMS;
    _plCurrentPos = 0;
  #endif

  _plLoadedPage = -1;
  _plLastGlobalPos = -1;
  _plCurrentPos = 0;
  _plLastDrawTime = 0;
}

void _loadPlaylistPage(int pageIndex, int itemsPerPage, int totalItems) {
  for (int i = 0; i < MAX_PL_PAGE_ITEMS; i++) _plCache[i] = "";

  if (config.playlistLength() == 0) return;

  File playlist = config.SDPLFS()->open(REAL_PLAYL, "r");
  File index = config.SDPLFS()->open(REAL_INDEX, "r");
  
  if (!playlist || !index) return;

  int startIdx = pageIndex * itemsPerPage;

  for (int i = 0; i < itemsPerPage; i++) {
    int currentGlobalIdx = startIdx + i;
    if (currentGlobalIdx >= config.playlistLength()) break;

    index.seek(currentGlobalIdx * 4, SeekSet);
    uint32_t posAddr;
    if (index.readBytes((char *)&posAddr, 4) != 4) break;

    playlist.seek(posAddr, SeekSet);
    String line = playlist.readStringUntil('\n');
    
    int tabIdx = line.indexOf('\t');
    if (tabIdx > 0) line = line.substring(0, tabIdx);
    line.trim();
    
    if (config.store.numplaylist && line.length() > 0) {
      _plCache[i] = String(currentGlobalIdx + 1) + " " + line;
    } else {
      _plCache[i] = line;
    }
  }
  playlist.close();
  index.close();
}

void PlayListWidget::drawPlaylist(uint16_t currentItem) {
  #ifndef DSP_LCD
  
  // Jeśli od ostatniego wywołania minęło > 2000ms to uznajemy, że wracamy z innego ekranu i przerysowujemy cały ekran
    
  bool isLongPause = (millis() - _plLastDrawTime > 2000);
  _plLastDrawTime = millis();

  int activeIdx = (currentItem > 0) ? (currentItem - 1) : 0;
  int itemsPerPage = _plTtemsCount;
  int newPage = activeIdx / itemsPerPage;
  int newLocalPos = activeIdx % itemsPerPage;

  _plCurrentPos = newLocalPos; 

  bool pageChanged = (newPage != _plLoadedPage);
  
  // WARUNKI PEŁNEGO RYSOWANIA:
  // 1. Zmiana strony
  // 2. Powrót do playera (V-Tom 2 sekundy / potwierdzenie automatyczne wyboru streamu)
  // 3. Pusty cache (start)
  
  if (pageChanged || isLongPause || _plCache[0].length() == 0) {
    
    // Jeśli to tylko powrót do tej samej strony, nie musimy czytać z pamięci (oszczędność czasu)
    // Czytamy z pamięci tylko gdy faktycznie zmieniła się strona lub cache jest pusty.
    if (pageChanged || _plCache[0].length() == 0) {
        _loadPlaylistPage(newPage, itemsPerPage, config.playlistLength());
        _plLoadedPage = newPage;
    }
    
    // Rysujemy całe tło i listę (to usuwa śmieci po playerze)
    dsp.fillRect(0, _plYStart, dsp.width(), itemsPerPage * _plItemHeight, config.theme.background);

    for (int i = 0; i < itemsPerPage; i++) {
      _printPLitem(i, _plCache[i].c_str());
    }

  } else {
    // SMART REDRAW - Szybka ścieżka dla płynnego przewijania
    int oldLocalPos = (_plLastGlobalPos > 0 ? _plLastGlobalPos - 1 : 0) % itemsPerPage;
    
    if (oldLocalPos != newLocalPos && oldLocalPos >= 0 && oldLocalPos < itemsPerPage) {
       _printPLitem(oldLocalPos, _plCache[oldLocalPos].c_str());
    }

    _printPLitem(newLocalPos, _plCache[newLocalPos].c_str());
  }

  _plLastGlobalPos = currentItem;

  #else
    dsp.setCursor(0, 0);
    dsp.print(F("CH: "));
    dsp.print(currentItem);
    dsp.print(F("        "));
  #endif
}

void PlayListWidget::_printPLitem(uint8_t pos, const char *item) {
  #ifndef DSP_LCD
    if (pos >= _plTtemsCount) return;

    int16_t yPos = _plYStart + pos * _plItemHeight;
    
    bool isSelected = (pos == _plCurrentPos);
    uint16_t fgColor = isSelected ? config.theme.plcurrent : config.theme.playlist[0];
    uint16_t bgColor = config.theme.background;

    // dsp.fillRect(0, yPos, dsp.width(), _plItemHeight - 1, bgColor); // zbędna linia (poprawka)

    if (item && item[0] != '\0') {
        dsp.setTextColor(fgColor, bgColor);
        dsp.setTextSize(playlistConf.widget.textsize);
        dsp.setCursor(TFT_FRAMEWDT, yPos + 4); 
        dsp.print(utf8To(item, true));
    }
  #endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment