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:
Cliff
2022-02-04 10:36:56 +10:00
committed by GitHub
parent f0077d4f7e
commit 9fc701c131
2 changed files with 98 additions and 14 deletions
+76 -13
View File
@@ -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;
+22 -1
View File
@@ -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;