From 7da6f12ad915a318ff1bd7f45b0ecf8beb24222f Mon Sep 17 00:00:00 2001 From: Krzysztof Gutkowski Date: Fri, 26 Jun 2026 18:46:20 +0200 Subject: [PATCH 1/3] Update style of key binding UI This PR refreshes the appearance of `KeyBindingRow` to better match the new form controls used in the settings overlay. It also gets rid of the previously used font on the actual key display with Torus. --- .../Input/KeyBindingConflictPopover.cs | 21 ++-- .../Sections/Input/KeyBindingRow.KeyButton.cs | 21 ++-- .../Settings/Sections/Input/KeyBindingRow.cs | 96 +++++++++++-------- 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs index 05aeb0b810d5..4ca51a315ede 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingConflictPopover.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -200,11 +201,7 @@ private void load(OverlayColourProvider colourProvider) Masking = true, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5 - }, + new FormControlBackground(), new GridContainer { RelativeSizeAxes = Axes.X, @@ -219,10 +216,10 @@ private void load(OverlayColourProvider colourProvider) { new Drawable[] { - new OsuSpriteText + new FormFieldCaption { - Text = action.GetLocalisableDescription(), - Margin = new MarginPadding(7.5f), + Caption = action.GetLocalisableDescription(), + Margin = new MarginPadding(9), }, new Container { @@ -239,13 +236,13 @@ private void load(OverlayColourProvider colourProvider) RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background6 }, - Empty().With(d => d.Width = 80), // poor man's min-width + Empty().With(d => d.Width = 60), // poor man's min-width newBindingText = new OsuSpriteText { - Font = OsuFont.Numeric.With(size: 10), + Font = OsuFont.Default.With(size: 14, weight: FontWeight.Bold), Margin = new MarginPadding(5), Anchor = Anchor.Centre, - Origin = Anchor.Centre + Origin = Anchor.Centre, } } }, @@ -288,7 +285,7 @@ private void updateState() if (LocalisableString.IsNullOrEmpty(keyCombinationText)) keyCombinationText = InputSettingsStrings.ActionHasNoKeyBinding; - newBindingText.Text = keyCombinationText; + newBindingText.Text = keyCombinationText.ToUpper(); } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs index 5b986f65eefd..d0636e03e165 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -56,21 +57,17 @@ public bool IsBinding public KeyButton() { - Margin = new MarginPadding(padding); - + AutoSizeAxes = Axes.Both; Masking = true; - CornerRadius = padding; - - Height = height; - AutoSizeAxes = Axes.X; + CornerRadius = 5; Children = new Drawable[] { new Container { AlwaysPresent = true, - Width = 80, - Height = height, + Width = 60, + AutoSizeAxes = Axes.Y, }, box = new Box { @@ -78,8 +75,8 @@ public KeyButton() }, Text = new OsuSpriteText { - Font = OsuFont.Numeric.With(size: 10), - Margin = new MarginPadding(5), + Font = OsuFont.Default.With(size: 14, weight: FontWeight.Bold), + Margin = new MarginPadding(4), Anchor = Anchor.Centre, Origin = Anchor.Centre, }, @@ -131,7 +128,7 @@ private void updateHoverState() else { box.FadeColour(IsHovered ? colourProvider.Light4 : colourProvider.Background6, transition_time, Easing.OutQuint); - Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint); + Text.FadeColour(IsHovered ? Color4.Black : colourProvider.Content1, transition_time, Easing.OutQuint); } } @@ -180,7 +177,7 @@ void updateText() alpha = 0.4f; } - Text.Text = keyCombinationString; + Text.Text = keyCombinationString.ToUpper(); Text.Alpha = alpha; } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index cb839b92e796..7f8a3a3effa7 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -10,18 +10,14 @@ using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Database; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; @@ -74,7 +70,7 @@ public bool MatchingFilter public bool FilteringActive { get; set; } - public IEnumerable FilterTerms => KeyBindings.Select(b => (LocalisableString)keyCombinationProvider.GetReadableString(b.KeyCombination)).Prepend(text.Text); + public IEnumerable FilterTerms => KeyBindings.Select(b => (LocalisableString)keyCombinationProvider.GetReadableString(b.KeyCombination)).Prepend(caption.Caption); #endregion @@ -93,7 +89,8 @@ public bool MatchingFilter private Container content = null!; - private OsuSpriteText text = null!; + private FormControlBackground background = null!; + private FormFieldCaption caption = null!; private SettingsRevertToDefaultButton revertButton = null!; private FillFlowContainer cancelAndClearButtons = null!; private FillFlowContainer buttons = null!; @@ -103,8 +100,7 @@ public bool MatchingFilter private Sample?[]? keypressSamples; private const float transition_time = 150; - private const float height = 20; - private const float padding = 5; + private const float spacing = 5; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos); @@ -124,11 +120,11 @@ public KeyBindingRow(object action) } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, AudioManager audioManager) + private void load(AudioManager audioManager) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Padding = new MarginPadding { Right = SettingsPanel.CONTENT_PADDING.Right }; + Padding = SettingsPanel.CONTENT_PADDING; InternalChildren = new Drawable[] { @@ -139,49 +135,62 @@ private void load(OverlayColourProvider colourProvider, AudioManager audioManage RelativeSizeAxes = Axes.Y, Action = RestoreDefaults, }, - new Container + content = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = SettingsPanel.CONTENT_PADDING.Left }, Children = new Drawable[] { - content = new Container + background = new FormControlBackground(), + new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Masking = true, - CornerRadius = padding, - EdgeEffect = new EdgeEffectParameters + Direction = FillDirection.Vertical, + Spacing = new Vector2(spacing), + Padding = new MarginPadding { - Radius = 2, - Colour = colourProvider.Highlight1.Opacity(0), - Type = EdgeEffectType.Shadow, - Hollow = true, + Vertical = 5, + Left = 9, + Right = 5, }, Children = new Drawable[] { - new Box + new GridContainer { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background5, - }, - text = new OsuSpriteText - { - Text = Action.GetLocalisableDescription(), - Margin = new MarginPadding(1.5f * padding), - }, - buttons = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Spacing = new Vector2(-6, 0), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, size: 9), + new Dimension(GridSizeMode.AutoSize), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable?[] + { + caption = new FormFieldCaption + { + Caption = Action.GetLocalisableDescription(), + Margin = new MarginPadding { Vertical = 4 }, + }, + null, + buttons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(spacing), + }, + }, + }, }, cancelAndClearButtons = new FillFlowContainer { AutoSizeAxes = Axes.Both, - Padding = new MarginPadding(padding) { Top = height + padding * 2 }, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Alpha = 0, @@ -202,7 +211,6 @@ private void load(OverlayColourProvider colourProvider, AudioManager audioManage }, }, }, - new HoverClickSounds() } } } @@ -250,16 +258,22 @@ public void RestoreDefaults() protected override bool OnHover(HoverEvent e) { - content.FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); - + updateState(); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - content.FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); - base.OnHoverLost(e); + updateState(); + } + + private void updateState() + { + if (IsHovered) + background.VisualStyle = VisualStyle.Hovered; + else + background.VisualStyle = VisualStyle.Normal; } protected override bool OnClick(ClickEvent e) => true; From 497e15c9137f31f290447d8d4582ef8f2cb880b5 Mon Sep 17 00:00:00 2001 From: Krzysztof Gutkowski Date: Sat, 27 Jun 2026 01:30:41 +0200 Subject: [PATCH 2/3] Fix tests --- .../Settings/TestSceneKeyBindingPanel.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index d077294dd510..9b7431a1e9c1 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Graphics.Sprites; @@ -161,15 +162,15 @@ public void TestClearButtonOnBindings() AddStep("click first row with two bindings", () => { multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); - InputManager.MoveMouseTo(multiBindingRow.ChildrenOfType().First()); + InputManager.MoveMouseTo(multiBindingRow.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); clickClearButton(); AddAssert("first binding cleared", - () => multiBindingRow.ChildrenOfType().First().Text.Text, - () => Is.EqualTo(InputSettingsStrings.ActionHasNoKeyBinding)); + () => multiBindingRow.ChildrenOfType().First().Text.Text.ToString(), + () => Is.EqualTo(InputSettingsStrings.ActionHasNoKeyBinding.ToUpper().ToString())); AddStep("click second binding", () => { @@ -182,8 +183,8 @@ public void TestClearButtonOnBindings() clickClearButton(); AddAssert("second binding cleared", - () => multiBindingRow.ChildrenOfType().ElementAt(1).Text.Text, - () => Is.EqualTo(InputSettingsStrings.ActionHasNoKeyBinding)); + () => multiBindingRow.ChildrenOfType().ElementAt(1).Text.Text.ToString(), + () => Is.EqualTo(InputSettingsStrings.ActionHasNoKeyBinding.ToUpper().ToString())); void clickClearButton() { @@ -395,7 +396,7 @@ public void TestBindingConflictCausedByResetToDefaultOfSingleRow() scrollToAndStartBinding("Left (centre)"); AddStep("clear binding", () => { - var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == "Left (centre)")); + var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Caption.ToString() == "Left (centre)")); row.ChildrenOfType().Single().TriggerClick(); }); scrollToAndStartBinding("Left (rim)"); @@ -403,7 +404,7 @@ public void TestBindingConflictCausedByResetToDefaultOfSingleRow() AddStep("reset Left (centre) to default", () => { - var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == "Left (centre)")); + var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Caption.ToString() == "Left (centre)")); row.ChildrenOfType().Single().TriggerClick(); }); @@ -459,7 +460,7 @@ public void TestResettingRowCannotConflictWithItself() AddStep("revert row to default", () => { - var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == "Left (centre)")); + var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Caption.ToString() == "Left (centre)")); InputManager.MoveMouseTo(row.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); @@ -471,7 +472,7 @@ private void clearBinding() { AddStep("clear binding", () => { - var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == "Left (centre)")); + var row = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Caption.ToString() == "Left (centre)")); row.ChildrenOfType().Single().TriggerClick(); }); } @@ -480,11 +481,11 @@ private void checkBinding(string name, string keyName) { AddAssert($"Check {name} is bound to {keyName}", () => { - var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == name)); + var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Caption.ToString() == name)); var firstButton = firstRow.ChildrenOfType().First(); return firstButton.Text.Text.ToString(); - }, () => Is.EqualTo(keyName)); + }, () => Is.EqualTo(keyName.ToUpperInvariant())); } private void scrollToAndStartBinding(string name, int bindingIndex = 0) @@ -493,7 +494,7 @@ private void scrollToAndStartBinding(string name, int bindingIndex = 0) AddStep($"Scroll to {name}", () => { - var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Text.ToString() == name)); + var firstRow = panel.ChildrenOfType().First(r => r.ChildrenOfType().Any(s => s.Caption.ToString() == name)); targetButton = firstRow.ChildrenOfType().ElementAt(bindingIndex); panel.ChildrenOfType().First().ScrollTo(targetButton); From 0716504697037ea2eb5c81cc265ce5f40b731a84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Jun 2026 01:43:41 +0900 Subject: [PATCH 3/3] Adjust visual and layout slightly --- .../Settings/TestSceneKeyBindingPanel.cs | 6 ++++ osu.Game/Localisation/InputSettingsStrings.cs | 5 +++ .../Sections/Input/KeyBindingRow.KeyButton.cs | 24 ++++++++++---- .../Settings/Sections/Input/KeyBindingRow.cs | 33 +++++++++++++++---- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 9b7431a1e9c1..fb3e33dc53cf 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -45,6 +45,12 @@ public void SetUpSteps() AddWaitStep("wait for scroll", 5); } + [Test] + public void TestBasic() + { + AddStep("do nothing", () => { }); + } + [Test] public void TestBindingTwoNonModifiers() { diff --git a/osu.Game/Localisation/InputSettingsStrings.cs b/osu.Game/Localisation/InputSettingsStrings.cs index 661d583e8f34..1329824305a7 100644 --- a/osu.Game/Localisation/InputSettingsStrings.cs +++ b/osu.Game/Localisation/InputSettingsStrings.cs @@ -95,6 +95,11 @@ public static class InputSettingsStrings /// public static LocalisableString ActionHasNoKeyBinding => new TranslatableString(getKey(@"action_has_no_key_binding"), @"(none)"); + /// + /// "Press a key combination to set a new binding..." + /// + public static LocalisableString PendingBinding => new TranslatableString(getKey(@"press_akey_combination_to"), @"Press a key combination to set a new binding..."); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs index d0636e03e165..d738f95b4f44 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.KeyButton.cs @@ -21,6 +21,7 @@ using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Localisation; +using osuTK; using osuTK.Graphics; namespace osu.Game.Overlays.Settings.Sections.Input @@ -59,14 +60,15 @@ public KeyButton() { AutoSizeAxes = Axes.Both; Masking = true; - CornerRadius = 5; + CornerRadius = 3f; + CornerExponent = 2.5f; Children = new Drawable[] { new Container { AlwaysPresent = true, - Width = 60, + Width = 65, AutoSizeAxes = Axes.Y, }, box = new Box @@ -75,8 +77,9 @@ public KeyButton() }, Text = new OsuSpriteText { - Font = OsuFont.Default.With(size: 14, weight: FontWeight.Bold), - Margin = new MarginPadding(4), + Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), + Spacing = new Vector2(1, 0), + Margin = new MarginPadding { Horizontal = 10, Vertical = 5 }, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, @@ -120,15 +123,22 @@ protected override void OnHoverLost(HoverLostEvent e) private void updateHoverState() { + const float transition_time = 120; + if (isBinding) { - box.FadeColour(colourProvider.Light2, transition_time, Easing.OutQuint); + box.FadeColour(colourProvider.Light3, transition_time, Easing.OutQuint); + Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); + } + else if (IsHovered) + { + box.FadeColour(colourProvider.Light4, transition_time, Easing.OutQuint); Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint); } else { - box.FadeColour(IsHovered ? colourProvider.Light4 : colourProvider.Background6, transition_time, Easing.OutQuint); - Text.FadeColour(IsHovered ? Color4.Black : colourProvider.Content1, transition_time, Easing.OutQuint); + box.FadeColour(colourProvider.Background5, transition_time * 2, Easing.OutQuint); + Text.FadeColour(colourProvider.Content1, transition_time * 2, Easing.OutQuint); } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index 7f8a3a3effa7..1493980f1ba9 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -18,13 +18,16 @@ using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Input.Bindings; -using osu.Game.Resources.Localisation.Web; +using osu.Game.Localisation; using osu.Game.Rulesets; using osuTK; using osuTK.Input; +using CommonStrings = osu.Game.Resources.Localisation.Web.CommonStrings; namespace osu.Game.Overlays.Settings.Sections.Input { @@ -99,7 +102,6 @@ public bool MatchingFilter private Sample?[]? keypressSamples; - private const float transition_time = 150; private const float spacing = 5; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => @@ -119,6 +121,8 @@ public KeyBindingRow(object action) AutoSizeAxes = Axes.Y; } + private OsuSpriteText pendingBindingText = null!; + [BackgroundDependencyLoader] private void load(AudioManager audioManager) { @@ -190,23 +194,38 @@ private void load(AudioManager audioManager) }, cancelAndClearButtons = new FillFlowContainer { - AutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Alpha = 0, Spacing = new Vector2(5), + Padding = new MarginPadding(5) { Top = 0 }, Children = new Drawable[] { + pendingBindingText = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Alpha = 0.4f, + Text = InputSettingsStrings.PendingBinding, + Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), + }, new RoundedButton { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, Text = CommonStrings.ButtonsCancel, - Size = new Vector2(80, 20), + Size = new Vector2(120, 30), Action = () => finalise(false) }, new DangerousRoundedButton { - Text = CommonStrings.ButtonsClear, - Size = new Vector2(80, 20), + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = "Clear binding", + Size = new Vector2(120, 30), Action = clear }, }, @@ -513,6 +532,8 @@ protected override void OnFocus(FocusEvent e) cancelAndClearButtons.FadeIn(300, Easing.OutQuint); cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; + pendingBindingText.FadeTo(1, 500).Then().FadeTo(0.4f, 500).Loop(); + updateBindTarget(); base.OnFocus(e); }