UI: Handle focus on page up/down.

This moves keyboard focus along with paging, but it also only responds to
paging when already focused inside the scroll view.
This commit is contained in:
Unknown W. Brackets 2021-08-08 14:46:05 -07:00
parent 8df188af2b
commit b52a495b96
3 changed files with 102 additions and 8 deletions

View file

@ -448,6 +448,7 @@ public:
// Fake RTTI
virtual bool IsViewGroup() const { return false; }
virtual bool ContainsSubview(const View *view) const { return false; }
Point GetFocusPosition(FocusDirection dir);

View file

@ -57,6 +57,14 @@ void ViewGroup::RemoveSubview(View *view) {
}
}
bool ViewGroup::ContainsSubview(const View *view) const {
for (const View *subview : views_) {
if (subview == view || subview->ContainsSubview(view))
return true;
}
return false;
}
void ViewGroup::Clear() {
std::lock_guard<std::mutex> guard(modifyLock_);
for (size_t i = 0; i < views_.size(); i++) {
@ -444,11 +452,57 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
return result;
}
case FOCUS_PREV_PAGE:
case FOCUS_NEXT_PAGE:
return FindScrollNeighbor(view, Point(INFINITY, INFINITY), direction, result);
default:
return result;
}
}
float GetTargetScore(const Point &target, View *view) {
if (!view->CanBeFocused())
return 0.0f;
if (view->IsEnabled() == false)
return 0.0f;
if (view->GetVisibility() != V_VISIBLE)
return 0.0f;
Point viewPos = view->GetBounds().Center();
float dx = viewPos.x - target.x;
float dy = viewPos.y - target.y;
float distance = sqrtf(dx * dx + dy * dy);
return 10.0f / std::max(1.0f, distance);
}
NeighborResult ViewGroup::FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) {
if (!IsEnabled())
return best;
if (GetVisibility() != V_VISIBLE)
return best;
if (target.x < INFINITY && target.y < INFINITY) {
for (auto v : views_) {
// Note: we consider the origin itself, which might already be the best option.
float score = GetTargetScore(target, v);
if (score > best.score) {
best.score = score;
best.view = v;
}
}
}
for (auto v : views_) {
if (v->IsViewGroup()) {
ViewGroup *vg = static_cast<ViewGroup *>(v);
if (vg)
best = vg->FindScrollNeighbor(view, target, direction, best);
}
}
return best;
}
// TODO: This code needs some cleanup/restructuring...
void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) {
MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_);
@ -792,12 +846,6 @@ bool ScrollView::Key(const KeyInput &input) {
case NKCODE_EXT_MOUSEWHEEL_DOWN:
ScrollRelative(250);
break;
case NKCODE_PAGE_DOWN:
ScrollRelative((orientation_ == ORIENT_VERTICAL ? bounds_.h : bounds_.w) - 50);
break;
case NKCODE_PAGE_UP:
ScrollRelative(-(orientation_ == ORIENT_VERTICAL ? bounds_.h : bounds_.w) + 50);
break;
}
}
return ViewGroup::Key(input);
@ -903,6 +951,47 @@ bool ScrollView::SubviewFocused(View *view) {
return true;
}
NeighborResult ScrollView::FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) {
if (ContainsSubview(view) && views_[0]->IsViewGroup()) {
ViewGroup *vg = static_cast<ViewGroup *>(views_[0]);
int found = -1;
for (int i = 0, n = vg->GetNumSubviews(); i < n; ++i) {
View *child = vg->GetViewByIndex(i);
if (child == view || child->ContainsSubview(view)) {
found = i;
break;
}
}
// Okay, the previously focused view is inside this.
if (found != -1) {
float mult = 0.0f;
switch (direction) {
case FOCUS_PREV_PAGE:
mult = -1.0f;
break;
case FOCUS_NEXT_PAGE:
mult = 1.0f;
break;
default:
break;
}
// Okay, now where is our ideal target?
Point targetPos = view->GetBounds().Center();
if (orientation_ == ORIENT_VERTICAL)
targetPos.y += mult * bounds_.h;
else
targetPos.x += mult * bounds_.x;
// Okay, which subview is closest to that?
return vg->FindScrollNeighbor(view, targetPos, direction, best);
}
}
return ViewGroup::FindScrollNeighbor(view, target, direction, best);
}
void ScrollView::PersistData(PersistStatus status, std::string anonId, PersistMap &storage) {
ViewGroup::PersistData(status, anonId, storage);

View file

@ -63,9 +63,11 @@ public:
// Assumes that layout has taken place.
NeighborResult FindNeighbor(View *view, FocusDirection direction, NeighborResult best);
virtual NeighborResult FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best);
virtual bool CanBeFocused() const override { return false; }
virtual bool IsViewGroup() const override { return true; }
bool CanBeFocused() const override { return false; }
bool IsViewGroup() const override { return true; }
bool ContainsSubview(const View *view) const override;
virtual void SetBG(const Drawable &bg) { bg_ = bg; }
@ -268,6 +270,8 @@ public:
void PersistData(PersistStatus status, std::string anonId, PersistMap &storage) override;
void SetVisibility(Visibility visibility) override;
NeighborResult FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) override;
// Quick hack to prevent scrolling to top in some lists
void SetScrollToTop(bool t) { scrollToTopOnSizeChange_ = t; }