mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-13 21:31:53 +00:00
Keymapper WIP:
* Further improvement of automatic mapping * Remap dialog - added timeout feature svn-id: r33827
This commit is contained in:
parent
532faef82b
commit
17c1dba992
@ -50,7 +50,7 @@ void Action::mapKey(const HardwareKey *key) {
|
||||
assert(_parent);
|
||||
if (_hwKey) _parent->unregisterMapping(this);
|
||||
_hwKey = key;
|
||||
if (_hwKey) _parent->registerMapping(this, key);
|
||||
if (_hwKey) _parent->registerMapping(this, _hwKey);
|
||||
}
|
||||
|
||||
const HardwareKey *Action::getMappedKey() const {
|
||||
|
@ -92,64 +92,107 @@ void KeymapManager::initKeymap(ConfigManager::Domain *domain,
|
||||
automaticMap(map);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - current weaknesses:
|
||||
// - if an action finds a key with required type / category but a parent
|
||||
// action with higher priority is using it, that key is never used
|
||||
void KeymapManager::automaticMap(Keymap *map) {
|
||||
List<Action*> actions(map->getActions()), unmapped;
|
||||
List<Action*>::iterator actIt;
|
||||
List<const HardwareKey*> keys = _hardwareKeys->getHardwareKeys();
|
||||
List<const HardwareKey*>::iterator keyIt, selectedKey;
|
||||
// Create local copies of action and key lists.
|
||||
List<Action*> actions(map->getActions());
|
||||
List<const HardwareKey*> keys(_hardwareKeys->getHardwareKeys());
|
||||
|
||||
// sort by priority
|
||||
List<Action*>::iterator actIt;
|
||||
List<const HardwareKey*>::iterator keyIt, selectedKey;
|
||||
|
||||
// Remove actions and keys from local lists that have already been mapped.
|
||||
actIt = actions.begin();
|
||||
while (actIt != actions.end()) {
|
||||
Action *act = *actIt;
|
||||
const HardwareKey *key = act->getMappedKey();
|
||||
if (key) {
|
||||
keys.remove(key);
|
||||
actIt = actions.erase(actIt);
|
||||
} else {
|
||||
++actIt;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort remaining actions by priority.
|
||||
ActionPriorityComp priorityComp;
|
||||
sort(actions.begin(), actions.end(), priorityComp);
|
||||
|
||||
for (actIt = actions.begin(); actIt != actions.end(); actIt++) {
|
||||
// First mapping pass:
|
||||
// - Match if a key's preferred type or category is same as the action's.
|
||||
// - Priority is given to:
|
||||
// - keys that match type over category.
|
||||
// - keys that have not been used by parent maps.
|
||||
// - If a key has been used by a parent map the new action must have a
|
||||
// higher priority than the parent action.
|
||||
// - As soon as the number of skipped actions equals the number of keys
|
||||
// remaining we stop matching. This means that the second pass will assign keys
|
||||
// to these higher priority skipped actions.
|
||||
uint skipped = 0;
|
||||
actIt = actions.begin();
|
||||
while (actIt != actions.end() && skipped < keys.size()) {
|
||||
selectedKey = keys.end();
|
||||
int keyScore = 0;
|
||||
int matchRank = 0;
|
||||
Action *act = *actIt;
|
||||
for (keyIt = keys.begin(); keyIt != keys.end(); keyIt++) {
|
||||
for (keyIt = keys.begin(); keyIt != keys.end(); ++keyIt) {
|
||||
if ((*keyIt)->preferredType == act->type) {
|
||||
Action *parentAct = getParentMappedAction(map, (*keyIt)->key);
|
||||
if (!parentAct) {
|
||||
selectedKey = keyIt;
|
||||
break;
|
||||
} else if (parentAct->priority <= act->priority && keyScore < 3) {
|
||||
} else if (parentAct->priority <= act->priority && matchRank < 3) {
|
||||
selectedKey = keyIt;
|
||||
keyScore = 3;
|
||||
matchRank = 3;
|
||||
}
|
||||
} else if ((*keyIt)->preferredCategory == act->category && keyScore < 2) {
|
||||
} else if ((*keyIt)->preferredCategory == act->category && matchRank < 2) {
|
||||
Action *parentAct = getParentMappedAction(map, (*keyIt)->key);
|
||||
if (!parentAct) {
|
||||
selectedKey = keyIt;
|
||||
keyScore = 2;
|
||||
} else if (parentAct->priority <= act->priority && keyScore < 1) {
|
||||
matchRank = 2;
|
||||
} else if (parentAct->priority <= act->priority && matchRank < 1) {
|
||||
selectedKey = keyIt;
|
||||
keyScore = 1;
|
||||
matchRank = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedKey != keys.end()) {
|
||||
// Map action and delete action & key from local lists.
|
||||
act->mapKey(*selectedKey);
|
||||
keys.erase(selectedKey);
|
||||
} else
|
||||
unmapped.push_back(act);
|
||||
actIt = actions.erase(actIt);
|
||||
} else {
|
||||
// Skip action (will be mapped in next pass).
|
||||
++actIt;
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
|
||||
for (actIt = unmapped.begin(); actIt != unmapped.end(); actIt++) {
|
||||
// Second mapping pass:
|
||||
// - Maps any remaining actions to keys
|
||||
// - priority given to:
|
||||
// - keys that have no parent action
|
||||
// - keys whose parent action has lower priority than the new action
|
||||
// - keys whose parent action has the lowest priority
|
||||
// - is guarantees to match a key if one is available
|
||||
for (actIt = actions.begin(); actIt != actions.end(); ++actIt) {
|
||||
selectedKey = keys.end();
|
||||
int keyScore = 0;
|
||||
int lowestPriority = 0; //dummy value
|
||||
int matchRank = 0;
|
||||
int lowestPriority = 0;
|
||||
Action *act = *actIt;
|
||||
for (keyIt = keys.begin(); keyIt != keys.end(); keyIt++) {
|
||||
for (keyIt = keys.begin(); keyIt != keys.end(); ++keyIt) {
|
||||
Action *parentAct = getParentMappedAction(map, (*keyIt)->key);
|
||||
if (!parentAct) {
|
||||
selectedKey = keyIt;
|
||||
break;
|
||||
} else if (keyScore < 2) {
|
||||
} else if (matchRank < 2) {
|
||||
if (parentAct->priority <= act->priority) {
|
||||
keyScore = 2;
|
||||
matchRank = 2;
|
||||
selectedKey = keyIt;
|
||||
} else if (parentAct->priority < lowestPriority || keyScore == 0) {
|
||||
keyScore = 1;
|
||||
} else if (parentAct->priority < lowestPriority || matchRank == 0) {
|
||||
matchRank = 1;
|
||||
lowestPriority = parentAct->priority;
|
||||
selectedKey = keyIt;
|
||||
}
|
||||
@ -158,8 +201,9 @@ void KeymapManager::automaticMap(Keymap *map) {
|
||||
if (selectedKey != keys.end()) {
|
||||
act->mapKey(*selectedKey);
|
||||
keys.erase(selectedKey);
|
||||
} else // no keys left
|
||||
} else {// no match = no keys left
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ enum {
|
||||
};
|
||||
|
||||
RemapDialog::RemapDialog()
|
||||
: Dialog("remap"), _activeRemapAction(0), _topAction(0) {
|
||||
: Dialog("remap"), _activeRemapAction(0), _topAction(0), _remapTimeout(0) {
|
||||
|
||||
const int screenW = g_system->getOverlayWidth();
|
||||
const int screenH = g_system->getOverlayHeight();
|
||||
@ -112,15 +112,16 @@ void RemapDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 d
|
||||
}
|
||||
|
||||
void RemapDialog::startRemapping(uint i) {
|
||||
|
||||
_remapTimeout = getMillis() + kRemapTimeoutDelay;
|
||||
_activeRemapAction = _currentActions[_topAction + i].action;
|
||||
_keymapWidgets[i].keyButton->setLabel("...");
|
||||
_keymapWidgets[i].keyButton->draw();
|
||||
|
||||
_keymapper->setEnabled(false);
|
||||
|
||||
}
|
||||
|
||||
void RemapDialog::stopRemapping() {
|
||||
_topAction = -1;
|
||||
refreshKeymap();
|
||||
_activeRemapAction = 0;
|
||||
_keymapper->setEnabled(true);
|
||||
@ -136,7 +137,19 @@ void RemapDialog::handleKeyUp(Common::KeyState state) {
|
||||
} else {
|
||||
GUI::Dialog::handleKeyDown(state);
|
||||
}
|
||||
}
|
||||
|
||||
void RemapDialog::handleMouseDown(int x, int y, int button, int clickCount) {
|
||||
if (_activeRemapAction)
|
||||
stopRemapping();
|
||||
else
|
||||
Dialog::handleMouseDown(x, y, button, clickCount);
|
||||
}
|
||||
|
||||
void RemapDialog::handleTickle() {
|
||||
if (_activeRemapAction && getMillis() > _remapTimeout)
|
||||
stopRemapping();
|
||||
Dialog::handleTickle();
|
||||
}
|
||||
|
||||
void RemapDialog::loadKeymap() {
|
||||
|
@ -42,6 +42,8 @@ public:
|
||||
virtual void open();
|
||||
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
|
||||
virtual void handleKeyUp(Common::KeyState state);
|
||||
virtual void handleMouseDown(int x, int y, int button, int clickCount);
|
||||
virtual void handleTickle();
|
||||
|
||||
protected:
|
||||
struct ActionWidgets {
|
||||
@ -77,6 +79,8 @@ protected:
|
||||
|
||||
Array<ActionWidgets> _keymapWidgets;
|
||||
Action *_activeRemapAction;
|
||||
uint32 _remapTimeout;
|
||||
static const uint32 kRemapTimeoutDelay = 3000;
|
||||
|
||||
};
|
||||
|
||||
|
@ -374,11 +374,11 @@ const char *Theme::_defaultConfigINI =
|
||||
"scummsaveload_extinfo.visible=true\n"
|
||||
"\n"
|
||||
"# Keymapper remap dialog\n"
|
||||
"remap=8 8 (w - 16) (h / 5)\n"
|
||||
"remap=(w / 4) (h / 4) (w / 2) (h / 2)\n"
|
||||
"remap_spacing=10\n"
|
||||
"remap_popup=remap_spacing remap_spacing (parent.w - remap_spacing * 2) (kLineHeight + 2)\n"
|
||||
"remap_popup=remap_spacing remap_spacing (prev.w - remap_spacing * 2) (kLineHeight + 2)\n"
|
||||
"remap_popup_labelW=buttonWidth\n"
|
||||
"remap_col_count=3\n"
|
||||
"remap_col_count=2\n"
|
||||
"remap_keymap_area=remap_spacing (remap_popup.y + remap_popup.h + remap_spacing) (remap.w - remap_spacing * 2) (remap.h - self.y - remap_spacing)\n"
|
||||
"############################################\n"
|
||||
"[chooser]\n"
|
||||
|
Loading…
Reference in New Issue
Block a user