From 10c448468c4e225fab701e69bdc296422bb3f509 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 12 Sep 2016 18:34:12 +0300 Subject: [PATCH] Rewrite Panel navigation logic so it makes more sense Rather than calculating distances between widget centers it now considers the closeness of their edges. It's not perfect but better than before. --- source/panel.cpp | 101 +++++++++++++++++++++++++++++------------------ source/panel.h | 4 +- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/source/panel.cpp b/source/panel.cpp index 7e34c59..dc2808e 100644 --- a/source/panel.cpp +++ b/source/panel.cpp @@ -66,49 +66,31 @@ bool Panel::navigate(Navigation nav) if(nav==NAV_UP || nav==NAV_DOWN || nav==NAV_LEFT || nav==NAV_RIGHT) { - int x = geom.w/2; - int y = geom.h/2; + int nav_x = (nav==NAV_RIGHT ? 1 : nav==NAV_LEFT ? -1 : 0); + int nav_y = (nav==NAV_UP ? 1 : nav==NAV_DOWN ? -1 : 0); + + int origin_x, origin_y, origin_dim; if(input_focus) { const Geometry &fgeom = input_focus->get_geometry(); - x = fgeom.x+fgeom.w/2; - y = fgeom.y+fgeom.h/2; + origin_x = fgeom.x+(nav_x*0.5+0.5)*fgeom.w; + origin_y = fgeom.y+(nav_y*0.5+0.5)*fgeom.h; + origin_dim = abs(nav_x)*fgeom.h+abs(nav_y)*fgeom.w; } - else if(nav==NAV_UP) - y = 0; - else if(nav==NAV_DOWN) - y = geom.h; - else if(nav==NAV_RIGHT) - x = 0; - else if(nav==NAV_LEFT) - x = geom.w; - - Widget *sibling = 0; - int best_score = 0; - for(list::const_iterator i=children.begin(); i!=children.end(); ++i) + else { - if((*i)->widget==input_focus || !(*i)->widget->is_focusable()) - continue; - - const Geometry &cgeom = (*i)->widget->get_geometry(); - int dx = cgeom.x+cgeom.w/2-x; - int dy = cgeom.y+cgeom.h/2-y; - - int score = -1; - if(nav==NAV_UP && dy>0) - score = dy+abs(dx)*4; - else if(nav==NAV_DOWN && dy<0) - score = -dy+abs(dx)*4; - else if(nav==NAV_RIGHT && dx>0) - score = dx+abs(dy)*4; - else if(nav==NAV_LEFT && dx<0) - score = -dx+abs(dy)*4; - - if(score>0 && (!sibling || scorewidget; - best_score = score; - } + origin_x = geom.w*(0.5-nav_x*0.5); + origin_y = geom.h*(0.5-nav_y*0.5); + origin_dim = abs(nav_x)*geom.h+abs(nav_y)*geom.w; + } + + Widget *sibling = find_next_child(origin_x, origin_y, origin_dim, nav_x, nav_y); + if(!sibling && input_focus) + { + const Geometry &fgeom = input_focus->get_geometry(); + origin_x -= fgeom.w*(nav_x*0.5); + origin_y -= fgeom.h*(nav_y*0.5); + sibling = find_next_child(origin_x, origin_y, origin_dim, nav_x, nav_y); } if(sibling) @@ -123,6 +105,49 @@ bool Panel::navigate(Navigation nav) return false; } +Widget *Panel::find_next_child(int origin_x, int origin_y, int origin_dim, int nav_x, int nav_y) const +{ + Widget *sibling = 0; + int best_score = 0; + for(list::const_iterator i=children.begin(); i!=children.end(); ++i) + { + if((*i)->widget==input_focus || !(*i)->widget->is_focusable()) + continue; + + const Geometry &cgeom = (*i)->widget->get_geometry(); + int dx = compute_delta(cgeom.x, cgeom.w, origin_x, origin_dim, nav_x); + int dy = compute_delta(cgeom.y, cgeom.h, origin_y, origin_dim, nav_y); + + int score = -1; + if(nav_y && nav_y*dy>=0) + score = nav_y*dy+abs(dx)*4; + else if(nav_x && nav_x*dx>=0) + score = nav_x*dx+abs(dy)*4; + + if(score>=0 && (!sibling || scorewidget; + best_score = score; + } + } + + return sibling; +} + +int Panel::compute_delta(int pos, int dim, int origin_pos, int origin_dim, int nav) +{ + if(nav<0) + return pos+dim-origin_pos; + else if(nav>0) + return pos-origin_pos; + else if(pos+dimorigin_pos+origin_dim/2) + return pos-origin_pos-origin_dim/2; + else + return 0; +} + void Panel::on_geometry_change() { if(layout) diff --git a/source/panel.h b/source/panel.h index ebe765d..2bc7a5f 100644 --- a/source/panel.h +++ b/source/panel.h @@ -73,8 +73,10 @@ protected: public: virtual bool navigate(Navigation); - protected: + Widget *find_next_child(int, int, int, int, int) const; + static int compute_delta(int, int, int, int, int); + virtual void on_geometry_change(); virtual void on_child_added(Widget &); virtual void on_child_removed(Widget &); -- 2.43.0