String picker search field. Fix #701

This commit is contained in:
2025-03-20 16:38:09 -05:00
parent d10b6e9e15
commit a17f5b1a7c
3 changed files with 95 additions and 0 deletions

View File

@@ -3,6 +3,10 @@
<dialog defbtn='done'> <dialog defbtn='done'>
<pict type='dlog' num='16' top='8' left='8'/> <pict type='dlog' num='16' top='8' left='8'/>
<text name='title' size='large' top='6' left='50' width='256' height='14'>Select:</text> <text name='title' size='large' top='6' left='50' width='256' height='14'>Select:</text>
<field name='search-field' relative='pos-in pos' rel-anchor='prev' top='0' left='2' width='200' height='16'/>
<button name='search' type='regular' def-key='ctrl f' relative='pos neg' rel-anchor='prev' left='5' top='4'>Search</button>
<led name='reverse' state='off' relative='pos pos-in' rel-anchor='prev' top='6' left='7'>Reverse</led>
<text name='search-label' size='9' framed='true' relative='pos-in pos' anchor='search-field' top='4' left='0' width='250' height='10'/>
<group name='strings'> <group name='strings'>
<!-- Column 1 --> <!-- Column 1 -->
<led name='led1' state='off' top='54' left='8'/> <led name='led1' state='off' top='54' left='8'/>

View File

@@ -12,10 +12,14 @@
#include <algorithm> #include <algorithm>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include "dialogxml/widgets/field.hpp" #include "dialogxml/widgets/field.hpp"
#include "fileio/resmgr/res_dialog.hpp" #include "fileio/resmgr/res_dialog.hpp"
#include "sounds.hpp" #include "sounds.hpp"
#include "gfx/render_shapes.hpp"
const sf::Color HILITE_COLOUR = Colours::LIGHT_GREEN;
cStringChoice::cStringChoice(cDialog* parent, bool editable) cStringChoice::cStringChoice(cDialog* parent, bool editable)
: editable(editable) : editable(editable)
@@ -41,6 +45,7 @@ void cStringChoice::attachHandlers() {
dlg["right"].attachClickHandler(std::bind(&cStringChoice::onRight,this)); dlg["right"].attachClickHandler(std::bind(&cStringChoice::onRight,this));
dlg["done"].attachClickHandler(std::bind(&cStringChoice::onOkay,this,_1)); dlg["done"].attachClickHandler(std::bind(&cStringChoice::onOkay,this,_1));
dlg["cancel"].attachClickHandler(std::bind(&cStringChoice::onCancel,this,_1)); dlg["cancel"].attachClickHandler(std::bind(&cStringChoice::onCancel,this,_1));
dlg["search"].attachClickHandler(std::bind(&cStringChoice::onSearch,this,_1));
leds = &dynamic_cast<cLedGroup&>(dlg["strings"]); leds = &dynamic_cast<cLedGroup&>(dlg["strings"]);
leds->attachFocusHandler(std::bind(&cStringChoice::onSelect,this,_3)); leds->attachFocusHandler(std::bind(&cStringChoice::onSelect,this,_3));
if(editable) { if(editable) {
@@ -156,6 +161,88 @@ bool cStringChoice::onOkay(cDialog& me){
return true; return true;
} }
bool cStringChoice::onSearch(cDialog& me){
cLed& reverse_led = dynamic_cast<cLed&>(me["reverse"]);
bool reversed = reverse_led.getState() == led_red;
size_t page_delta = reversed ? -1 : 1;
size_t loop_from_page = reversed ? 0 : lastPage();
size_t loop_to_page = !reversed ? 0 : lastPage();
std::string loop_from_str = reversed ? "Reached the beginning." : "Reached the end.";
std::string loop_to_str = !reversed ? "Starting from the beginning" : "Starting from the end";
size_t start_page = page;
bool looped_once = false;
std::string new_search = me["search-field"].getText();
boost::to_lower(new_search);
bool repeat_search = new_search == search_str;
search_str = new_search;
cControl& output = me["search-label"];
clearHighlights();
if(search_str.empty()) {
output.setText("");
return true;
}
bool found_next = false;
if(!repeat_search){
// First-time search, check the current page before paging forward
if(highlightSearch()) found_next = true;
}
while(!found_next){
if(page == loop_from_page){
// Looped already, found nothing. Avoid infinite loop:
if(looped_once){
break;
}
output.setText(loop_from_str + " " + loop_to_str);
looped_once = true;
page = loop_to_page;
}else{
page += page_delta;
}
found_next = highlightSearch();
}
if(!looped_once){
output.setText("");
}
if(!found_next){
page = start_page;
output.setText("Not found");
}else{
fillPage();
}
return found_next;
}
void cStringChoice::clearHighlights() {
leds->forEach([this](std::string, cControl& led) {
led.setColour(dlg.getDefTextClr());
});
}
bool cStringChoice::highlightSearch() {
bool match_on_page = false;
for(int offset = 0; offset < per_page; ++offset){
std::string led_id = "led" + std::to_string(offset + 1);
int str_idx = page * per_page + offset;
// Copy the string
std::string str = strings[str_idx];
// Make case insensitive
boost::to_lower(str);
if(str.find(search_str) != std::string::npos){
match_on_page = true;
leds->getChild(led_id).setColour(HILITE_COLOUR);
}
}
return match_on_page;
}
bool cStringChoice::onSelect(bool losing) { bool cStringChoice::onSelect(bool losing) {
if(losing) return true; if(losing) return true;
int i = boost::lexical_cast<int>(leds->getSelected().substr(3)); int i = boost::lexical_cast<int>(leds->getSelected().substr(3));

View File

@@ -27,12 +27,16 @@ class cStringChoice {
bool onOkay(cDialog& me); bool onOkay(cDialog& me);
bool onSelect(bool losing); bool onSelect(bool losing);
bool onFocus(std::string which, bool losing); bool onFocus(std::string which, bool losing);
bool onSearch(cDialog& me);
void clearHighlights();
bool highlightSearch();
void attachHandlers(); void attachHandlers();
void fillPage(); void fillPage();
void savePage(); void savePage();
size_t lastPage() const; size_t lastPage() const;
std::vector<std::string> strings; std::vector<std::string> strings;
size_t page, cur; size_t page, cur;
std::string search_str;
cLedGroup* leds; cLedGroup* leds;
std::function<void(cStringChoice&,int)> select_handler; std::function<void(cStringChoice&,int)> select_handler;
cStringChoice(cDialog* parent, bool editable = false); cStringChoice(cDialog* parent, bool editable = false);