1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 23:27:25 +08:00

Merge branch 'master' into mania-hitobject-object-conversion

This commit is contained in:
Dean Herbert 2017-05-22 21:12:00 +09:00 committed by GitHub
commit 51728e48be
18 changed files with 381 additions and 155 deletions

View File

@ -140,6 +140,26 @@ namespace osu.Game.Rulesets.Mania.Judgements
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
}
/// <summary>
/// Retrieves the hit result for a time offset.
/// </summary>
/// <param name="hitOffset">The time offset.</param>
/// <returns>The hit result, or null if the time offset results in a miss.</returns>
public ManiaHitResult? ResultFor(double hitOffset)
{
if (hitOffset <= Perfect / 2)
return ManiaHitResult.Perfect;
if (hitOffset <= Great / 2)
return ManiaHitResult.Great;
if (hitOffset <= Good / 2)
return ManiaHitResult.Good;
if (hitOffset <= Ok / 2)
return ManiaHitResult.Ok;
if (hitOffset <= Bad / 2)
return ManiaHitResult.Bad;
return null;
}
/// <summary>
/// Constructs new hit windows which have been multiplied by a value.
/// </summary>

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Rulesets.Mania.Judgements
{
public enum ManiaHitResult
{
[Description("PERFECT")]
Perfect,
[Description("GREAT")]
Great,
[Description("GOOD")]
Good,
[Description("OK")]
Ok,
[Description("BAD")]
Bad
}
}

View File

@ -10,5 +10,10 @@ namespace osu.Game.Rulesets.Mania.Judgements
public override string ResultString => string.Empty;
public override string MaxResultString => string.Empty;
/// <summary>
/// The hit result.
/// </summary>
public ManiaHitResult ManiaResult;
}
}

View File

@ -5,6 +5,8 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using OpenTK.Input;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
@ -14,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly BodyPiece bodyPiece;
private readonly NotePiece tailPiece;
public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
public DrawableHoldNote(HoldNote hitObject, Bindable<Key> key = null)
: base(hitObject, key)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;

View File

@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
@ -11,13 +13,21 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject, ManiaJudgement>
where TObject : ManiaHitObject
{
/// <summary>
/// The key that will trigger input for this hit object.
/// </summary>
protected Bindable<Key> Key { get; private set; } = new Bindable<Key>();
public new TObject HitObject;
protected DrawableManiaHitObject(TObject hitObject)
protected DrawableManiaHitObject(TObject hitObject, Bindable<Key> key = null)
: base(hitObject)
{
HitObject = hitObject;
if (key != null)
Key.BindTo(key);
RelativePositionAxes = Axes.Y;
Y = (float)HitObject.StartTime;
}

View File

@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
@ -12,8 +14,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject)
: base(hitObject)
public DrawableNote(Note hitObject, Bindable<Key> key = null)
: base(hitObject, key)
{
RelativeSizeAxes = Axes.Both;
Height = 100;

View File

@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Objects
/// <summary>
/// The key-release hit windows for this hold note.
/// </summary>
protected HitWindows ReleaseHitWindows = new HitWindows();
public HitWindows ReleaseHitWindows { get; protected set; } = new HitWindows();
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Objects
/// <summary>
/// The key-press hit window for this note.
/// </summary>
protected HitWindows HitWindows = new HitWindows();
public HitWindows HitWindows { get; protected set; } = new HitWindows();
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{

View File

@ -22,5 +22,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
protected override void OnNewJudgement(ManiaJudgement judgement)
{
}
protected override void Reset()
{
base.Reset();
Health.Value = 1;
}
}
}

View File

