mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
WIP: Working through adornment focus stuff.
This commit is contained in:
@@ -62,6 +62,11 @@ public class ApplicationNavigation
|
||||
}
|
||||
}
|
||||
|
||||
//if (start.Border is { })
|
||||
//{
|
||||
// return IsInHierarchy (start.Border, view);
|
||||
//}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public class Border : Adornment
|
||||
{
|
||||
Parent = parent;
|
||||
CanFocus = false;
|
||||
TabStop = TabBehavior.TabGroup;
|
||||
|
||||
Application.GrabbingMouse += Application_GrabbingMouse;
|
||||
Application.UnGrabbingMouse += Application_UnGrabbingMouse;
|
||||
@@ -291,22 +292,22 @@ public class Border : Adornment
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3312
|
||||
if (!_dragPosition.HasValue && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
|
||||
{
|
||||
Parent.SetFocus ();
|
||||
ApplicationOverlapped.BringOverlappedTopToFront ();
|
||||
|
||||
if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable)
|
||||
&& !Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only start grabbing if the user clicks in the Thickness area
|
||||
// Adornment.Contains takes Parent SuperView=relative coords.
|
||||
if (Contains (new (mouseEvent.Position.X + Parent.Frame.X + Frame.X, mouseEvent.Position.Y + Parent.Frame.Y + Frame.Y)))
|
||||
@@ -930,11 +931,16 @@ public class Border : Adornment
|
||||
|
||||
private ViewArrangement _arranging;
|
||||
|
||||
private Button? _arrangeButton;
|
||||
private Button? _moveButton; // always top-left
|
||||
private Button? _allSizeButton;
|
||||
private Button? _leftSizeButton;
|
||||
private Button? _rightSizeButton;
|
||||
private Button? _topSizeButton;
|
||||
private Button? _bottomSizeButton;
|
||||
|
||||
/// <summary>
|
||||
/// Starts "Arrange Mode" where <see cref="Adornment.Parent"/> can be moved and/or resized using the mouse
|
||||
/// or keyboard.
|
||||
/// or keyboard. If <paramref name="arrangement"/> is <see cref="ViewArrangement.Fixed"/> keyboard mode is enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Arrange Mode is exited by the user pressing <see cref="Application.ArrangeKey"/>, <see cref="Key.Esc"/>, or by clicking
|
||||
@@ -955,21 +961,231 @@ public class Border : Adornment
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Assert (_arrangeButton is null);
|
||||
_arrangeButton = new Button
|
||||
// Add Commands and Keybindigs - Note it's ok these get added each time. KeyBindings are cleared in EndArrange()
|
||||
AddArrangeModeKeyBindings ();
|
||||
|
||||
Application.MouseEvent += ApplicationOnMouseEvent;
|
||||
|
||||
// Create buttons for resizing and moving
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
|
||||
{
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
};
|
||||
Add (_arrangeButton);
|
||||
Debug.Assert (_moveButton is null);
|
||||
|
||||
CanFocus = true;
|
||||
_moveButton = new Button
|
||||
{
|
||||
Id = "moveButton",
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
Visible = false,
|
||||
};
|
||||
Add (_moveButton);
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
|
||||
{
|
||||
Debug.Assert (_allSizeButton is null);
|
||||
|
||||
_allSizeButton = new Button
|
||||
{
|
||||
Id = "allSizeButton",
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
X = Pos.AnchorEnd (),
|
||||
Y = Pos.AnchorEnd (),
|
||||
Visible = false,
|
||||
};
|
||||
Add (_allSizeButton);
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable))
|
||||
{
|
||||
Debug.Assert (_topSizeButton is null);
|
||||
|
||||
_topSizeButton = new Button
|
||||
{
|
||||
Id = "topSizeButton",
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
X = Pos.Center () + Parent!.Margin.Thickness.Horizontal,
|
||||
Y = 0,
|
||||
Visible = false,
|
||||
};
|
||||
Add (_topSizeButton);
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
|
||||
{
|
||||
Debug.Assert (_rightSizeButton is null);
|
||||
|
||||
_rightSizeButton = new Button
|
||||
{
|
||||
Id = "rightSizeButton",
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
X = Pos.AnchorEnd (),
|
||||
Y = Pos.Center () + Parent!.Margin.Thickness.Vertical,
|
||||
Visible = false,
|
||||
};
|
||||
Add (_rightSizeButton);
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
|
||||
{
|
||||
Debug.Assert (_leftSizeButton is null);
|
||||
|
||||
_leftSizeButton = new Button
|
||||
{
|
||||
Id = "leftSizeButton",
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
X = 0,
|
||||
Y = Pos.Center () + Parent!.Margin.Thickness.Vertical,
|
||||
Visible = false,
|
||||
};
|
||||
Add (_leftSizeButton);
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable))
|
||||
{
|
||||
Debug.Assert (_bottomSizeButton is null);
|
||||
|
||||
_bottomSizeButton = new Button
|
||||
{
|
||||
Id = "bottomSizeButton",
|
||||
CanFocus = true,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
NoDecorations = true,
|
||||
NoPadding = true,
|
||||
ShadowStyle = ShadowStyle.None,
|
||||
Text = $"{Glyphs.Diamond}",
|
||||
X = Pos.Center () + Parent!.Margin.Thickness.Horizontal,
|
||||
Y = Pos.AnchorEnd (),
|
||||
Visible = false,
|
||||
};
|
||||
Add (_bottomSizeButton);
|
||||
}
|
||||
|
||||
|
||||
if (arrangement == ViewArrangement.Fixed)
|
||||
{
|
||||
// Keyboard mode
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
|
||||
{
|
||||
_arranging = ViewArrangement.Movable;
|
||||
_moveButton!.Visible = true;
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
|
||||
{
|
||||
_arranging = ViewArrangement.Resizable;
|
||||
_allSizeButton!.Visible = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mouse mode
|
||||
_arranging = arrangement;
|
||||
|
||||
switch (_arranging)
|
||||
{
|
||||
case ViewArrangement.Movable:
|
||||
_moveButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.RightResizable | ViewArrangement.BottomResizable:
|
||||
case ViewArrangement.Resizable:
|
||||
_rightSizeButton!.Visible = true;
|
||||
_bottomSizeButton!.Visible = true;
|
||||
_allSizeButton!.X = Pos.AnchorEnd ();
|
||||
_allSizeButton!.Y = Pos.AnchorEnd ();
|
||||
_allSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.LeftResizable:
|
||||
_leftSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.RightResizable:
|
||||
_rightSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.TopResizable:
|
||||
_topSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.BottomResizable:
|
||||
_bottomSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.LeftResizable | ViewArrangement.BottomResizable:
|
||||
_rightSizeButton!.Visible = true;
|
||||
_bottomSizeButton!.Visible = true;
|
||||
_allSizeButton!.X = 0;
|
||||
_allSizeButton!.Y = Pos.AnchorEnd ();
|
||||
_allSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.LeftResizable | ViewArrangement.TopResizable:
|
||||
_leftSizeButton!.Visible = true;
|
||||
_topSizeButton!.Visible = true;
|
||||
break;
|
||||
|
||||
case ViewArrangement.RightResizable | ViewArrangement.TopResizable:
|
||||
_rightSizeButton!.Visible = true;
|
||||
_topSizeButton!.Visible = true;
|
||||
_allSizeButton!.X = Pos.AnchorEnd ();
|
||||
_allSizeButton!.Y = 0;
|
||||
_allSizeButton!.Visible = true;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (_arranging != ViewArrangement.Fixed)
|
||||
{
|
||||
if (arrangement == ViewArrangement.Fixed)
|
||||
{
|
||||
// Keyboard mode - enable nav
|
||||
CanFocus = true;
|
||||
SetFocus ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hack for now
|
||||
EndArrange ();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddArrangeModeKeyBindings ()
|
||||
{
|
||||
AddCommand (Command.Quit, EndArrange);
|
||||
|
||||
AddCommand (Command.Up,
|
||||
@@ -1060,8 +1276,18 @@ public class Border : Adornment
|
||||
return true;
|
||||
});
|
||||
|
||||
AddCommand (Command.Tab, Navigate);
|
||||
AddCommand (Command.BackTab, Navigate);
|
||||
AddCommand (Command.Tab, () =>
|
||||
{
|
||||
AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
|
||||
return true; // Always eat
|
||||
});
|
||||
AddCommand (Command.BackTab, () =>
|
||||
{
|
||||
AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
|
||||
|
||||
return true; // Always eat
|
||||
});
|
||||
|
||||
KeyBindings.Add (Key.Esc, KeyBindingScope.HotKey, Command.Quit);
|
||||
KeyBindings.Add (Application.ArrangeKey, KeyBindingScope.HotKey, Command.Quit);
|
||||
@@ -1072,113 +1298,6 @@ public class Border : Adornment
|
||||
|
||||
KeyBindings.Add (Key.Tab, KeyBindingScope.HotKey, Command.Tab);
|
||||
KeyBindings.Add (Key.Tab.WithShift, KeyBindingScope.HotKey, Command.BackTab);
|
||||
|
||||
Application.MouseEvent += ApplicationOnMouseEvent;
|
||||
|
||||
if (arrangement == ViewArrangement.Fixed)
|
||||
{
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
|
||||
{
|
||||
_arranging = ViewArrangement.Movable;
|
||||
_arrangeButton.X = 0;
|
||||
_arrangeButton.Y = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
|
||||
{
|
||||
_arranging = ViewArrangement.Resizable;
|
||||
_arrangeButton.X = Pos.AnchorEnd ();
|
||||
_arrangeButton.Y = Pos.AnchorEnd ();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_arranging = arrangement;
|
||||
|
||||
switch (_arranging)
|
||||
{
|
||||
case ViewArrangement.Movable:
|
||||
_arrangeButton.X = 0;
|
||||
_arrangeButton.Y = 0;
|
||||
return true;
|
||||
|
||||
case ViewArrangement.RightResizable | ViewArrangement.BottomResizable:
|
||||
case ViewArrangement.Resizable:
|
||||
_arrangeButton.X = Pos.AnchorEnd ();
|
||||
_arrangeButton.Y = Pos.AnchorEnd ();
|
||||
return true;
|
||||
|
||||
case ViewArrangement.LeftResizable:
|
||||
_arrangeButton.X = 0;
|
||||
_arrangeButton.Y = Pos.Center () + Parent!.Margin.Thickness.Vertical;
|
||||
return true;
|
||||
|
||||
case ViewArrangement.RightResizable:
|
||||
_arrangeButton.X = Pos.AnchorEnd ();
|
||||
_arrangeButton.Y = Pos.Center ();
|
||||
return true;
|
||||
|
||||
case ViewArrangement.TopResizable:
|
||||
_arrangeButton.X = Pos.Center ();
|
||||
_arrangeButton.Y = 0;
|
||||
return true;
|
||||
|
||||
case ViewArrangement.BottomResizable:
|
||||
_arrangeButton.X = Pos.Center ();
|
||||
_arrangeButton.Y = Pos.AnchorEnd ();
|
||||
return true;
|
||||
|
||||
case ViewArrangement.LeftResizable | ViewArrangement.BottomResizable:
|
||||
_arrangeButton.X = 0;
|
||||
_arrangeButton.Y = Pos.AnchorEnd ();
|
||||
|
||||
return true;
|
||||
|
||||
case ViewArrangement.LeftResizable | ViewArrangement.TopResizable:
|
||||
_arrangeButton.X = 0;
|
||||
_arrangeButton.Y = 0;
|
||||
|
||||
return true;
|
||||
|
||||
case ViewArrangement.RightResizable | ViewArrangement.TopResizable:
|
||||
_arrangeButton.X = Pos.AnchorEnd (); ;
|
||||
_arrangeButton.Y = 0;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Hack for now
|
||||
EndArrange ();
|
||||
return false;
|
||||
|
||||
bool? Navigate ()
|
||||
{
|
||||
if (_arranging == ViewArrangement.Movable)
|
||||
{
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
|
||||
{
|
||||
_arranging = ViewArrangement.Resizable;
|
||||
_arrangeButton.X = Pos.AnchorEnd ();
|
||||
_arrangeButton.Y = Pos.AnchorEnd ();
|
||||
}
|
||||
}
|
||||
else if (_arranging == ViewArrangement.Resizable)
|
||||
{
|
||||
if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
|
||||
{
|
||||
_arranging = ViewArrangement.Movable;
|
||||
_arrangeButton.X = 0;
|
||||
_arrangeButton.Y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplicationOnMouseEvent (object? sender, MouseEvent e)
|
||||
@@ -1210,17 +1329,55 @@ public class Border : Adornment
|
||||
Application.UngrabMouse ();
|
||||
}
|
||||
|
||||
CanFocus = false;
|
||||
|
||||
if (_arrangeButton is { })
|
||||
if (_moveButton is { })
|
||||
{
|
||||
Remove (_arrangeButton);
|
||||
_arrangeButton.Dispose ();
|
||||
_arrangeButton = null;
|
||||
Remove (_moveButton);
|
||||
_moveButton.Dispose ();
|
||||
_moveButton = null;
|
||||
}
|
||||
|
||||
if (_allSizeButton is { })
|
||||
{
|
||||
Remove (_allSizeButton);
|
||||
_allSizeButton.Dispose ();
|
||||
_allSizeButton = null;
|
||||
}
|
||||
|
||||
if (_leftSizeButton is { })
|
||||
{
|
||||
Remove (_leftSizeButton);
|
||||
_leftSizeButton.Dispose ();
|
||||
_leftSizeButton = null;
|
||||
}
|
||||
|
||||
if (_rightSizeButton is { })
|
||||
{
|
||||
Remove (_rightSizeButton);
|
||||
_rightSizeButton.Dispose ();
|
||||
_rightSizeButton = null;
|
||||
}
|
||||
|
||||
if (_topSizeButton is { })
|
||||
{
|
||||
Remove (_topSizeButton);
|
||||
_topSizeButton.Dispose ();
|
||||
_topSizeButton = null;
|
||||
}
|
||||
|
||||
if (_bottomSizeButton is { })
|
||||
{
|
||||
Remove (_bottomSizeButton);
|
||||
_bottomSizeButton.Dispose ();
|
||||
_bottomSizeButton = null;
|
||||
}
|
||||
|
||||
KeyBindings.Clear ();
|
||||
|
||||
if (CanFocus)
|
||||
{
|
||||
CanFocus = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -473,15 +473,16 @@ public partial class View // Drawing APIs
|
||||
{
|
||||
if (NeedsDisplay)
|
||||
{
|
||||
if (!CanBeVisible (this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SuperView is { })
|
||||
{
|
||||
Clear ();
|
||||
}
|
||||
|
||||
if (!CanBeVisible (this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty (TextFormatter.Text))
|
||||
{
|
||||
|
||||
@@ -214,7 +214,33 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
/// <summary>Gets the currently focused Subview of this view, or <see langword="null"/> if nothing is focused.</summary>
|
||||
public View? Focused
|
||||
{
|
||||
get { return Subviews.FirstOrDefault (v => v.HasFocus); }
|
||||
get
|
||||
{
|
||||
View? focused = Subviews.FirstOrDefault (v => v.HasFocus);
|
||||
|
||||
if (focused is { })
|
||||
{
|
||||
return focused;
|
||||
}
|
||||
|
||||
// How about in Adornments?
|
||||
if (Margin is { HasFocus:true })
|
||||
{
|
||||
return Margin;
|
||||
}
|
||||
|
||||
if (Border is { HasFocus: true })
|
||||
{
|
||||
return Border;
|
||||
}
|
||||
|
||||
if (Padding is { HasFocus: true })
|
||||
{
|
||||
return Padding;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns a value indicating if this View is currently on Top (Active)</summary>
|
||||
@@ -384,7 +410,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
if (CanFocus && SuperView is { CanFocus: false })
|
||||
var thisAsAdornment = this as Adornment;
|
||||
View? superViewOrParent = thisAsAdornment?.Parent ?? SuperView;
|
||||
|
||||
if (CanFocus && superViewOrParent is { CanFocus: false })
|
||||
{
|
||||
Debug.WriteLine ($@"WARNING: Attempt to FocusChanging where SuperView.CanFocus == false. {this}");
|
||||
|
||||
@@ -412,7 +441,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
|
||||
// Make sure superviews up the superview hierarchy have focus.
|
||||
// Any of them may cancel gaining focus. In which case we need to back out.
|
||||
if (SuperView is { HasFocus: false } sv)
|
||||
if (superViewOrParent is { HasFocus: false } sv)
|
||||
{
|
||||
(bool focusSet, bool svCancelled) = sv.SetHasFocusTrue (previousFocusedView, true);
|
||||
|
||||
@@ -422,20 +451,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
}
|
||||
}
|
||||
|
||||
// Are we an Adornment?
|
||||
if (this is Adornment adornment)
|
||||
{
|
||||
if (adornment.Parent is { HasFocus: false } parent)
|
||||
{
|
||||
(bool focusSet, bool parentCancelled) = parent.SetHasFocusTrue (previousFocusedView, true);
|
||||
|
||||
if (!focusSet)
|
||||
{
|
||||
return (false, parentCancelled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_hasFocus)
|
||||
{
|
||||
// Something else beat us to the change (likely a FocusChanged handler).
|
||||
@@ -445,16 +460,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
// By setting _hasFocus to true we definitively change HasFocus for this view.
|
||||
|
||||
// Get whatever peer has focus, if any
|
||||
View? focusedPeer = SuperView?.Focused;
|
||||
|
||||
if (focusedPeer is null)
|
||||
{
|
||||
// Are we an Adornment?
|
||||
if (this is Adornment ad)
|
||||
{
|
||||
focusedPeer = ad.Parent?.Focused;
|
||||
}
|
||||
}
|
||||
View? focusedPeer = superViewOrParent?.Focused;
|
||||
|
||||
_hasFocus = true;
|
||||
|
||||
@@ -481,6 +487,14 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
previousFocusedView.SetHasFocusFalse (this);
|
||||
}
|
||||
|
||||
if (previousFocusedView is { HasFocus: true })
|
||||
{
|
||||
if (previousFocusedView.SuperView is Adornment a)
|
||||
{
|
||||
previousFocusedView.SetHasFocusFalse (this);
|
||||
}
|
||||
}
|
||||
|
||||
if (Arrangement.HasFlag (ViewArrangement.Overlapped))
|
||||
{
|
||||
SuperView?.MoveSubviewToEnd (this);
|
||||
@@ -570,43 +584,34 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
throw new InvalidOperationException ("SetHasFocusFalse should not be called if the view does not have focus.");
|
||||
}
|
||||
|
||||
var thisAsAdornment = this as Adornment;
|
||||
View? superViewOrParent = thisAsAdornment?.Parent ?? SuperView;
|
||||
|
||||
// If newFocusedVew is null, we need to find the view that should get focus, and SetFocus on it.
|
||||
if (!traversingDown && newFocusedView is null)
|
||||
{
|
||||
if (SuperView?._previouslyMostFocused is { })
|
||||
if (superViewOrParent?._previouslyMostFocused is { })
|
||||
{
|
||||
if (SuperView?._previouslyMostFocused != this)
|
||||
if (superViewOrParent?._previouslyMostFocused != this)
|
||||
{
|
||||
SuperView?._previouslyMostFocused?.SetFocus ();
|
||||
superViewOrParent?._previouslyMostFocused?.SetFocus ();
|
||||
|
||||
// The above will cause SetHasFocusFalse, so we can return
|
||||
return;
|
||||
}
|
||||
newFocusedView = SuperView?._previouslyMostFocused;
|
||||
|
||||
newFocusedView = superViewOrParent?._previouslyMostFocused;
|
||||
}
|
||||
|
||||
if (SuperView is { })
|
||||
if (superViewOrParent is { })
|
||||
{
|
||||
if (SuperView.AdvanceFocus (NavigationDirection.Forward, TabStop))
|
||||
if (superViewOrParent.AdvanceFocus (NavigationDirection.Forward, TabStop))
|
||||
{
|
||||
// The above will cause SetHasFocusFalse, so we can return
|
||||
return;
|
||||
}
|
||||
newFocusedView = SuperView;
|
||||
}
|
||||
|
||||
// Are we an Adornment?
|
||||
if (this is Adornment ad)
|
||||
{
|
||||
if (ad.Parent is {})
|
||||
{
|
||||
if (ad.Parent.RestoreFocus ())
|
||||
{
|
||||
// The above will cause SetHasFocusFalse, so we can return
|
||||
return;
|
||||
}
|
||||
newFocusedView = ad.Parent;
|
||||
}
|
||||
newFocusedView = superViewOrParent;
|
||||
}
|
||||
|
||||
if (Application.Navigation is { } && Application.Current is { })
|
||||
@@ -645,6 +650,13 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
bottom = bottom.SuperView;
|
||||
}
|
||||
|
||||
if (bottom == this && bottom.SuperView is Adornment a)
|
||||
{
|
||||
a.SetHasFocusFalse (newFocusedView, true);
|
||||
}
|
||||
|
||||
Debug.Assert (!mostFocused.WasDisposed);
|
||||
|
||||
_previouslyMostFocused = mostFocused;
|
||||
}
|
||||
|
||||
@@ -654,7 +666,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
NotifyFocusChanging (HasFocus, !HasFocus, newFocusedView, this);
|
||||
|
||||
// Get whatever peer has focus, if any
|
||||
View? focusedPeer = SuperView?.Focused;
|
||||
View? focusedPeer = superViewOrParent?.Focused;
|
||||
_hasFocus = false;
|
||||
|
||||
if (Application.Navigation is { })
|
||||
@@ -663,7 +675,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
|
||||
if (appFocused is { } || appFocused == this)
|
||||
{
|
||||
Application.Navigation.SetFocused (newFocusedView ?? SuperView);
|
||||
Application.Navigation.SetFocused (newFocusedView ?? superViewOrParent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,9 +687,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
|
||||
return;
|
||||
}
|
||||
|
||||
if (SuperView is { })
|
||||
if (superViewOrParent is { })
|
||||
{
|
||||
SuperView._previouslyMostFocused = focusedPeer;
|
||||
Debug.Assert(!focusedPeer.WasDisposed);
|
||||
superViewOrParent._previouslyMostFocused = focusedPeer;
|
||||
}
|
||||
|
||||
// Post-conditions - prove correctness
|
||||
|
||||
@@ -190,6 +190,115 @@ public class SetFocusTests () : TestsAllViews
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_AdornmentSubView_SetFocus_Sets ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
var borderSubView = new View
|
||||
{
|
||||
Id = "borderSubView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true
|
||||
};
|
||||
borderSubView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Border.Add (borderSubView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
|
||||
view.Border.CanFocus = true;
|
||||
subViewSubView1.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.True (borderSubView.HasFocus);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = false;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = true;
|
||||
view.Border.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (view.Border.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.True (borderSubView.HasFocus);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = false;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = true;
|
||||
subViewSubView1.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.True (view.Border.HasFocus);
|
||||
Assert.True (borderSubView.HasFocus);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
subView.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_Peer_LeavesOther ()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user