Skip to content

Commit

Permalink
wip2
Browse files Browse the repository at this point in the history
  • Loading branch information
smoogipoo committed Dec 11, 2024
1 parent bb6c523 commit 75ff473
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 60 deletions.
8 changes: 3 additions & 5 deletions osu.Framework/Graphics/Drawable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1532,11 +1532,7 @@ internal set

// When this drawable is part of any focus hierarchy (whether it is the first responder or not) unfocus the environment.
if (HasFocus)
{
// Prevent re-entry.
HasFocus = false;
GetContainingFocusSystem()!.ResignFocus(GetContainingFocusEnvironment()!.CurrentFocus!);
}
GetContainingFocusSystem()!.ResignFocusImmediately(this);

parent = value;
Invalidate(InvalidationFromParentSize | Invalidation.Colour | Invalidation.Presence | Invalidation.Parent);
Expand Down Expand Up @@ -2416,6 +2412,8 @@ public bool TriggerEvent(UIEvent e)
/// </summary>
public virtual bool HandlePositionalInput => RequestsPositionalInput;

internal bool HadFocus { get; set; }

/// <summary>
/// Check whether we have active focus.
/// </summary>
Expand Down
32 changes: 0 additions & 32 deletions osu.Framework/Input/Focus/IFocusEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,5 @@ internal void ChangeFocus(InputState state, Drawable? target)

CurrentFocus = target;
}

internal static bool IsDrawableValidForFocus(Drawable drawable)
{
while (drawable != null)
{
if (!drawable.IsAlive || !drawable.IsPresent || drawable.Parent == null)
return false;

if (drawable is IFocusEnvironment)
return true;

drawable = drawable.Parent;
}

return false;
}

internal static IEnumerable<Drawable> BuildFocusSet(Drawable? target)
{
Drawable? d = target;

while (d != null)
{
if (d is Drawable obj)
yield return obj;

if (d is IFocusEnvironment)
break;

d = d.Parent;
}
}
}
}
2 changes: 2 additions & 0 deletions osu.Framework/Input/Focus/IFocusSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ public interface IFocusSystem : IDrawable
/// </summary>
/// <param name="target">The drawable.</param>
void ResignFocus(Drawable target);

void ResignFocusImmediately(Drawable target);
}
}
114 changes: 91 additions & 23 deletions osu.Framework/Input/InputManager_Focus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ private void updateFocus()
Debug.Assert(env.CurrentFocus != null);

if (!isDrawableValidForFocus(env.CurrentFocus))
{
}
env.ChangeFocus(CurrentState, null);
}

activeEnvironments.Clear();
}

foreach (IFocusEnvironment env in activeEnvironments)
Expand All @@ -50,6 +51,12 @@ public void AcquireFocus(Drawable target)
public void ResignFocus(Drawable target)
=> enqueueRequest(new FocusRequest(FocusRequestType.Resign, CurrentState, target));

public void ResignFocusImmediately(Drawable target)
{
Debug.Assert(target.HasFocus);
resign(CurrentState, getEnvironment(target)!.CurrentFocus!);
}

internal FocusUpdateBatch BeginFocusUpdateBatch() => new FocusUpdateBatch(this);

internal readonly ref struct FocusUpdateBatch
Expand Down Expand Up @@ -132,40 +139,78 @@ void acquire(InputState state, Drawable target)
{
lastFirstResponder = FirstResponder;
nextFirstResponder = target;

FirstResponder = target;
}

lastFirstResponder?.TriggerEvent(new ResignFirstResponderEvent(state, nextFirstResponder));
environment.ChangeFocus(state, target);
nextFirstResponder?.TriggerEvent(new BecomeFirstResponderEvent(state, lastFirstResponder));
IEnumerable<Drawable> lastFocusSet = [];
IEnumerable<Drawable> nextFocusSet = [];

