Rearrange the dialog event loop a little
This patch is contributed by @x-qq and should improve CPU usage on Linux.
This commit is contained in:
@@ -387,13 +387,9 @@ void cDialog::run(std::function<void(cDialog&)> onopen){
|
||||
// TODO: The introduction of the static topWindow means I may be able to use this instead of parent->win; do I still need parent?
|
||||
sf::RenderWindow* parentWin = &(parent ? parent->win : mainPtr);
|
||||
cursor_type former_curs = Cursor::current;
|
||||
set_cursor(sword_curs);
|
||||
using kb = sf::Keyboard;
|
||||
kb::Key k;
|
||||
cKey key, pendingKey = {true};
|
||||
sf::Event currentEvent;
|
||||
std::string itemHit = "";
|
||||
dialogNotToast = true;
|
||||
set_cursor(sword_curs);
|
||||
sf::Event currentEvent;
|
||||
// Focus the first text field, if there is one
|
||||
if(!tabOrder.empty()) {
|
||||
auto iter = std::find_if(tabOrder.begin(), tabOrder.end(), [](std::pair<std::string,cTextField*> ctrl){
|
||||
@@ -421,148 +417,9 @@ void cDialog::run(std::function<void(cDialog&)> onopen){
|
||||
ModalSession dlog(win, *parentWin);
|
||||
if(onopen) onopen(*this);
|
||||
animTimer.restart();
|
||||
while(dialogNotToast){
|
||||
draw();
|
||||
if(!win.pollEvent(currentEvent)) continue;
|
||||
location where;
|
||||
switch(currentEvent.type){
|
||||
case sf::Event::KeyPressed:
|
||||
k = currentEvent.key.code;
|
||||
switch(k){
|
||||
case kb::Up:
|
||||
key.spec = true;
|
||||
key.k = key_up;
|
||||
break;
|
||||
case kb::Right:
|
||||
key.spec = true;
|
||||
key.k = key_right;
|
||||
break;
|
||||
case kb::Left:
|
||||
key.spec = true;
|
||||
key.k = key_left;
|
||||
break;
|
||||
case kb::Down:
|
||||
key.spec = true;
|
||||
key.k = key_down;
|
||||
break;
|
||||
case kb::Escape:
|
||||
key.spec = true;
|
||||
key.k = key_esc;
|
||||
break;
|
||||
case kb::Return: // TODO: Also enter (keypad)
|
||||
key.spec = true;
|
||||
key.k = key_enter;
|
||||
break;
|
||||
case kb::BackSpace:
|
||||
key.spec = true;
|
||||
key.k = key_bsp;
|
||||
break;
|
||||
case kb::Delete:
|
||||
key.spec = true;
|
||||
key.k = key_del;
|
||||
break;
|
||||
case kb::Tab:
|
||||
key.spec = true;
|
||||
key.k = key_tab;
|
||||
break;
|
||||
case kb::Insert:
|
||||
key.spec = true;
|
||||
key.k = key_insert;
|
||||
break;
|
||||
case kb::F1:
|
||||
key.spec = true;
|
||||
key.k = key_help;
|
||||
break;
|
||||
case kb::Home:
|
||||
key.spec = true;
|
||||
key.k = key_home;
|
||||
break;
|
||||
case kb::End:
|
||||
key.spec = true;
|
||||
key.k = key_end;
|
||||
break;
|
||||
case kb::PageUp:
|
||||
key.spec = true;
|
||||
key.k = key_pgup;
|
||||
break;
|
||||
case kb::PageDown:
|
||||
key.spec = true;
|
||||
key.k = key_pgdn;
|
||||
break;
|
||||
case kb::LShift:
|
||||
case kb::RShift:
|
||||
case kb::LAlt:
|
||||
case kb::RAlt:
|
||||
case kb::LControl:
|
||||
case kb::RControl:
|
||||
case kb::LSystem:
|
||||
case kb::RSystem:
|
||||
continue;
|
||||
default:
|
||||
key.spec = false;
|
||||
key.c = keyToChar(k, false);
|
||||
break;
|
||||
}
|
||||
key.mod = mod_none;
|
||||
if(currentEvent.key.*systemKey)
|
||||
key.mod += mod_ctrl;
|
||||
if(currentEvent.key.shift) key.mod += mod_shift;
|
||||
if(currentEvent.key.alt) key.mod += mod_alt;
|
||||
process_keystroke(key);
|
||||
// Now check for focused fields.
|
||||
if(currentFocus.empty()) break;
|
||||
// If it's a tab, handle tab order
|
||||
if(key.spec && key.k == key_tab){
|
||||
// Could use key.mod, but this is slightly easier.
|
||||
if(currentEvent.key.shift)
|
||||
handleTabOrder(currentFocus, tabOrder.rbegin(), tabOrder.rend());
|
||||
else handleTabOrder(currentFocus, tabOrder.begin(), tabOrder.end());
|
||||
} else {
|
||||
// If it's a character key, and the system key (control/command) is not pressed,
|
||||
// we have an upcoming TextEntered event which contains more information.
|
||||
// Otherwise, handle it right away. But never handle enter or escape.
|
||||
if((key.spec && key.k != key_enter && key.k != key_esc) || mod_contains(key.mod, mod_ctrl))
|
||||
dynamic_cast<cTextField*>(controls[currentFocus])->handleInput(key);
|
||||
pendingKey = key;
|
||||
if(key.k != key_enter && key.k != key_esc) itemHit = "";
|
||||
}
|
||||
break;
|
||||
case sf::Event::TextEntered:
|
||||
if(!pendingKey.spec && !currentFocus.empty()) {
|
||||
pendingKey.c = currentEvent.text.unicode;
|
||||
if(pendingKey.c != '\t')
|
||||
dynamic_cast<cTextField*>(controls[currentFocus])->handleInput(pendingKey);
|
||||
}
|
||||
break;
|
||||
case sf::Event::MouseButtonPressed:
|
||||
key.mod = mod_none;
|
||||
if(kb::isKeyPressed(kb::LControl)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::RControl)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::LSystem)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::RSystem)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::LAlt)) key.mod += mod_alt;
|
||||
if(kb::isKeyPressed(kb::RAlt)) key.mod += mod_alt;
|
||||
if(kb::isKeyPressed(kb::LShift)) key.mod += mod_shift;
|
||||
if(kb::isKeyPressed(kb::RShift)) key.mod += mod_shift;
|
||||
where = {currentEvent.mouseButton.x, currentEvent.mouseButton.y};
|
||||
process_click(where, key.mod);
|
||||
break;
|
||||
default: // To silence warning of unhandled enum values
|
||||
break;
|
||||
case sf::Event::GainedFocus:
|
||||
case sf::Event::MouseMoved:
|
||||
bool inField = false;
|
||||
for(auto& ctrl : controls) {
|
||||
if(ctrl.second->getType() == CTRL_FIELD && ctrl.second->getBounds().contains(currentEvent.mouseMove.x, currentEvent.mouseMove.y)) {
|
||||
set_cursor(text_curs);
|
||||
inField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!inField) set_cursor(sword_curs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle_events();
|
||||
|
||||
win.setVisible(false);
|
||||
while(parentWin->pollEvent(currentEvent));
|
||||
set_cursor(former_curs);
|
||||
@@ -570,6 +427,176 @@ void cDialog::run(std::function<void(cDialog&)> onopen){
|
||||
makeFrontWindow(*parentWin);
|
||||
}
|
||||
|
||||
// This method is a main event event loop of the dialog.
|
||||
void cDialog::handle_events() {
|
||||
sf::Event currentEvent;
|
||||
|
||||
sf::Clock framerate_clock;
|
||||
const sf::Int64 desired_microseconds_per_frame { 1000000 / 60 }; // us / FPS
|
||||
|
||||
while(dialogNotToast) {
|
||||
while(win.pollEvent(currentEvent)) handle_one_event(currentEvent);
|
||||
|
||||
// Ideally, this should be the only draw call that is done in a cycle.
|
||||
draw();
|
||||
|
||||
// Prevent the loop from executing too fast.
|
||||
const sf::Int64 remaining_time_budget = desired_microseconds_per_frame - framerate_clock.getElapsedTime().asMicroseconds();
|
||||
if(remaining_time_budget > 0) sf::sleep(sf::microseconds(remaining_time_budget));
|
||||
framerate_clock.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// This method handles one event received by the dialog.
|
||||
void cDialog::handle_one_event(const sf::Event& currentEvent) {
|
||||
using kb = sf::Keyboard;
|
||||
|
||||
cKey key;
|
||||
// HACK: This needs to be stored between consecutive invocations of this function
|
||||
static cKey pendingKey = {true};
|
||||
std::string itemHit = "";
|
||||
location where;
|
||||
|
||||
switch(currentEvent.type) {
|
||||
case sf::Event::KeyPressed:
|
||||
switch(currentEvent.key.code){
|
||||
case kb::Up:
|
||||
key.spec = true;
|
||||
key.k = key_up;
|
||||
break;
|
||||
case kb::Right:
|
||||
key.spec = true;
|
||||
key.k = key_right;
|
||||
break;
|
||||
case kb::Left:
|
||||
key.spec = true;
|
||||
key.k = key_left;
|
||||
break;
|
||||
case kb::Down:
|
||||
key.spec = true;
|
||||
key.k = key_down;
|
||||
break;
|
||||
case kb::Escape:
|
||||
key.spec = true;
|
||||
key.k = key_esc;
|
||||
break;
|
||||
case kb::Return: // TODO: Also enter (keypad)
|
||||
key.spec = true;
|
||||
key.k = key_enter;
|
||||
break;
|
||||
case kb::BackSpace:
|
||||
key.spec = true;
|
||||
key.k = key_bsp;
|
||||
break;
|
||||
case kb::Delete:
|
||||
key.spec = true;
|
||||
key.k = key_del;
|
||||
break;
|
||||
case kb::Tab:
|
||||
key.spec = true;
|
||||
key.k = key_tab;
|
||||
break;
|
||||
case kb::Insert:
|
||||
key.spec = true;
|
||||
key.k = key_insert;
|
||||
break;
|
||||
case kb::F1:
|
||||
key.spec = true;
|
||||
key.k = key_help;
|
||||
break;
|
||||
case kb::Home:
|
||||
key.spec = true;
|
||||
key.k = key_home;
|
||||
break;
|
||||
case kb::End:
|
||||
key.spec = true;
|
||||
key.k = key_end;
|
||||
break;
|
||||
case kb::PageUp:
|
||||
key.spec = true;
|
||||
key.k = key_pgup;
|
||||
break;
|
||||
case kb::PageDown:
|
||||
key.spec = true;
|
||||
key.k = key_pgdn;
|
||||
break;
|
||||
case kb::LShift:
|
||||
case kb::RShift:
|
||||
case kb::LAlt:
|
||||
case kb::RAlt:
|
||||
case kb::LControl:
|
||||
case kb::RControl:
|
||||
case kb::LSystem:
|
||||
case kb::RSystem:
|
||||
return;
|
||||
default:
|
||||
key.spec = false;
|
||||
key.c = keyToChar(currentEvent.key.code, false);
|
||||
break;
|
||||
}
|
||||
key.mod = mod_none;
|
||||
if(currentEvent.key.*systemKey)
|
||||
key.mod += mod_ctrl;
|
||||
if(currentEvent.key.shift) key.mod += mod_shift;
|
||||
if(currentEvent.key.alt) key.mod += mod_alt;
|
||||
process_keystroke(key);
|
||||
// Now check for focused fields.
|
||||
if(currentFocus.empty()) break;
|
||||
// If it's a tab, handle tab order
|
||||
if(key.spec && key.k == key_tab) {
|
||||
// Could use key.mod, but this is slightly easier.
|
||||
if(currentEvent.key.shift) {
|
||||
handleTabOrder(currentFocus, tabOrder.rbegin(), tabOrder.rend());
|
||||
} else {
|
||||
handleTabOrder(currentFocus, tabOrder.begin(), tabOrder.end());
|
||||
}
|
||||
} else {
|
||||
// If it's a character key, and the system key (control/command) is not pressed,
|
||||
// we have an upcoming TextEntered event which contains more information.
|
||||
// Otherwise, handle it right away. But never handle enter or escape.
|
||||
if((key.spec && key.k != key_enter && key.k != key_esc) || mod_contains(key.mod, mod_ctrl))
|
||||
dynamic_cast<cTextField*>(controls[currentFocus])->handleInput(key);
|
||||
pendingKey = key;
|
||||
if(key.k != key_enter && key.k != key_esc) itemHit = "";
|
||||
}
|
||||
break;
|
||||
case sf::Event::TextEntered:
|
||||
if(!pendingKey.spec && !currentFocus.empty()) {
|
||||
pendingKey.c = currentEvent.text.unicode;
|
||||
if(pendingKey.c != '\t')
|
||||
dynamic_cast<cTextField*>(controls[currentFocus])->handleInput(pendingKey);
|
||||
}
|
||||
break;
|
||||
case sf::Event::MouseButtonPressed:
|
||||
key.mod = mod_none;
|
||||
if(kb::isKeyPressed(kb::LControl)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::RControl)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::LSystem)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::RSystem)) key.mod += mod_ctrl;
|
||||
if(kb::isKeyPressed(kb::LAlt)) key.mod += mod_alt;
|
||||
if(kb::isKeyPressed(kb::RAlt)) key.mod += mod_alt;
|
||||
if(kb::isKeyPressed(kb::LShift)) key.mod += mod_shift;
|
||||
if(kb::isKeyPressed(kb::RShift)) key.mod += mod_shift;
|
||||
where = {currentEvent.mouseButton.x, currentEvent.mouseButton.y};
|
||||
process_click(where, key.mod);
|
||||
break;
|
||||
default: // To silence warning of unhandled enum values
|
||||
break;
|
||||
case sf::Event::GainedFocus:
|
||||
case sf::Event::MouseMoved:
|
||||
bool inField = false;
|
||||
for(auto& ctrl : controls) {
|
||||
if(ctrl.second->getType() == CTRL_FIELD && ctrl.second->getBounds().contains(currentEvent.mouseMove.x, currentEvent.mouseMove.y)) {
|
||||
set_cursor(text_curs);
|
||||
inField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!inField) set_cursor(sword_curs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Iter> void cDialog::handleTabOrder(string& itemHit, Iter begin, Iter end) {
|
||||
auto cur = find_if(begin, end, [&itemHit](pair<string,cTextField*>& a) {
|
||||
return a.first == itemHit;
|
||||
|
||||
@@ -214,6 +214,8 @@ public:
|
||||
cDialog(cDialog& other) = delete;
|
||||
private:
|
||||
void draw();
|
||||
void handle_events();
|
||||
void handle_one_event(const sf::Event&);
|
||||
void process_keystroke(cKey keyHit);
|
||||
void process_click(location where, eKeyMod mods);
|
||||
bool dialogNotToast, didAccept;
|
||||
|
||||
Reference in New Issue
Block a user