mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Cleanups around focus moves
This commit is contained in:
parent
77dff18701
commit
cbd012f522
3 changed files with 63 additions and 45 deletions
|
@ -134,20 +134,23 @@ void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root, bool ignoreInsets
|
|||
}
|
||||
|
||||
void MoveFocus(ViewGroup *root, FocusDirection direction) {
|
||||
if (!GetFocusedView()) {
|
||||
View *focusedView = GetFocusedView();
|
||||
if (!focusedView) {
|
||||
// Nothing was focused when we got in here. Focus the first non-group in the hierarchy.
|
||||
root->SetFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
NeighborResult neigh(0, 0);
|
||||
neigh = root->FindNeighbor(GetFocusedView(), direction, neigh);
|
||||
NeighborResult neigh;
|
||||
neigh = root->FindNeighbor(focusedView, direction, neigh);
|
||||
|
||||
if (neigh.view) {
|
||||
neigh.view->SetFocus();
|
||||
root->SubviewFocused(neigh.view);
|
||||
|
||||
PlayUISound(UISound::SELECT);
|
||||
} else {
|
||||
INFO_LOG(SCECTRL, "No neighbor view");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +207,7 @@ bool IsScrollKey(const KeyInput &input) {
|
|||
}
|
||||
}
|
||||
|
||||
KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
|
||||
static KeyEventResult KeyEventToFocusMoves(const KeyInput &key) {
|
||||
KeyEventResult retval = KeyEventResult::PASS_THROUGH;
|
||||
// Ignore repeats for focus moves.
|
||||
if ((key.flags & (KEY_DOWN | KEY_IS_REPEAT)) == KEY_DOWN) {
|
||||
|
@ -215,6 +218,7 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
|
|||
hk.deviceId = key.deviceId;
|
||||
hk.triggerTime = time_now_d() + repeatDelay;
|
||||
|
||||
std::lock_guard<std::mutex> lock(focusLock);
|
||||
// Check if the key is already held. If it is, ignore it. This is to avoid
|
||||
// multiple key repeat mechanisms colliding.
|
||||
if (heldKeys.find(hk) != heldKeys.end()) {
|
||||
|
@ -222,7 +226,7 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
|
|||
}
|
||||
|
||||
heldKeys.insert(hk);
|
||||
std::lock_guard<std::mutex> lock(focusLock);
|
||||
INFO_LOG(SCECTRL, "focus move: %d", key.keyCode);
|
||||
focusMoves.push_back(key.keyCode);
|
||||
retval = KeyEventResult::ACCEPT;
|
||||
}
|
||||
|
@ -240,6 +244,11 @@ KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
KeyEventResult UnsyncKeyEvent(const KeyInput &key, ViewGroup *root) {
|
||||
KeyEventResult retval = KeyEventToFocusMoves(key);
|
||||
|
||||
// Ignore volume keys and stuff here. Not elegant but need to propagate bools through the view hierarchy as well...
|
||||
switch (key.keyCode) {
|
||||
|
@ -259,32 +268,6 @@ void KeyEvent(const KeyInput &key, ViewGroup *root) {
|
|||
root->Key(key);
|
||||
}
|
||||
|
||||
static void ProcessHeldKeys(ViewGroup *root) {
|
||||
double now = time_now_d();
|
||||
|
||||
restart:
|
||||
|
||||
for (std::set<HeldKey>::iterator iter = heldKeys.begin(); iter != heldKeys.end(); ++iter) {
|
||||
if (iter->triggerTime < now) {
|
||||
KeyInput key;
|
||||
key.keyCode = iter->key;
|
||||
key.deviceId = iter->deviceId;
|
||||
key.flags = KEY_DOWN;
|
||||
KeyEvent(key, root);
|
||||
|
||||
std::lock_guard<std::mutex> lock(focusLock);
|
||||
focusMoves.push_back(key.keyCode);
|
||||
|
||||
// Cannot modify the current item when looping over a set, so let's do this instead.
|
||||
HeldKey hk = *iter;
|
||||
heldKeys.erase(hk);
|
||||
hk.triggerTime = now + repeatInterval;
|
||||
heldKeys.insert(hk);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TouchEvent(const TouchInput &touch, ViewGroup *root) {
|
||||
focusForced = false;
|
||||
root->Touch(touch);
|
||||
|
@ -293,6 +276,11 @@ void TouchEvent(const TouchInput &touch, ViewGroup *root) {
|
|||
}
|
||||
}
|
||||
|
||||
static void FakeKeyEvent(const KeyInput &key, ViewGroup *root) {
|
||||
KeyEventToFocusMoves(key);
|
||||
KeyEvent(key, root);
|
||||
}
|
||||
|
||||
void AxisEvent(const AxisInput &axis, ViewGroup *root) {
|
||||
enum class DirState {
|
||||
NONE = 0,
|
||||
|
@ -300,9 +288,7 @@ void AxisEvent(const AxisInput &axis, ViewGroup *root) {
|
|||
NEG = 2,
|
||||
};
|
||||
struct PrevState {
|
||||
PrevState() : x(DirState::NONE), y(DirState::NONE) {
|
||||
}
|
||||
|
||||
PrevState() : x(DirState::NONE), y(DirState::NONE) {}
|
||||
DirState x;
|
||||
DirState y;
|
||||
};
|
||||
|
@ -321,18 +307,18 @@ void AxisEvent(const AxisInput &axis, ViewGroup *root) {
|
|||
|
||||
// Cannot use the remapper since this is for the menu, so we provide our own
|
||||
// axis->button emulation here.
|
||||
auto GenerateKeyFromAxis = [&](DirState old, DirState cur, InputKeyCode neg_key, InputKeyCode pos_key) {
|
||||
auto GenerateKeyFromAxis = [=](DirState old, DirState cur, InputKeyCode neg_key, InputKeyCode pos_key) {
|
||||
if (old == cur)
|
||||
return;
|
||||
if (old == DirState::POS) {
|
||||
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_UP }, root);
|
||||
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_UP }, root);
|
||||
} else if (old == DirState::NEG) {
|
||||
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_UP }, root);
|
||||
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_UP }, root);
|
||||
}
|
||||
if (cur == DirState::POS) {
|
||||
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_DOWN }, root);
|
||||
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, pos_key, KEY_DOWN }, root);
|
||||
} else if (cur == DirState::NEG) {
|
||||
KeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_DOWN }, root);
|
||||
FakeKeyEvent(KeyInput{ DEVICE_ID_KEYBOARD, neg_key, KEY_DOWN }, root);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -384,6 +370,31 @@ void AxisEvent(const AxisInput &axis, ViewGroup *root) {
|
|||
root->Axis(axis);
|
||||
}
|
||||
|
||||
static void ProcessHeldKeys(ViewGroup *root) {
|
||||
double now = time_now_d();
|
||||
|
||||
restart:
|
||||
for (std::set<HeldKey>::iterator iter = heldKeys.begin(); iter != heldKeys.end(); ++iter) {
|
||||
if (iter->triggerTime < now) {
|
||||
KeyInput key;
|
||||
key.keyCode = iter->key;
|
||||
key.deviceId = iter->deviceId;
|
||||
key.flags = KEY_DOWN;
|
||||
KeyEvent(key, root);
|
||||
|
||||
std::lock_guard<std::mutex> lock(focusLock);
|
||||
focusMoves.push_back(key.keyCode);
|
||||
|
||||
// Cannot modify the current item when looping over a set, so let's do this instead.
|
||||
HeldKey hk = *iter;
|
||||
heldKeys.erase(hk);
|
||||
hk.triggerTime = now + repeatInterval;
|
||||
heldKeys.insert(hk);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateViewHierarchy(ViewGroup *root) {
|
||||
ProcessHeldKeys(root);
|
||||
frameCount++;
|
||||
|
@ -397,6 +408,7 @@ void UpdateViewHierarchy(ViewGroup *root) {
|
|||
std::lock_guard<std::mutex> lock(focusLock);
|
||||
EnableFocusMovement(true);
|
||||
if (!GetFocusedView()) {
|
||||
// Find a view to focus.
|
||||
View *defaultView = root->GetDefaultFocusView();
|
||||
// Can't focus what you can't see.
|
||||
if (defaultView && defaultView->GetVisibility() == V_VISIBLE) {
|
||||
|
@ -406,6 +418,7 @@ void UpdateViewHierarchy(ViewGroup *root) {
|
|||
}
|
||||
root->SubviewFocused(GetFocusedView());
|
||||
} else {
|
||||
INFO_LOG(SCECTRL, "Processing focus moves.");
|
||||
for (size_t i = 0; i < focusMoves.size(); i++) {
|
||||
switch (focusMoves[i]) {
|
||||
case NKCODE_DPAD_LEFT: MoveFocus(root, FOCUS_LEFT); break;
|
||||
|
@ -419,6 +432,7 @@ void UpdateViewHierarchy(ViewGroup *root) {
|
|||
}
|
||||
}
|
||||
}
|
||||
INFO_LOG(SCECTRL, "Clearing focus moves.");
|
||||
focusMoves.clear();
|
||||
}
|
||||
|
||||
|
@ -426,4 +440,4 @@ void UpdateViewHierarchy(ViewGroup *root) {
|
|||
DispatchEvents();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace UI
|
||||
|
|
|
@ -401,10 +401,13 @@ static float GetDirectionScore(int originIndex, const View *origin, View *destin
|
|||
}
|
||||
|
||||
NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, NeighborResult result) {
|
||||
if (!IsEnabled())
|
||||
if (!IsEnabled()) {
|
||||
INFO_LOG(SCECTRL, "Not enabled");
|
||||
return result;
|
||||
if (GetVisibility() != V_VISIBLE)
|
||||
}
|
||||
if (GetVisibility() != V_VISIBLE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// First, find the position of the view in the list.
|
||||
int num = -1;
|
||||
|
@ -420,7 +423,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
|
|||
case FOCUS_PREV:
|
||||
// If view not found, no neighbor to find.
|
||||
if (num == -1)
|
||||
return NeighborResult(0, 0.0f);
|
||||
return NeighborResult(nullptr, 0.0f);
|
||||
return NeighborResult(views_[(num + views_.size() - 1) % views_.size()], 0.0f);
|
||||
|
||||
case FOCUS_NEXT:
|
||||
|
@ -429,6 +432,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
|
|||
return NeighborResult(0, 0.0f);
|
||||
return NeighborResult(views_[(num + 1) % views_.size()], 0.0f);
|
||||
default:
|
||||
// Can't happen
|
||||
return NeighborResult(nullptr, 0.0f);
|
||||
}
|
||||
}
|
||||
|
@ -474,6 +478,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
|
|||
return FindScrollNeighbor(view, Point(INFINITY, INFINITY), direction, result);
|
||||
|
||||
default:
|
||||
ERROR_LOG(SYSTEM, "Bad focus direction %d", (int)direction);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,8 @@ class AnchorTranslateTween;
|
|||
class ScrollView;
|
||||
|
||||
struct NeighborResult {
|
||||
NeighborResult() : view(0), score(0) {}
|
||||
NeighborResult() : view(nullptr), score(0) {}
|
||||
NeighborResult(View *v, float s) : view(v), score(s) {}
|
||||
|
||||
View *view;
|
||||
float score;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue