Make keyboard navigation act slightly less crazy.

This commit is contained in:
Henrik Rydgård 2013-10-23 13:55:11 +02:00
parent 4d49541aa5
commit 4c8157694a
5 changed files with 72 additions and 22 deletions

View File

@ -2,6 +2,7 @@
#include "input/input_state.h"
#include "ui/screen.h"
#include "ui/ui.h"
#include "ui/view.h"
ScreenManager::ScreenManager() {
nextScreen_ = 0;
@ -60,6 +61,7 @@ void ScreenManager::switchToNext() {
delete temp.screen;
}
nextScreen_ = 0;
UI::SetFocusedView(0);
}
void ScreenManager::touch(const TouchInput &touch) {
@ -148,6 +150,7 @@ void ScreenManager::push(Screen *screen, int layerFlags) {
if (screen->isTransparent()) {
layerFlags |= LAYER_TRANSPARENT;
}
UI::SetFocusedView(0);
Layer layer = {screen, layerFlags};
stack_.push_back(layer);
}

View File

@ -20,6 +20,10 @@
#include "base/display.h"
#include "base/NativeApp.h"
namespace UI {
class View;
}
struct InputState;
enum DialogResult {
@ -121,6 +125,7 @@ private:
struct Layer {
Screen *screen;
int flags; // From LAYER_ enum above
UI::View *focusedView; // TODO: save focus here. Going for quick solution now to reset focus.
};
// Dialog stack. These are shown "on top" of base screens and the Android back button works as expected.

View File

@ -1,6 +1,6 @@
#pragma once
// More traditional UI framework than ui/ui.h.
// More traditional UI framework than ui/ui.h.
// Still very simple to use.

View File

@ -14,6 +14,9 @@ namespace UI {
const float ITEM_HEIGHT = 64.f;
static recursive_mutex focusLock;
static std::vector<int> focusMoves;
void ApplyGravity(const Bounds outer, const Margins &margins, float w, float h, int gravity, Bounds &inner) {
inner.w = w - (margins.left + margins.right);
inner.h = h - (margins.right + margins.left);
@ -140,6 +143,33 @@ bool ViewGroup::SubviewFocused(View *view) {
return false;
}
static float HorizontalOverlap(const Bounds &a, const Bounds &b) {
if (a.x2() < b.x || b.x2() < a.x)
return 0.0f;
// okay they do overlap. Let's clip.
float maxMin = std::max(a.x, b.x);
float minMax = std::min(a.x2(), b.x2());
float overlap = minMax - maxMin;
if (overlap < 0.0f)
return 0.0f;
else
return std::min(1.0f, overlap / std::min(a.w, b.w));
}
// Returns the percentage the smaller one overlaps the bigger one.
static float VerticalOverlap(const Bounds &a, const Bounds &b) {
if (a.y2() < b.y || b.y2() < a.y)
return 0.0f;
// okay they do overlap. Let's clip.
float maxMin = std::max(a.y, b.y);
float minMax = std::min(a.y2(), b.y2());
float overlap = minMax - maxMin;
if (overlap < 0.0f)
return 0.0f;
else
return std::min(1.0f, overlap / std::min(a.h, b.h));
}
float GetDirectionScore(View *origin, View *destination, FocusDirection direction) {
// Skip labels and things like that.
if (!destination->CanBeFocused())
@ -155,30 +185,42 @@ float GetDirectionScore(View *origin, View *destination, FocusDirection directio
float dx = destPos.x - originPos.x;
float dy = destPos.y - originPos.y;
float distance = sqrtf(dx*dx+dy*dy);
float distance = sqrtf(dx*dx + dy*dy);
float overlap = 0.0f;
float dirX = dx / distance;
float dirY = dy / distance;
bool wrongDirection = false;
float horizOverlap = HorizontalOverlap(origin->GetBounds(), destination->GetBounds());
float vertOverlap = VerticalOverlap(origin->GetBounds(), destination->GetBounds());
if (horizOverlap == 1.0f && vertOverlap == 1.0f) {
ILOG("Contain overlap");
return 0.0;
}
switch (direction) {
case FOCUS_LEFT:
distance = -dirX / sqrtf(distance);
//if (dirX > 0.0f) return 0.0f;
//if (fabsf(dirY) > fabsf(dirX)) return 0.0f;
overlap = vertOverlap;
if (dirX > 0.0f) {
wrongDirection = true;
}
break;
case FOCUS_UP:
distance = -dirY / sqrtf(distance);
//if (dirY > 0.0f) return 0.0f;
//if (fabsf(dirX) > fabsf(dirY)) return 0.0f;
overlap = horizOverlap;
if (dirY > 0.0f) {
wrongDirection = true;
}
break;
case FOCUS_RIGHT:
//if (dirX < 0.0f) return 0.0f;
//if (fabsf(dirY) > fabsf(dirX)) return 0.0f;
distance = dirX / sqrtf(distance);
overlap = vertOverlap;
if (dirX < 0.0f) {
wrongDirection = true;
}
break;
case FOCUS_DOWN:
//if (dirY < 0.0f) return 0.0f;
//if (fabsf(dirX) > fabsf(dirY)) return 0.0f;
distance = dirY / sqrtf(distance);
overlap = horizOverlap;
if (dirY < 0.0f) {
wrongDirection = true;
}
break;
case FOCUS_PREV:
case FOCUS_NEXT:
@ -186,7 +228,10 @@ float GetDirectionScore(View *origin, View *destination, FocusDirection directio
break;
}
return distance;
if (wrongDirection)
return 0.0f;
else
return 1.0f/distance + overlap*10;
}
@ -207,7 +252,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
// TODO: Do the cardinal directions right. Now we just map to
// prev/next.
switch (direction) {
case FOCUS_PREV:
// If view not found, no neighbor to find.
@ -257,7 +302,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei
default:
return result;
}
}
}
void MoveFocus(ViewGroup *root, FocusDirection direction) {
@ -287,7 +332,7 @@ void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
if (views_.empty())
return;
return;
float sum = 0.0f;
float maxOther = 0.0f;
@ -992,9 +1037,6 @@ void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root) {
root->Layout();
}
static recursive_mutex focusLock;
static std::vector<int> focusMoves;
void KeyEvent(const KeyInput &key, ViewGroup *root) {
if (key.flags & KEY_DOWN) {
// We ignore the device ID here. Anything with a DPAD is OK.

View File

@ -354,7 +354,7 @@ private:
class ListView : public ScrollView {
public:
ListView(ListAdaptor *a, LayoutParams *layoutParams = 0);
int GetSelected() { return adaptor_->GetSelected(); }
Event OnChoice;