Dialog tab order implementation
- Defaults to order of definition in file - tab-order attribtue can be set to a postive number to force towards the start, or a negative number to force towards the end
This commit is contained in:
@@ -689,6 +689,8 @@ template<> pair<string,cTextField*> cDialog::parse(Element& who /*field*/){
|
||||
attr->GetValue(&width);
|
||||
}else if(name == "height"){
|
||||
attr->GetValue(&height);
|
||||
}else if(name == "tab-order"){
|
||||
attr->GetValue(&p.second->tabOrder);
|
||||
}else throw xBadAttr("button",name,attr->Row(),attr->Column(),fname);
|
||||
}
|
||||
if(!foundTop) throw xMissingAttr("field","top",attr->Row(),attr->Column(),fname);
|
||||
@@ -756,14 +758,21 @@ void cDialog::loadFromFile(std::string path){
|
||||
throw xBadAttr(type,name,attr->Row(),attr->Column(),fname);
|
||||
}
|
||||
|
||||
vector<int> specificTabs, reverseTabs;
|
||||
for(node = node.begin(xml.FirstChildElement()); node != node.end(); node++){
|
||||
node->GetValue(&type);
|
||||
// Yes, I'm using insert instead of [] to add elements to the map.
|
||||
// In this situation, it's actually easier that way; the reason being, the
|
||||
// map key is obtained from the name attribute of each element.
|
||||
if(type == "field")
|
||||
controls.insert(parse<cTextField>(*node));
|
||||
else if(type == "text")
|
||||
if(type == "field") {
|
||||
auto field = parse<cTextField>(*node);
|
||||
controls.insert(field);
|
||||
tabOrder.push_back(field);
|
||||
if(field.second->tabOrder > 0)
|
||||
specificTabs.push_back(field.second->tabOrder);
|
||||
else if(field.second->tabOrder < 0)
|
||||
reverseTabs.push_back(field.second->tabOrder);
|
||||
} else if(type == "text")
|
||||
controls.insert(parse<cTextMsg>(*node));
|
||||
else if(type == "pict")
|
||||
controls.insert(parse<cPict>(*node));
|
||||
@@ -775,6 +784,41 @@ void cDialog::loadFromFile(std::string path){
|
||||
controls.insert(parse<cLedGroup>(*node));
|
||||
else throw xBadNode(type,node->Row(),node->Column(),fname);
|
||||
}
|
||||
// Sort by tab order
|
||||
// First, fill any gaps that might have been left, using ones that had no specific tab order
|
||||
// Of course, if there are not enough without a specific tab order, there could still be gaps
|
||||
using fld_t = decltype(tabOrder)::value_type;
|
||||
auto noTabOrder = [](fld_t x) {return x.second->tabOrder == 0;};
|
||||
if(!specificTabs.empty()) {
|
||||
int max = *max_element(specificTabs.begin(), specificTabs.end());
|
||||
for(int i = 1; i < max; i++) {
|
||||
auto check = find(specificTabs.begin(), specificTabs.end(), i);
|
||||
if(check != specificTabs.end()) continue;
|
||||
auto change = find_if(tabOrder.begin(), tabOrder.end(), noTabOrder);
|
||||
if(change == tabOrder.end()) break;
|
||||
change->second->tabOrder = i;
|
||||
}
|
||||
}
|
||||
if(!reverseTabs.empty()) {
|
||||
int max = -*min_element(reverseTabs.begin(), reverseTabs.end());
|
||||
for(int i = 1; i < max; i++) {
|
||||
auto check = find(reverseTabs.begin(), reverseTabs.end(), -i);
|
||||
if(check != reverseTabs.end()) continue;
|
||||
auto change = find_if(tabOrder.begin(), tabOrder.end(), noTabOrder);
|
||||
if(change == tabOrder.end()) break;
|
||||
change->second->tabOrder = -i;
|
||||
}
|
||||
}
|
||||
// Then, sort - first, positive tab order ascending; then zeros; then negative tab order descending.
|
||||
stable_sort(tabOrder.begin(), tabOrder.end(), [](fld_t a, fld_t b) -> bool {
|
||||
bool a_neg = a.second->tabOrder < 0, b_neg = b.second->tabOrder < 0;
|
||||
if(a_neg && !b_neg) return false;
|
||||
else if(!a_neg && b_neg) return true;
|
||||
bool a_pos = a.second->tabOrder > 0, b_pos = b.second->tabOrder > 0;
|
||||
if(a_pos && !b_pos) return true;
|
||||
else if(!a_pos && b_pos) return false;
|
||||
return a.second->tabOrder < b.second->tabOrder;
|
||||
});
|
||||
} catch(Exception& x){ // XML processing exception
|
||||
printf("%s",x.what());
|
||||
exit(1);
|
||||
@@ -865,12 +909,8 @@ void cDialog::run(){
|
||||
std::string itemHit = "";
|
||||
dialogNotToast = true;
|
||||
// Focus the first text field, if there is one
|
||||
for(auto ctrl : controls) {
|
||||
if(ctrl.second->getType() == CTRL_FIELD) {
|
||||
ctrl.second->triggerFocusHandler(*this, ctrl.first, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!tabOrder.empty())
|
||||
tabOrder[0].second->triggerFocusHandler(*this, tabOrder[0].first, false);
|
||||
win.create(sf::VideoMode(winRect.width(), winRect.height()), "Dialog", sf::Style::Titlebar);
|
||||
win.setActive();
|
||||
win.setVisible(true);
|
||||
@@ -994,19 +1034,25 @@ void cDialog::run(){
|
||||
if(controls[itemHit]->getType() == CTRL_FIELD){
|
||||
if(key.spec && key.k == key_tab){
|
||||
// TODO: Tabbing through fields, and trigger focus events.
|
||||
ctrlIter cur = controls.find(itemHit), iter;
|
||||
auto cur = find_if(tabOrder.begin(), tabOrder.end(), [&itemHit](pair<string,cTextField*>& a) {
|
||||
return a.first == itemHit;
|
||||
});
|
||||
if(cur == tabOrder.end()) break; // Unlikely, but let's be safe
|
||||
if(!cur->second->triggerFocusHandler(*this,itemHit,true)) break;
|
||||
cTextField* wasFocus = currentFocus;
|
||||
iter = std::next(cur);
|
||||
auto iter = std::next(cur);
|
||||
if(iter == tabOrder.end()) iter = tabOrder.begin();
|
||||
while(iter != cur){
|
||||
// If tab order is explicitly specified for all fields, gaps are possible
|
||||
if(iter->second == nullptr) continue;
|
||||
if((currentFocus = dynamic_cast<cTextField*>(iter->second))){
|
||||
if(currentFocus->triggerFocusHandler(*this,iter->first,false)){
|
||||
itemHit = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(iter == controls.end()) iter = controls.begin();
|
||||
else iter++;
|
||||
iter++;
|
||||
if(iter == tabOrder.end()) iter = tabOrder.begin();
|
||||
}
|
||||
if(iter == cur) // no focus change occured!
|
||||
currentFocus = wasFocus; // TODO: Surely something should happen here?
|
||||
|
Reference in New Issue
Block a user