209 lines
4.5 KiB
C++
209 lines
4.5 KiB
C++
#ifndef UI_LIST_H
|
|
#define UI_LIST_H
|
|
|
|
#undef min
|
|
#undef max
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include "UIElement.h"
|
|
#include "UIButton.h"
|
|
|
|
class UIListModel {
|
|
|
|
std::vector<std::string> entries;
|
|
|
|
public:
|
|
|
|
void add(const std::string& str) {
|
|
entries.push_back(str);
|
|
}
|
|
|
|
void remove(const size_t idx) {
|
|
entries.erase(entries.begin()+idx);
|
|
}
|
|
|
|
size_t size() const {
|
|
return entries.size();
|
|
}
|
|
|
|
const std::string& get(const size_t idx) const {
|
|
return entries[idx];
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class UIList : public UIElement, public UIButton::Listener {
|
|
|
|
public:
|
|
|
|
class Listener {
|
|
public:
|
|
virtual void onSelected(UIList* lst, int idx) = 0;
|
|
};
|
|
|
|
private:
|
|
|
|
UIListModel model;
|
|
int offset = 0;
|
|
int selectedIndex = -1;
|
|
|
|
UIButton btnUp;
|
|
UIButton btnDown;
|
|
|
|
static constexpr const int btnW = 32;
|
|
|
|
Color cRect = Color::fromRGB(0,0,0);
|
|
Color cNormalBG = Color::fromRGB(230,230,230);
|
|
Color cSelectedBG = Color::fromRGB(190,190,255);
|
|
Color cText = Color::fromRGB(0,0,0);
|
|
|
|
Listener* listener = nullptr;
|
|
|
|
static constexpr const char* TAG = "UIList";
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
UIList() : btnUp("<"), btnDown(">") {
|
|
addChild(&btnUp);
|
|
addChild(&btnDown);
|
|
btnUp.setListener(this);
|
|
btnDown.setListener(this);
|
|
}
|
|
|
|
void setListener(Listener* l) {
|
|
this->listener = l;
|
|
}
|
|
|
|
// UIListModel& getModel() {
|
|
// setNeedsRedraw();
|
|
// return model;
|
|
// }
|
|
|
|
// const UIListModel& getModel() const {
|
|
// return model;
|
|
// }
|
|
|
|
void add(const std::string& str) {
|
|
model.add(str);
|
|
btnUp.setVisible(needsScroll());
|
|
btnDown.setVisible(needsScroll());
|
|
setNeedsRedraw();
|
|
}
|
|
|
|
void remove(const size_t idx) {
|
|
model.remove(idx);
|
|
if (idx == selectedIndex) {
|
|
selectedIndex = -1;
|
|
if (listener) {listener->onSelected(this, selectedIndex);}
|
|
} else if (idx < selectedIndex) {
|
|
--selectedIndex;
|
|
if (listener) {listener->onSelected(this, selectedIndex);}
|
|
}
|
|
if (offset > maxOffset()) {offset = maxOffset();}
|
|
btnUp.setVisible(needsScroll());
|
|
btnDown.setVisible(needsScroll());
|
|
setNeedsRedraw();
|
|
}
|
|
|
|
size_t size() const {
|
|
return model.size();
|
|
}
|
|
|
|
const std::string& get(const size_t idx) const {
|
|
return model.get(idx);
|
|
}
|
|
|
|
int getSelectedIndex() const {
|
|
return this->selectedIndex;
|
|
}
|
|
|
|
void reLayout() override {
|
|
int h = rect.h / 2 - 1;
|
|
btnUp.setRect(rect.x+rect.w-btnW-1, rect.y+1, btnW, h);
|
|
btnDown.setRect(rect.x+rect.w-btnW-1, rect.y+h+2, btnW, h);
|
|
}
|
|
|
|
virtual void draw(UIPainter& p) override {
|
|
|
|
debugMod(TAG, "draw()");
|
|
debugMod4(TAG, "rect: %d %d %d %d", rect.x, rect.y, rect.w, rect.h);
|
|
|
|
const uint8_t oy = (elementHeight() - fnt_f1.getHeight()) / 2;
|
|
const uint16_t entryW = rect.w - (needsScroll() ? btnW : 0);
|
|
|
|
// outline rectangle
|
|
p.setFG(cNormalBG);
|
|
p.fillRect(rect.x+1, rect.y+1, entryW, rect.h-2);
|
|
p.setFG(cRect);
|
|
p.drawRect(rect);
|
|
|
|
// display as many elements as fit within the list's height
|
|
for (unsigned int i = 0; i < elementsVisible(); ++i) {
|
|
|
|
// determine position (y-coordinate)
|
|
const uint16_t y = i * elementHeight();
|
|
|
|
// determine element from model
|
|
const int idx = i+offset;
|
|
const bool selected = idx == selectedIndex;
|
|
|
|
// draw background depending on selection
|
|
//p.setFG( selected ? cSelectedBG : cNormalBG );
|
|
//p.fillRect(UIRect(rect.x+1, rect.y+y+1, entryW-1, elementHeight()-1));
|
|
if (selected) {
|
|
p.setFG( cSelectedBG );
|
|
p.fillRect(UIRect(rect.x+1, rect.y+y+1, entryW-1, elementHeight()-1));
|
|
}
|
|
|
|
// draw text
|
|
p.setFG(cText);
|
|
p.drawText(rect.x+3, rect.y + y + oy, model.get(idx).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
virtual void onTouchDown(uint16_t, uint16_t y) override {
|
|
unsigned int idx = y / elementHeight() + offset;
|
|
this->selectedIndex = (idx < numElements()) ? (idx) : (-1);
|
|
debugMod1(TAG, "selected: %d", selectedIndex);
|
|
setNeedsRedraw();
|
|
if (listener) {listener->onSelected(this, selectedIndex);}
|
|
}
|
|
|
|
virtual void onTouch(uint16_t, uint16_t) override {
|
|
|
|
}
|
|
|
|
virtual void onTouchUp() override {
|
|
|
|
}
|
|
|
|
virtual void onClick(UIButton* btn) override {
|
|
if (btn == &btnUp) {
|
|
if (offset > 0) {--offset; setNeedsRedraw();}
|
|
} else if (btn == &btnDown) {
|
|
if (offset < maxOffset()) {++offset; setNeedsRedraw();}
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
unsigned int elementHeight() const {return 16;}
|
|
unsigned int maxElementsVisible() const {return rect.h / elementHeight();}
|
|
unsigned int elementsVisible() const {return std::min(maxElementsVisible(), numElements());}
|
|
unsigned int numElements() const {return model.size();}
|
|
unsigned int maxOffset() const {return numElements()-maxElementsVisible();}
|
|
bool needsScroll() const {return numElements() > maxElementsVisible();}
|
|
|
|
};
|
|
|
|
#endif // UI_LIST_H
|