if (lastFirstResponder != nextFirstResponder)
FirstResponder = nextFirstResponder;
}
if (target != environment.CurrentFocus)
{
lastFocusSet = enumerateFocusSet(environment.CurrentFocus);
nextFocusSet = enumerateFocusSet(target);

void resign(InputState state, Drawable target)
{
if (getEnvironment(target) is not IFocusEnvironment environment)
return;
foreach (var drawable in lastFocusSet)
{
drawable.HadFocus = drawable.HasFocus;
drawable.HasFocus = false;
}

if (environment.CurrentFocus != target)
return;
foreach (var drawable in nextFocusSet)
{
drawable.HadFocus = drawable.HasFocus;
drawable.HasFocus = true;
}

Drawable? lastFirstResponder = null;
Drawable? nextFirstResponder = null;
environment.CurrentFocus = target;
}

if (FirstResponder == target)
// 1. Resign old responder.
lastFirstResponder?.TriggerEvent(new ResignFirstResponderEvent(state, nextFirstResponder));

// 2. Resign focus on old drawables.
foreach (var drawable in lastFocusSet)
{
lastFirstResponder = FirstResponder;
nextFirstResponder = this.ChildrenOfType<IFocusEnvironment>().Select(e => e.CurrentFocus).FirstOrDefault(d => d != null && d != lastFirstResponder);
if (drawable.HadFocus && !drawable.HasFocus)
drawable.TriggerEvent(new FocusLostEvent(state, target));
}

lastFirstResponder?.TriggerEvent(new ResignFirstResponderEvent(state, nextFirstResponder));
environment.ChangeFocus(state, null);
// 3. Acquire focus on new drawables.
foreach (var drawable in nextFocusSet.Reverse())
{
if (!drawable.HadFocus && drawable.HadFocus)
drawable.TriggerEvent(new FocusLostEvent(state, target));
}

// 4. Acquire new responder.
nextFirstResponder?.TriggerEvent(new BecomeFirstResponderEvent(state, lastFirstResponder));
}
}

private void resign(InputState state, Drawable target)
{
if (getEnvironment(target) is not IFocusEnvironment environment)
return;

if (environment.CurrentFocus != target)
return;

Drawable? lastFirstResponder = null;
Drawable? nextFirstResponder = null;

if (lastFirstResponder != nextFirstResponder)
FirstResponder = nextFirstResponder;
if (FirstResponder == target)
{
lastFirstResponder = FirstResponder;
nextFirstResponder = this.ChildrenOfType<IFocusEnvironment>().Select(e => e.CurrentFocus).FirstOrDefault(d => d != null && d != lastFirstResponder);
}

lastFirstResponder?.TriggerEvent(new ResignFirstResponderEvent(state, nextFirstResponder));
environment.ChangeFocus(state, null);
nextFirstResponder?.TriggerEvent(new BecomeFirstResponderEvent(state, lastFirstResponder));

if (lastFirstResponder != nextFirstResponder)
FirstResponder = nextFirstResponder;
}

private IFocusEnvironment? getEnvironment(Drawable? drawable)
Expand All @@ -187,6 +232,29 @@ private bool isDrawableValidForFocus(Drawable drawable)
return true;
}

/// <summary>
/// Enumerates self and all parenting drawables from a target drawable until the first focus environment is found.
/// When the target drawable's focus is changed, the returned set contains all targets that must receive focus change events
/// in order from target to environment.
/// </summary>
/// <param name="target">The drawable whose focus state is changed.</param>
/// <returns>The set of all drawables that must receive focus change events in order from target to environment.</returns>
private static IEnumerable<Drawable> enumerateFocusSet(Drawable? target)
{
Drawable? d = target;

while (d != null)
{
if (d is Drawable obj)
yield return obj;

if (d is IFocusEnvironment)
break;

d = d.Parent;
}
}

void IFocusManager.TriggerFocusContention(Drawable? triggerSource)
{
}
Expand Down

0 comments on commit 75ff473

Please sign in to comment.