@ -18,6 +18,8 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Beatmaps.Timing;
using System;
using osu.Framework.Configuration;
namespace osu.Game.Rulesets.Mania.UI
{
@ -33,7 +35,10 @@ namespace osu.Game.Rulesets.Mania.UI
private const float column_width = 45;
private const float special_column_width = 70;
public Key Key;
/// <summary>
/// The key that will trigger input actions for this column and hit objects contained inside it.
/// </summary>
public Bindable<Key> Key = new Bindable<Key>();
private readonly Box background;
private readonly Container hitTargetBar;
@ -95,6 +100,12 @@ namespace osu.Game.Rulesets.Mania.UI
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
},
// For column lighting, we need to capture input events before the notes
new InputTarget
{
KeyDown = onKeyDown,
KeyUp = onKeyUp
}
}
},
new Container
@ -178,12 +189,9 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
{
ControlPointContainer.Add(hitObject);
}
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject) => ControlPointContainer.Add(hitObject);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
private bool onKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat)
return false;
@ -197,7 +205,7 @@ namespace osu.Game.Rulesets.Mania.UI
return false;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
private bool onKeyUp(InputState state, KeyUpEventArgs args)
{
if (args.Key == Key)
{
@ -207,5 +215,24 @@ namespace osu.Game.Rulesets.Mania.UI
return false;
}
/// <summary>
/// This is a simple container which delegates various input events that have to be captured before the notes.
/// </summary>
private class InputTarget : Container
{
public Func<InputState, KeyDownEventArgs, bool> KeyDown;
public Func<InputState, KeyUpEventArgs, bool> KeyUp;
public InputTarget()
{
RelativeSizeAxes = Axes.Both;
AlwaysPresent = true;
Alpha = 0;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false;
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false;
}
}
}

View File

@ -4,6 +4,8 @@
using System;
using System.Linq;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
@ -76,13 +78,19 @@ namespace osu.Game.Rulesets.Mania.UI
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
{
var maniaPlayfield = Playfield as ManiaPlayfield;
if (maniaPlayfield == null)
return null;
Bindable<Key> key = maniaPlayfield.Columns.ElementAt(h.Column).Key;
var holdNote = h as HoldNote;
if (holdNote != null)
return new DrawableHoldNote(holdNote);
return new DrawableHoldNote(holdNote, key);
var note = h as Note;
if (note != null)
return new DrawableNote(note);
return new DrawableNote(note, key);
return null;
}

View File

@ -55,7 +55,8 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public readonly FlowContainer<Column> Columns;
private readonly FlowContainer<Column> columns;
public IEnumerable<Column> Columns => columns.Children;
private readonly ControlPointContainer barlineContainer;
@ -87,7 +88,7 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
Columns = new FillFlowContainer<Column>
columns = new FillFlowContainer<Column>
{
Name = "Columns",
RelativeSizeAxes = Axes.Y,
@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.UI
};
for (int i = 0; i < columnCount; i++)
Columns.Add(new Column(timingChanges));
columns.Add(new Column(timingChanges));
TimeSpan = time_span_default;
}
@ -133,17 +134,17 @@ namespace osu.Game.Rulesets.Mania.UI
// Set the special column + colour + key
for (int i = 0; i < columnCount; i++)
{
Column column = Columns.Children.ElementAt(i);
Column column = Columns.ElementAt(i);
column.IsSpecial = isSpecialColumn(i);
if (!column.IsSpecial)
continue;
column.Key = Key.Space;
column.Key.Value = Key.Space;
column.AccentColour = specialColumnColour;
}
var nonSpecialColumns = Columns.Children.Where(c => !c.IsSpecial).ToList();
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
// We'll set the colours of the non-special columns in a separate loop, because the non-special
// column colours are mirrored across their centre and special styles mess with this
@ -162,11 +163,11 @@ namespace osu.Game.Rulesets.Mania.UI
int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
if (keyOffset >= 0 && keyOffset < default_keys.Length)
column.Key = default_keys[keyOffset];
column.Key.Value = default_keys[keyOffset];
else
// There is no default key defined for this column. Let's set this to Unknown for now
// however note that this will be gone after bindings are in place
column.Key = Key.Unknown;
column.Key.Value = Key.Unknown;
}
}
@ -189,7 +190,7 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.Children.ElementAt(h.HitObject.Column).Add(h);
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
@ -225,7 +226,7 @@ namespace osu.Game.Rulesets.Mania.UI
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
barlineContainer.TimeSpan = value;
Columns.Children.ForEach(c => c.ControlPointContainer.TimeSpan = value);
Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value);
}
}

