/// <summary> /// Adds a <see cref="ScoreInfo"/> to this list. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to add.</param> /// <param name="isNewLocalScore">Whether this is a score that has just been achieved locally. Controls whether flair is added to the display or not.</param> public ScorePanel AddScore(ScoreInfo score, bool isNewLocalScore = false) { var panel = new ScorePanel(score, isNewLocalScore) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, PostExpandAction = () => PostExpandAction?.Invoke() }.With(p => { p.StateChanged += s => { if (s == PanelState.Expanded) { SelectedScore.Value = p.Score; } }; }); var trackingContainer = panel.CreateTrackingContainer().With(d => { d.Anchor = Anchor.Centre; d.Origin = Anchor.Centre; d.Hide(); }); flow.Add(trackingContainer); if (IsLoaded) { displayScore(trackingContainer); } return(panel); }
/// <summary> /// Brings a <see cref="ScoreInfo"/> to the centre of the screen and expands it. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to present.</param> private void selectedScoreChanged(ValueChangedEvent <ScoreInfo> score) { // Contract the old panel. foreach (var p in flow.Where(p => p.Score == score.OldValue)) { p.State = PanelState.Contracted; p.Margin = new MarginPadding(); } // Find the panel corresponding to the new score. expandedPanel = flow.SingleOrDefault(p => p.Score == score.NewValue); // handle horizontal scroll only when not hovering the expanded panel. scroll.HandleScroll = () => expandedPanel?.IsHovered != true; if (expandedPanel == null) { return; } // Expand the new panel. expandedPanel.State = PanelState.Expanded; expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; // Scroll to the new panel. This is done manually since we need: // 1) To scroll after the scroll container's visible range is updated. // 2) To account for the centre anchor/origins of panels. // In the end, it's easier to compute the scroll position manually. float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); scroll.ScrollTo(scrollOffset); }
/// <summary> /// Brings a <see cref="ScoreInfo"/> to the centre of the screen and expands it. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to present.</param> private void selectedScoreChanged(ValueChangedEvent <ScoreInfo> score) { // Contract the old panel. foreach (var t in flow.Where(t => t.Panel.Score == score.OldValue)) { t.Panel.State = PanelState.Contracted; t.Margin = new MarginPadding(); } // Find the panel corresponding to the new score. var expandedTrackingComponent = flow.SingleOrDefault(t => t.Panel.Score == score.NewValue); expandedPanel = expandedTrackingComponent?.Panel; if (expandedPanel == null) { return; } Debug.Assert(expandedTrackingComponent != null); // Expand the new panel. expandedTrackingComponent.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; expandedPanel.State = PanelState.Expanded; // Scroll to the new panel. This is done manually since we need: // 1) To scroll after the scroll container's visible range is updated. // 2) To account for the centre anchor/origins of panels. // In the end, it's easier to compute the scroll position manually. float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); scroll.ScrollTo(scrollOffset); }
private void onStatisticsStateChanged(ValueChangedEvent <Visibility> state) { if (state.NewValue == Visibility.Visible) { // Detach the panel in its original location, and move into the desired location in the local container. var expandedPanel = ScorePanelList.GetPanelForScore(SelectedScore.Value); var screenSpacePos = expandedPanel.ScreenSpaceDrawQuad.TopLeft; // Detach and move into the local container. ScorePanelList.Detach(expandedPanel); detachedPanelContainer.Add(expandedPanel); // Move into its original location in the local container first, then to the final location. var origLocation = detachedPanelContainer.ToLocalSpace(screenSpacePos).X; expandedPanel.MoveToX(origLocation) .Then() .MoveToX(StatisticsPanel.SIDE_PADDING, 150, Easing.OutQuint); // Hide contracted panels. foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) { contracted.FadeOut(150, Easing.OutQuint); } ScorePanelList.HandleInput = false; // Dim background. ApplyToBackground(b => b.FadeTo(0.1f, 150)); detachedPanel = expandedPanel; } else if (detachedPanel != null) { var screenSpacePos = detachedPanel.ScreenSpaceDrawQuad.TopLeft; // Remove from the local container and re-attach. detachedPanelContainer.Remove(detachedPanel); ScorePanelList.Attach(detachedPanel); // Move into its original location in the attached container first, then to the final location. var origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos); detachedPanel.MoveTo(origLocation) .Then() .MoveTo(new Vector2(0, origLocation.Y), 150, Easing.OutQuint); // Show contracted panels. foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) { contracted.FadeIn(150, Easing.OutQuint); } ScorePanelList.HandleInput = true; // Un-dim background. ApplyToBackground(b => b.FadeTo(0.5f, 150)); detachedPanel = null; } }
/// <summary> /// Adds a <see cref="ScoreInfo"/> to this list. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to add.</param> /// <param name="isNewLocalScore">Whether this is a score that has just been achieved locally. Controls whether flair is added to the display or not.</param> public ScorePanel AddScore(ScoreInfo score, bool isNewLocalScore = false) { var panel = new ScorePanel(score, isNewLocalScore) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, PostExpandAction = () => PostExpandAction?.Invoke() }.With(p => { p.StateChanged += s => { if (s == PanelState.Expanded) { SelectedScore.Value = p.Score; } }; }); flow.Add(panel.CreateTrackingContainer().With(d => { d.Anchor = Anchor.Centre; d.Origin = Anchor.Centre; })); if (IsLoaded) { if (SelectedScore.Value == score) { SelectedScore.TriggerChange(); } else { // We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done. // But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel. if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score)) { // A somewhat hacky property is used here because we need to: // 1) Scroll after the scroll container's visible range is updated. // 2) Scroll before the scroll container's scroll position is updated. // Without this, we would have a 1-frame positioning error which looks very jarring. scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing; } } } return(panel); }
/// <summary> /// Brings a <see cref="ScoreInfo"/> to the centre of the screen and expands it. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to present.</param> private void selectedScoreChanged(ValueChangedEvent <ScoreInfo> score) { // avoid contracting panels unnecessarily when TriggerChange is fired manually. if (score.OldValue != null && !score.OldValue.Equals(score.NewValue)) { // Contract the old panel. foreach (var t in flow.Where(t => t.Panel.Score.Equals(score.OldValue))) { t.Panel.State = PanelState.Contracted; t.Margin = new MarginPadding(); } } // Find the panel corresponding to the new score. var expandedTrackingComponent = flow.SingleOrDefault(t => t.Panel.Score.Equals(score.NewValue)); expandedPanel = expandedTrackingComponent?.Panel; if (expandedPanel == null) { return; } Debug.Assert(expandedTrackingComponent != null); // Expand the new panel. expandedTrackingComponent.Margin = new MarginPadding { Horizontal = expanded_panel_spacing }; expandedPanel.State = PanelState.Expanded; // requires schedule after children to ensure the flow (and thus ScrollContainer's ScrollableExtent) has been updated. ScheduleAfterChildren(() => { // Scroll to the new panel. This is done manually since we need: // 1) To scroll after the scroll container's visible range is updated. // 2) To account for the centre anchor/origins of panels. // In the end, it's easier to compute the scroll position manually. float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing); scroll.ScrollTo(scrollOffset); }); }
internal ScorePanelTrackingContainer(ScorePanel panel) { Panel = panel; Attach(); }