diff --git a/xalia/Ui/TargetMoveRoutine.cs b/xalia/Ui/TargetMoveRoutine.cs index 84b2879..79425d3 100644 --- a/xalia/Ui/TargetMoveRoutine.cs +++ b/xalia/Ui/TargetMoveRoutine.cs @@ -43,19 +43,17 @@ private void DoMove(InputState state) { if (Math.Abs((int)state.XAxis) > Math.Abs((int)state.YAxis)) { - double bias = state.YAxis / Math.Abs((double)state.XAxis); if (state.XAxis > 0) - Main.TargetMove(UiMain.Direction.Right, bias); + Main.TargetMove(UiMain.Direction.Right); else - Main.TargetMove(UiMain.Direction.Left, bias); + Main.TargetMove(UiMain.Direction.Left); } else { - double bias = state.XAxis / Math.Abs((double)state.YAxis); if (state.YAxis > 0) - Main.TargetMove(UiMain.Direction.Down, bias); + Main.TargetMove(UiMain.Direction.Down); else - Main.TargetMove(UiMain.Direction.Up, bias); + Main.TargetMove(UiMain.Direction.Up); } } } diff --git a/xalia/Ui/UiMain.cs b/xalia/Ui/UiMain.cs index b6fda87..643356e 100644 --- a/xalia/Ui/UiMain.cs +++ b/xalia/Ui/UiMain.cs @@ -757,7 +757,43 @@ internal enum Direction throw new ArgumentException("invalid Direction value"); } - internal void TargetMove(Direction direction, double bias=0) + private void GetBoundsRelation((int,int,int,int) a, (int,int,int,int) b, + out int x_direction, out int y_direction) + { + // Determine what "direction" an (x,y,w,h) is from another + if (BoundsIntersect(a, b)) + { + // figure out the length of the shared x/y borders + var shared_x = Math.Min(a.Item1 + a.Item3, b.Item1 + b.Item3) - Math.Max(a.Item1, b.Item1); + var shared_y = Math.Min(a.Item2 + a.Item4, b.Item2 + b.Item4) - Math.Max(a.Item2, b.Item2); + int xofs = (a.Item1 * 2 + a.Item3) - (b.Item1 * 2 + b.Item3); + int yofs = (a.Item2 * 2 + a.Item4) - (b.Item2 * 2 + b.Item4); + + if (Math.Abs(xofs) - shared_x > Math.Abs(yofs) - shared_y) + yofs = 0; + else if (Math.Abs(xofs) - shared_x < Math.Abs(yofs) - shared_y) + xofs = 0; + x_direction = Math.Sign(xofs); + y_direction = Math.Sign(yofs); + return; + } + + if (a.Item1 >= b.Item1 + b.Item3) + x_direction = 1; + else if (a.Item1 + a.Item3 <= b.Item1) + x_direction = -1; + else + x_direction = 0; + + if (a.Item2 >= b.Item2 + b.Item4) + y_direction = 1; + else if (a.Item2 + a.Item4 <= b.Item2) + y_direction = -1; + else + y_direction = 0; + } + + internal void TargetMove(Direction direction) { if (TargetedElement is null) return; @@ -768,124 +804,77 @@ internal void TargetMove(Direction direction, double bias=0) return; } + var candidates = new List>(targetable_elements); + + int current_perpendicular; + current_bounds = TranslateBox(current_bounds, direction); - UiDomElement best_element = null; - // These are actually distance squared so we can skip sqrt. - long best_edge_distance = 0; - long best_center_distance = 0; - - foreach (var kvp in targetable_elements) - { - var candidate_element = kvp.Key; - var candidate_bounds = kvp.Value; - - if (candidate_element == TargetedElement) - continue; + var is_horizontal = (direction == Direction.Left || direction == Direction.Right); - candidate_bounds = TranslateBox(candidate_bounds, direction); + if (is_horizontal) + { + // Filter out anything that'd be vertical movement, or no horizontal movement + candidates.RemoveAll((KeyValuePair kvp) => + { + if (kvp.Key == TargetedElement) + return true; + GetBoundsRelation(current_bounds, TranslateBox(kvp.Value, direction), out var xd, out var yd); + if (xd == 0) + return true; + if (yd != 0) + return true; + return false; + }); - if (candidate_bounds.Item1 + candidate_bounds.Item3 > current_bounds.Item1 && - current_bounds.Item1 + current_bounds.Item3 > candidate_bounds.Item1 && - candidate_bounds.Item2 + candidate_bounds.Item4 > current_bounds.Item2 && - current_bounds.Item2 + current_bounds.Item4 > candidate_bounds.Item2) + if (candidates.Count == 0) { - // candidate intersects current target - int current_center_x = current_bounds.Item1 + current_bounds.Item3 / 2; - int current_center_y = current_bounds.Item2 + current_bounds.Item4 / 2; - int candidate_center_x = candidate_bounds.Item1 + candidate_bounds.Item3 / 2; - int candidate_center_y = candidate_bounds.Item2 + candidate_bounds.Item4 / 2; + AdjustValue(TargetedElement, direction); + return; + } + } - int center_dx = candidate_center_x - current_center_x; - int center_dy = candidate_center_y - current_center_y; + current_perpendicular = current_bounds.Item2 * 2 + current_bounds.Item4; - if (center_dx <= 0) - { - // candidate center is not to the right of target - continue; - } + var best_perpendicular = int.MaxValue; + var best_bounds = (0, 0, 0, 0); + UiDomElement best_element = null; + foreach (var kvp in candidates) + { + if (kvp.Key == TargetedElement) + continue; - if (center_dx < Math.Abs(center_dy)) - { - // candidate is more up/down than right. - continue; - } + var box = TranslateBox(kvp.Value, direction); + int box_perpendicular; - /* This value of edge_distance will be negative. This is intentional. */ - int edge_distance = candidate_bounds.Item1 - (current_bounds.Item1 + current_bounds.Item3); - int center_distance = center_dx * center_dx + center_dy * center_dy; + box_perpendicular = Math.Abs(box.Item2 * 2 + box.Item4 - current_perpendicular); - if (best_element is null || - edge_distance < best_edge_distance || - (edge_distance == best_edge_distance && center_distance < best_center_distance)) - { - best_element = candidate_element; - best_edge_distance = edge_distance; - best_center_distance = center_distance; - } - continue; - } + GetBoundsRelation(current_bounds, box, out var xd, out var yd); - if (candidate_bounds.Item1 < current_bounds.Item1 + current_bounds.Item3) - { - // candidate's left edge must be to the right of current target + if (xd >= 0) + // This would be moving "backwards" or "sideways" continue; - } - // Calculate edge distance - long dx = candidate_bounds.Item1 - (current_bounds.Item1 + current_bounds.Item3); - long dy; - - int y_diff_start = candidate_bounds.Item2 - current_bounds.Item2; - - if (bias != 0) + if (!(best_element is null)) { - y_diff_start -= (int)(Math.Round(bias * dx)); - } + GetBoundsRelation(best_bounds, box, out var bxd, out var byd); - int y_diff_end = y_diff_start + candidate_bounds.Item4; + if (bxd < 0) + // best_element is closer along the axis we're moving + continue; - if (y_diff_end < 0) - dy = -y_diff_end; - else if (y_diff_start > current_bounds.Item4) - dy = y_diff_start; - else - dy = 0; - if (dy != 0) - { - // Use the far edge/corner rather than the near in this case - dx += candidate_bounds.Item3; - dy += candidate_bounds.Item4; - } - var candidate_edge_distance = (dx * dx) + (dy * dy) * 4; - - // Calculate centerpoint distance, with bias - var candidate_biased_y = candidate_bounds.Item2 + (int)Math.Round(candidate_bounds.Item4 * (1 - bias) / 2); - candidate_biased_y -= (int)Math.Round(bias * dx); - var current_biased_y = current_bounds.Item2 + (int)Math.Round(current_bounds.Item4 * (1 + bias) / 2); - dy = candidate_biased_y - current_biased_y; - var candidate_center_distance = (dx * dx) + (dy * dy) * 4; - - if (best_element is null || - candidate_edge_distance < best_edge_distance || - (candidate_edge_distance == best_edge_distance && candidate_center_distance < best_center_distance)) - { - best_element = candidate_element; - best_edge_distance = candidate_edge_distance; - best_center_distance = candidate_center_distance; - continue; + if (bxd == 0 && box_perpendicular > best_perpendicular) + // similar distance along the axis we're moving, but best is less diagonal + continue; } + + best_element = kvp.Key; + best_bounds = box; + best_perpendicular = box_perpendicular; } if (best_element is null) - { - if (ScrollAncestor(TargetedElement, direction)) - return; - - AdjustValue(TargetedElement, direction); - return; - } TargetedElement = best_element;