View File

@ -57,6 +57,7 @@
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
<Compile Include="MathUtils\FastRandom.cs" />
<Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\ManiaHitResult.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />

View File

@ -0,0 +1,171 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// A container that can scroll to each section inside it.
/// </summary>
public class SectionsContainer : Container
{
private Drawable expandableHeader, fixedHeader, footer;
public readonly ScrollContainer ScrollContainer;
private readonly Container<Drawable> sectionsContainer;
public Drawable ExpandableHeader
{
get { return expandableHeader; }
set
{
if (value == expandableHeader) return;
if (expandableHeader != null)
Remove(expandableHeader);
expandableHeader = value;
if (value == null) return;
Add(expandableHeader);
lastKnownScroll = float.NaN;
}
}
public Drawable FixedHeader
{
get { return fixedHeader; }
set
{
if (value == fixedHeader) return;
if (fixedHeader != null)
Remove(fixedHeader);
fixedHeader = value;
if (value == null) return;
Add(fixedHeader);
lastKnownScroll = float.NaN;
}
}
public Drawable Footer
{
get { return footer; }
set
{
if (value == footer) return;
if (footer != null)
ScrollContainer.Remove(footer);
footer = value;
if (value == null) return;
footer.Anchor |= Anchor.y2;
footer.Origin |= Anchor.y2;
ScrollContainer.Add(footer);
lastKnownScroll = float.NaN;
}
}
public Bindable<Drawable> SelectedSection { get; } = new Bindable<Drawable>();
protected virtual Container<Drawable> CreateScrollContentContainer()
=> new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both
};
private List<Drawable> sections = new List<Drawable>();
public IEnumerable<Drawable> Sections
{
get { return sections; }
set
{
foreach (var section in sections)
sectionsContainer.Remove(section);
sections = value.ToList();
if (sections.Count == 0) return;
sectionsContainer.Add(sections);
SelectedSection.Value = sections[0];
lastKnownScroll = float.NaN;
}
}
private float headerHeight, footerHeight;
private readonly MarginPadding originalSectionsMargin;
private void updateSectionsMargin()
{
if (sections.Count == 0) return;
var newMargin = originalSectionsMargin;
newMargin.Top += headerHeight;
newMargin.Bottom += footerHeight;
sectionsContainer.Margin = newMargin;
}
public SectionsContainer()
{
Add(ScrollContainer = new ScrollContainer()
{
RelativeSizeAxes = Axes.Both,
Masking = false,
Children = new Drawable[] { sectionsContainer = CreateScrollContentContainer() }
});
originalSectionsMargin = sectionsContainer.Margin;
}
private float lastKnownScroll;
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0);
float footerH = Footer?.LayoutSize.Y ?? 0;
if (headerH != headerHeight || footerH != footerHeight)
{
headerHeight = headerH;
footerHeight = footerH;
updateSectionsMargin();
}
float currentScroll = Math.Max(0, ScrollContainer.Current);
if (currentScroll != lastKnownScroll)
{
lastKnownScroll = currentScroll;
if (expandableHeader != null && fixedHeader != null)
{
float offset = Math.Min(expandableHeader.LayoutSize.Y, currentScroll);
expandableHeader.Y = -offset;
fixedHeader.Y = -offset + expandableHeader.LayoutSize.Y;
}
Drawable bestMatch = null;
float minDiff = float.MaxValue;
foreach (var section in sections)
{
float diff = Math.Abs(ScrollContainer.GetChildPosInContent(section) - currentScroll);
if (diff < minDiff)
{
minDiff = diff;
bestMatch = section;
}
}
if (bestMatch != null)
SelectedSection.Value = bestMatch;
}
}
}
}

View File

