Listbox multiline selectable (#1549)
* Listbox multiline selectable * 1. Add check, long press was still calling OnTouchEnd. 2. When deselecting in multiselect focus was not update 3. On long press in multiselect only update focus
This commit is contained in:
@@ -51,12 +51,48 @@ ListBase::ListBase(Window *parent, const rect_t &rect, std::vector<std::string>
|
||||
|
||||
void ListBase::setSelected(int selected)
|
||||
{
|
||||
if (selected >= 0 && selected < (int)names.size() && selected != this->selected) {
|
||||
this->selected = selected;
|
||||
setScrollPositionY(lineHeight * this->selected - lineHeight);
|
||||
if (_setValue != nullptr) {
|
||||
_setValue(this->selected);
|
||||
int count = (int)names.size();
|
||||
if(selected < 0 || selected >= count)
|
||||
return;
|
||||
|
||||
if(selectionType == LISTBOX_SINGLE_SELECT) {
|
||||
if (selected != this->selected) {
|
||||
this->selected = selected;
|
||||
setScrollPositionY(lineHeight * this->selected - lineHeight);
|
||||
if (_setValue != nullptr) {
|
||||
_setValue(this->selected);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
} else if(selectionType == LISTBOX_MULTI_SELECT) {
|
||||
this->selected = selected;
|
||||
if(selectedIndexes.find(selected) != selectedIndexes.end()) { // Already selected -> Deselect it
|
||||
selectedIndexes.erase(selected);
|
||||
setScrollPositionY(lineHeight * this->selected - lineHeight);
|
||||
if(_multiSelectHandler != nullptr)
|
||||
_multiSelectHandler(selectedIndexes);
|
||||
invalidate();
|
||||
} else { // Not Selected -> Select it
|
||||
selectedIndexes.insert(selected);
|
||||
setScrollPositionY(lineHeight * this->selected - lineHeight);
|
||||
if (_setValue != nullptr) {
|
||||
_setValue(this->selected);
|
||||
}
|
||||
if(_multiSelectHandler != nullptr)
|
||||
_multiSelectHandler(selectedIndexes);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListBase::setFocusLine(int selected)
|
||||
{
|
||||
if(selectionType == LISTBOX_MULTI_SELECT) {
|
||||
int count = (int)names.size();
|
||||
if(selected < 0 || selected >= count)
|
||||
return;
|
||||
|
||||
this->selected = selected;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -83,9 +119,23 @@ void ListBase::paint(BitmapBuffer *dc)
|
||||
|
||||
int curY = 0;
|
||||
for (int n = 0; n < (int)names.size(); n++) {
|
||||
dc->drawSolidFilledRect(1, curY, rect.w - 2, lineHeight, n == selected ? COLOR_THEME_FOCUS : COLOR_THEME_PRIMARY2);
|
||||
LcdFlags bgcolor = COLOR_THEME_PRIMARY2;
|
||||
LcdFlags textColor = COLOR_THEME_SECONDARY1;
|
||||
|
||||
LcdFlags textColor = n == selected ? COLOR_THEME_PRIMARY2 : COLOR_THEME_SECONDARY1;
|
||||
// Color selected line(s)
|
||||
if((selectionType == LISTBOX_SINGLE_SELECT && n == selected) ||
|
||||
(selectionType == LISTBOX_MULTI_SELECT && (selectedIndexes.find(n) != selectedIndexes.end()))) {
|
||||
bgcolor = COLOR_THEME_FOCUS;
|
||||
textColor = COLOR_THEME_PRIMARY2;
|
||||
}
|
||||
|
||||
dc->drawSolidFilledRect(1, curY, rect.w - 2, lineHeight, bgcolor);
|
||||
|
||||
// Draw a dotted line around the current item
|
||||
if(n == selected && selectionType == LISTBOX_MULTI_SELECT &&
|
||||
hasFocus()) {
|
||||
dc->drawRect(2, curY, rect.w - 4, lineHeight, 2, DOTTED);
|
||||
}
|
||||
|
||||
auto fontHeight = getFontHeight(FONT(STD));
|
||||
drawLine(dc, { 8, curY + (lineHeight - fontHeight) / 2, rect.w, lineHeight}, n, textColor);
|
||||
@@ -105,13 +155,19 @@ void ListBase::paint(BitmapBuffer *dc)
|
||||
switch (event) {
|
||||
case EVT_ROTARY_RIGHT:
|
||||
oldSelected = (selected + 1) % names.size();
|
||||
setSelected(oldSelected);
|
||||
if(selectionType == LISTBOX_SINGLE_SELECT)
|
||||
setSelected(oldSelected);
|
||||
else
|
||||
setFocusLine(oldSelected);
|
||||
onKeyPress();
|
||||
break;
|
||||
case EVT_ROTARY_LEFT:
|
||||
oldSelected--;
|
||||
if (oldSelected < 0) oldSelected = names.size() - 1;
|
||||
setSelected(oldSelected);
|
||||
if(selectionType == LISTBOX_SINGLE_SELECT)
|
||||
setSelected(oldSelected);
|
||||
else
|
||||
setFocusLine(oldSelected);
|
||||
onKeyPress();
|
||||
break;
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
@@ -121,6 +177,8 @@ void ListBase::paint(BitmapBuffer *dc)
|
||||
}
|
||||
break;
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if(selectionType == LISTBOX_MULTI_SELECT)
|
||||
setSelected(this->selected);
|
||||
if (pressHandler) {
|
||||
killEvents(event);
|
||||
pressHandler(event);
|
||||
@@ -147,10 +205,14 @@ void ListBase::checkEvents(void)
|
||||
|
||||
if (isLongPress()) {
|
||||
if (longPressHandler) {
|
||||
setSelected(yDown / lineHeight);
|
||||
if(selectionType == LISTBOX_SINGLE_SELECT)
|
||||
setSelected(yDown / lineHeight);
|
||||
else
|
||||
setFocusLine(yDown / lineHeight);
|
||||
longPressHandler(0);
|
||||
killAllEvents();
|
||||
duration10ms = 0;
|
||||
waslongpress = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -158,15 +220,16 @@ void ListBase::checkEvents(void)
|
||||
|
||||
bool ListBase::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY)
|
||||
{
|
||||
if (touchState.event == TE_SLIDE_END) {
|
||||
if (touchState.event == TE_SLIDE_END) {
|
||||
duration10ms = 0;
|
||||
}
|
||||
|
||||
|
||||
return FormField::onTouchSlide(x, y, startX, startY, slideX, slideY);
|
||||
}
|
||||
|
||||
bool ListBase::onTouchStart(coord_t x, coord_t y)
|
||||
{
|
||||
waslongpress = false;
|
||||
if (duration10ms == 0) {
|
||||
duration10ms = getTicks();
|
||||
}
|
||||
@@ -180,7 +243,7 @@ bool ListBase::onTouchStart(coord_t x, coord_t y)
|
||||
bool ListBase::onTouchEnd(coord_t x, coord_t y)
|
||||
{
|
||||
if (!isEnabled()) return false;
|
||||
if (slidingWindow)
|
||||
if (slidingWindow || waslongpress)
|
||||
return false; // if we slide then this is not a selection
|
||||
|
||||
auto selected = yDown / lineHeight;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include "bitmapbuffer.h"
|
||||
#include "libopenui.h"
|
||||
#include "touch.h"
|
||||
@@ -37,13 +38,24 @@ class ListBase : public FormField
|
||||
uint8_t lineHeight = MENUS_LINE_HEIGHT,
|
||||
WindowFlags windowFlags = 0, LcdFlags lcdFlags = 0);
|
||||
|
||||
|
||||
typedef enum {
|
||||
LISTBOX_SINGLE_SELECT,
|
||||
LISTBOX_MULTI_SELECT
|
||||
} selecttype_e;
|
||||
|
||||
// draw one line of the list. Default implementation just draws the names. Oher types of lists
|
||||
// can draw anything they want.
|
||||
virtual void drawLine(BitmapBuffer *dc, const rect_t &rect, uint32_t index, LcdFlags lcdFlags);
|
||||
void paint (BitmapBuffer *dc) override;
|
||||
|
||||
virtual void setSelected(int selected);
|
||||
virtual void setFocusLine(int selected);
|
||||
void clearSelection() {selectedIndexes.clear(); invalidate();}
|
||||
|
||||
void setMultiSelectHandler(std::function<void(std::set<uint32_t>)> handler)
|
||||
{
|
||||
_multiSelectHandler = std::move(handler);
|
||||
}
|
||||
|
||||
void setLongPressHandler(std::function<void(event_t)> handler)
|
||||
{
|
||||
@@ -55,6 +67,11 @@ class ListBase : public FormField
|
||||
pressHandler = std::move(handler);
|
||||
}
|
||||
|
||||
void setSelectionMode(selecttype_e st)
|
||||
{
|
||||
selectionType = st;
|
||||
}
|
||||
|
||||
void setActiveIndex(int index)
|
||||
{
|
||||
if (index >= 0 && index < (int) names.size()) {
|
||||
@@ -108,9 +125,13 @@ class ListBase : public FormField
|
||||
std::vector<std::string> names;
|
||||
std::function<uint32_t()> _getValue;
|
||||
std::function<void(uint32_t)> _setValue;
|
||||
std::function<void(std::set<uint32_t>)> _multiSelectHandler = nullptr;
|
||||
int lineHeight;
|
||||
int32_t selected = -1;
|
||||
int32_t activeIndex = -1;
|
||||
std::set<uint32_t> selectedIndexes;
|
||||
selecttype_e selectionType = LISTBOX_SINGLE_SELECT;
|
||||
bool waslongpress = false;
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
uint32_t duration10ms;
|
||||
|
||||
Reference in New Issue
Block a user