// Copyright (c) 2007-2017 ppy Pty Ltd . // 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 { /// /// A container that can scroll to each section inside it. /// public class SectionsContainer : Container { private Drawable expandableHeader, fixedHeader; private ScrollContainer scrollContainer; public Drawable ExpandableHeader { get { return expandableHeader; } set { if (value == expandableHeader) return; if (expandableHeader != null) Remove(expandableHeader); expandableHeader = value; expandableHeader.Depth = float.MinValue; Add(expandableHeader); updateSectionMargin(); } } public Drawable FixedHeader { get { return fixedHeader; } set { if (value == fixedHeader) return; if (fixedHeader != null) Remove(fixedHeader); fixedHeader = value; fixedHeader.Depth = float.MinValue / 2; Add(fixedHeader); updateSectionMargin(); } } public Bindable SelectedSection { get; } = new Bindable(); public void ScrollToSection(Drawable section) => scrollContainer.ScrollIntoView(section); private List sections = new List(); public IEnumerable Sections { get { return sections; } set { if (value == sections) return; foreach (var section in sections) scrollContainer.Remove(section); sections = value.ToList(); if (sections.Count == 0) return; originalSectionMargin = sections[0].Margin; updateSectionMargin(); scrollContainer.Add(sections); SelectedSection.Value = sections[0]; } } private MarginPadding originalSectionMargin; private void updateSectionMargin() { if (sections.Count == 0) return; var newMargin = originalSectionMargin; newMargin.Top += ExpandableHeader?.Height ?? 0 + FixedHeader?.Height ?? 0; sections[0].Margin = newMargin; } public SectionsContainer() { Add(scrollContainer = new ScrollContainer { RelativeSizeAxes = Axes.Both }); } float lastKnownScroll; protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); if (expandableHeader == null) return; float position = scrollContainer.Current; float offset = Math.Max(expandableHeader.Height, position); expandableHeader.Y = -offset; fixedHeader.Y = -offset + expandableHeader.Height; float currentScroll = scrollContainer.Current; if (currentScroll != lastKnownScroll) { lastKnownScroll = currentScroll; 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; } } } }