@ -1,34 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Settings
{
public class SettingsHeader : Container
{
public SearchTextBox SearchTextBox;
private Box background;
private readonly Func<float> currentScrollOffset;
public Action Exit;
/// <param name="currentScrollOffset">A reference to the current scroll position of the ScrollContainer we are contained within.</param>
public SettingsHeader(Func<float> currentScrollOffset)
{
this.currentScrollOffset = currentScrollOffset;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
@ -37,11 +19,6 @@ namespace osu.Game.Overlays.Settings
Children = new Drawable[]
{
background = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
@ -53,7 +30,8 @@ namespace osu.Game.Overlays.Settings
{
Text = "settings",
TextSize = 40,
Margin = new MarginPadding {
Margin = new MarginPadding
{
Left = SettingsOverlay.CONTENT_MARGINS,
Top = Toolbar.Toolbar.TOOLTIP_HEIGHT
},
@ -63,45 +41,15 @@ namespace osu.Game.Overlays.Settings
Colour = colours.Pink,
Text = "Change the way osu! behaves",
TextSize = 18,
Margin = new MarginPadding {
Margin = new MarginPadding
{
Left = SettingsOverlay.CONTENT_MARGINS,
Bottom = 30
},
},
SearchTextBox = new SearchTextBox
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Width = 0.95f,
Margin = new MarginPadding {
Top = 20,
Bottom = 20
},
Exit = () => Exit(),
},
}
}
};
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// the point at which we will start anchoring to the top.
float anchorOffset = SearchTextBox.Y;
float scrollPosition = currentScrollOffset();
// we want to anchor the search field to the top of the screen when scrolling.
Margin = new MarginPadding { Top = Math.Max(0, scrollPosition - anchorOffset) };
// we don't want the header to scroll when scrolling beyond the upper extent.
Y = Math.Min(0, scrollPosition);
// we get darker as scroll progresses
background.Alpha = Math.Min(1, scrollPosition / anchorOffset) * 0.5f;
}
}
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings
private readonly Box backgroundBox;
private readonly Box selectionIndicator;
private readonly Container text;
public Action Action;
public Action<SettingsSection> Action;
private SettingsSection section;
public SettingsSection Section
@ -75,6 +75,7 @@ namespace osu.Game.Overlays.Settings
{
Width = Sidebar.DEFAULT_WIDTH,
RelativeSizeAxes = Axes.Y,
Colour = OsuColour.Gray(0.6f),
Children = new[]
{
headerText = new OsuSpriteText
@ -110,7 +111,7 @@ namespace osu.Game.Overlays.Settings
protected override bool OnClick(InputState state)
{
Action?.Invoke();
Action?.Invoke(section);
backgroundBox.FlashColour(Color4.White, 400);
return true;
}

View File

@ -7,10 +7,11 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using System;
using osu.Game.Overlays.Settings.Sections;
using osu.Framework.Input;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections;
namespace osu.Game.Overlays
{
@ -26,18 +27,13 @@ namespace osu.Game.Overlays
private const float sidebar_padding = 10;
private ScrollContainer scrollContainer;
private Sidebar sidebar;
private SidebarButton[] sidebarButtons;
private SettingsSection[] sections;
private SidebarButton selectedSidebarButton;
private SettingsHeader header;
private SettingsSectionsContainer sectionsContainer;
private SettingsFooter footer;
private SearchContainer searchContainer;
private float lastKnownScroll;
private SearchTextBox searchTextBox;
public SettingsOverlay()
{
@ -48,7 +44,7 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuGame game)
{
sections = new SettingsSection[]
var sections = new SettingsSection[]
{
new GeneralSection(),
new GraphicsSection(),
@ -68,27 +64,27 @@ namespace osu.Game.Overlays
Colour = Color4.Black,
Alpha = 0.6f,
},
scrollContainer = new ScrollContainer
sectionsContainer = new SettingsSectionsContainer
{
ScrollDraggerVisible = false,
RelativeSizeAxes = Axes.Y,
Width = width,
Margin = new MarginPadding { Left = SIDEBAR_WIDTH },
Children = new Drawable[]
ExpandableHeader = new SettingsHeader(),
FixedHeader = searchTextBox = new SearchTextBox
{
searchContainer = new SearchContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = sections,
},
footer = new SettingsFooter(),
header = new SettingsHeader(() => scrollContainer.Current)
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Width = 0.95f,
Margin = new MarginPadding
{
Top = 20,
Bottom = 20
},
Exit = Hide,
},
}
Sections = sections,
Footer = new SettingsFooter()
},
sidebar = new Sidebar
{
@ -96,84 +92,89 @@ namespace osu.Game.Overlays
Children = sidebarButtons = sections.Select(section =>
new SidebarButton
{
Selected = sections[0] == section,
Section = section,
Action = () => scrollContainer.ScrollIntoView(section),
Action = sectionsContainer.ScrollContainer.ScrollIntoView,
}
).ToArray()
}
};
header.SearchTextBox.Current.ValueChanged += newValue => searchContainer.SearchTerm = newValue;
selectedSidebarButton = sidebarButtons[0];
selectedSidebarButton.Selected = true;
scrollContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 };
}
protected override void UpdateAfterChildren()
sectionsContainer.SelectedSection.ValueChanged += section =>
{
base.UpdateAfterChildren();
selectedSidebarButton.Selected = false;
selectedSidebarButton = sidebarButtons.Single(b => b.Section == section);
selectedSidebarButton.Selected = true;
};
//we need to update these manually because we can't put the SettingsHeader inside the SearchContainer (due to its anchoring).
searchContainer.Y = header.DrawHeight;
footer.Y = searchContainer.Y + searchContainer.DrawHeight;
}
searchTextBox.Current.ValueChanged += newValue => sectionsContainer.SearchContainer.SearchTerm = newValue;
protected override void Update()
{
base.Update();
float currentScroll = scrollContainer.Current;
if (currentScroll != lastKnownScroll)
{
lastKnownScroll = currentScroll;
SettingsSection bestCandidate = null;
float bestDistance = float.MaxValue;
foreach (SettingsSection section in sections)
{
float distance = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll);
if (distance < bestDistance)
{
bestDistance = distance;
bestCandidate = section;
}
}
var previous = sidebarButtons.SingleOrDefault(sb => sb.Selected);
var next = sidebarButtons.SingleOrDefault(sb => sb.Section == bestCandidate);
if (previous != null) previous.Selected = false;
if (next != null) next.Selected = true;
}
sectionsContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 };
}
protected override void PopIn()
{
base.PopIn();
scrollContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
sectionsContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
sidebar.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
FadeTo(1, TRANSITION_LENGTH / 2);
header.SearchTextBox.HoldFocus = true;
searchTextBox.HoldFocus = true;
}
protected override void PopOut()
{
base.PopOut();
scrollContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint);
sectionsContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint);
sidebar.MoveToX(-SIDEBAR_WIDTH, TRANSITION_LENGTH, EasingTypes.OutQuint);
FadeTo(0, TRANSITION_LENGTH / 2);
header.SearchTextBox.HoldFocus = false;
header.SearchTextBox.TriggerFocusLost();
searchTextBox.HoldFocus = false;
searchTextBox.TriggerFocusLost();
}
protected override bool OnFocus(InputState state)
{
header.SearchTextBox.TriggerFocus(state);
searchTextBox.TriggerFocus(state);
return false;
}
private class SettingsSectionsContainer : SectionsContainer
{
public SearchContainer SearchContainer;
private readonly Box headerBackground;
protected override Container<Drawable> CreateScrollContentContainer()
=> SearchContainer = new SearchContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
};
public SettingsSectionsContainer()
{
ScrollContainer.ScrollDraggerVisible = false;
Add(headerBackground = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.X
});
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// no null check because the usage of this class is strict
headerBackground.Height = ExpandableHeader.LayoutSize.Y + FixedHeader.LayoutSize.Y;
headerBackground.Y = ExpandableHeader.Y;
headerBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f;
}
}
}
}

View File

@ -81,6 +81,7 @@
<Compile Include="Overlays\Music\PlaylistItem.cs" />
<Compile Include="Overlays\Music\PlaylistList.cs" />
<Compile Include="Overlays\OnScreenDisplay.cs" />
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
<Compile Include="Overlays\Toolbar\ToolbarChatButton.cs" />