protected override void LoadComplete() { base.LoadComplete(); SelectedItems.BindTo(editor.SelectedComponents); // track each target container on the current screen. var targetContainers = target.ChildrenOfType <ISkinnableTarget>().ToArray(); if (targetContainers.Length == 0) { string targetScreen = target.ChildrenOfType <Screen>().LastOrDefault()?.GetType().Name ?? "this screen"; AddInternal(new ScreenWhiteBox.UnderConstructionMessage(targetScreen, "doesn't support skin customisation just yet.")); return; } foreach (var targetContainer in targetContainers) { var bindableList = new BindableList <ISkinnableDrawable> { BindTarget = targetContainer.Components }; bindableList.BindCollectionChanged(componentsChanged, true); targetComponents.Add(bindableList); } }
private void load() { if (beatmap?.PlayableBeatmap is KaraokeBeatmap karaokeBeatmap) { Languages.AddRange(karaokeBeatmap.AvailableTranslates); Languages.BindCollectionChanged((a, b) => { karaokeBeatmap.AvailableTranslates = Languages.ToArray(); }); } }
protected sealed override void OnApply(HitObjectLifetimeEntry entry) { // LifetimeStart is already computed using HitObjectLifetimeEntry's InitialLifetimeOffset. // We override this with DHO's InitialLifetimeOffset for a non-pooled DHO. if (entry is SyntheticHitObjectEntry) LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; ensureEntryHasResult(); foreach (var h in HitObject.NestedHitObjects) { var pooledDrawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h, this); var drawableNested = pooledDrawableNested ?? CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); // Only invoke the event for non-pooled DHOs, otherwise the event will be fired by the playfield. if (pooledDrawableNested == null) OnNestedDrawableCreated?.Invoke(drawableNested); drawableNested.OnNewResult += onNewResult; drawableNested.OnRevertResult += onRevertResult; drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState; // This is only necessary for non-pooled DHOs. For pooled DHOs, this is handled inside GetPooledDrawableRepresentation(). // Must be done before the nested DHO is added to occur before the nested Apply()! drawableNested.ParentHitObject = this; nestedHitObjects.Add(drawableNested); AddNestedHitObject(drawableNested); } StartTimeBindable.BindTo(HitObject.StartTimeBindable); StartTimeBindable.BindValueChanged(onStartTimeChanged); if (HitObject is IHasComboInformation combo) comboIndexBindable.BindTo(combo.ComboIndexBindable); samplesBindable.BindTo(HitObject.SamplesBindable); samplesBindable.BindCollectionChanged(onSamplesChanged, true); HitObject.DefaultsApplied += onDefaultsApplied; OnApply(); HitObjectApplied?.Invoke(this); // If not loaded, the state update happens in LoadComplete(). if (IsLoaded) { if (Result.IsHit) updateState(ArmedState.Hit, true); else if (Result.HasResult) updateState(ArmedState.Miss, true); else updateState(ArmedState.Idle, true); } }
public void TestBindCollectionChangedWithRunImmediately() { var list = new BindableList <int>(); NotifyCollectionChangedEventArgs triggeredArgs = null; list.BindCollectionChanged((_, args) => triggeredArgs = args, true); Assert.That(triggeredArgs.Action, Is.EqualTo(NotifyCollectionChangedAction.Add)); Assert.That(triggeredArgs.NewItems, Is.EquivalentTo(list)); }
public EditorBeatmapSkin(Skin skin) { Skin = skin; ComboColours = new BindableList <Colour4>(); if (Skin.Configuration.ComboColours != null) { ComboColours.AddRange(Skin.Configuration.ComboColours.Select(c => (Colour4)c)); } ComboColours.BindCollectionChanged((_, __) => updateColours()); }
public void TestBindCollectionChangedWithoutRunningImmediately() { var list = new BindableList <int> { 1 }; NotifyCollectionChangedEventArgs triggeredArgs = null; list.BindCollectionChanged((_, args) => triggeredArgs = args); Assert.That(triggeredArgs, Is.Null); }
private void load(LyricEditorColourProvider colourProvider, ILyricEditorState state) { hoveredBackground.Colour = colourHover = colourProvider.Background1(LyricEditorMode.CreateTimeTag); colourSelected = colourProvider.Colour3(LyricEditorMode.CreateTimeTag); // update selected state by bindable. selectedTimeTags = state.SelectedTimeTags.GetBoundCopy(); selectedTimeTags.BindCollectionChanged((a, b) => { selected = selectedTimeTags.Contains(timeTag); updateState(); }); Action = () => { // navigate to current lyric. switch (state.Mode) { case LyricEditorMode.CreateTimeTag: state.BindableCaretPosition.Value = new TimeTagIndexCaretPosition(lyric, timeTag?.Index ?? new TextIndex()); break; case LyricEditorMode.RecordTimeTag: state.BindableCaretPosition.Value = new TimeTagCaretPosition(lyric, timeTag); break; case LyricEditorMode.AdjustTimeTag: state.BindableCaretPosition.Value = new NavigateCaretPosition(lyric); break; default: throw new IndexOutOfRangeException(nameof(state.Mode)); } // set current time-tag as selected. selectedTimeTags.Clear(); if (timeTag == null) { return; } // select time-tag is not null. selectedTimeTags.Add(timeTag); if (timeTag.Time == null) { return; } // seek to target time-tag time if time-tag has time. clock.Seek(timeTag.Time.Value); }; }
private void load() { if (beatmap?.PlayableBeatmap is KaraokeBeatmap karaokeBeatmap) { Singers.AddRange(karaokeBeatmap.Singers); // should write-back if singer changed. Singers.BindCollectionChanged((a, b) => { karaokeBeatmap.Singers = Singers.ToArray(); }); } }
protected override void LoadComplete() { controlPoints.BindCollectionChanged((_, __) => { var copy = controlPoints.ToArray(); if (copy.Length != 3) { return; } path.Vertices = PathApproximator.ApproximateCircularArc(copy); var bounds = PathApproximator.CircularArcBoundingBox(copy); boundingBox.Size = bounds.Size; // because SmoothPath's bounding box is not exact, // adjust our box's anchoring so that it's always aligned correctly to encapsulate the arc. Anchor anchor = 0; if (path.Vertices.All(v => v.X < 0)) { anchor |= Anchor.x0; } else if (path.Vertices.All(v => v.X > 0)) { anchor |= Anchor.x2; } else { anchor |= Anchor.x1; } if (path.Vertices.All(v => v.Y < 0)) { anchor |= Anchor.y0; } else if (path.Vertices.All(v => v.Y > 0)) { anchor |= Anchor.y2; } else { anchor |= Anchor.y1; } boundingBox.Anchor = boundingBox.Origin = anchor; }); }
protected override void LoadComplete() { base.LoadComplete(); controlPoints = (BindableList <ControlPoint>)Group.ControlPoints.GetBoundCopy(); controlPoints.BindCollectionChanged((_, __) => { if (controlPoints.Count == 0) { Colour = Color4.Transparent; return; } Colour = controlPoints.Any(c => c is TimingControlPoint) ? colours.YellowDark : colours.Green; }, true); }
/// <summary> /// Applies a new <see cref="HitObject"/> to be represented by this <see cref="DrawableHitObject"/>. /// </summary> /// <param name="hitObject">The <see cref="HitObject"/> to apply.</param> public void Apply(HitObject hitObject) { free(); HitObject = hitObject ?? throw new InvalidOperationException($"Cannot apply a null {nameof(HitObject)}."); // Ensure this DHO has a result. Result ??= CreateResult(HitObject.CreateJudgement()) ?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); foreach (var h in HitObject.NestedHitObjects) { var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); drawableNested.OnNewResult += onNewResult; drawableNested.OnRevertResult += onRevertResult; drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState; nestedHitObjects.Value.Add(drawableNested); AddNestedHitObject(drawableNested); } startTimeBindable.BindTo(HitObject.StartTimeBindable); if (HitObject is IHasComboInformation combo) { comboIndexBindable.BindTo(combo.ComboIndexBindable); } samplesBindable.BindTo(HitObject.SamplesBindable); samplesBindable.BindCollectionChanged(onSamplesChanged, true); HitObject.DefaultsApplied += onDefaultsApplied; OnApply(hitObject); // If not loaded, the state update happens in LoadComplete(). Otherwise, the update is scheduled to allow for lifetime updates. if (IsLoaded) { Schedule(() => updateState(ArmedState.Idle, true)); } hasHitObjectApplied = true; }
protected override void LoadComplete() { base.LoadComplete(); // BindableList handles binding in a really bad way (Clear then AddRange) so we need to do this manually.. foreach (int userId in playingUsers) { if (!multiplayerClient.CurrentMatchPlayingUserIds.Contains(userId)) { usersChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { userId })); } } playingUsers.BindTo(multiplayerClient.CurrentMatchPlayingUserIds); playingUsers.BindCollectionChanged(usersChanged); // this leaderboard should be guaranteed to be completely loaded before the gameplay starts (is a prerequisite in MultiplayerPlayer). spectatorClient.OnNewFrames += handleIncomingFrames; }
private void load() { Dependencies.Cache(config = new OsuConfigManager(LocalStorage)); Dependencies.CacheAs(spectatorClient.Object); Dependencies.CacheAs(multiplayerClient.Object); // To emulate `MultiplayerClient.CurrentMatchPlayingUserIds` we need a bindable list of *only IDs*. // This tracks the list of users 1:1. MultiplayerUsers.BindCollectionChanged((c, e) => { switch (e.Action) { case NotifyCollectionChangedAction.Add: Debug.Assert(e.NewItems != null); foreach (var user in e.NewItems.OfType <MultiplayerRoomUser>()) { multiplayerUserIds.Add(user.UserID); } break; case NotifyCollectionChangedAction.Remove: Debug.Assert(e.OldItems != null); foreach (var user in e.OldItems.OfType <MultiplayerRoomUser>()) { multiplayerUserIds.Remove(user.UserID); } break; case NotifyCollectionChangedAction.Reset: multiplayerUserIds.Clear(); break; } }); multiplayerClient.SetupGet(c => c.CurrentMatchPlayingUserIds) .Returns(() => multiplayerUserIds); spectatorClient.SetupGet(c => c.WatchedUserStates) .Returns(() => watchedUserStates); }
protected override void LoadComplete() { base.LoadComplete(); Show(); // as long as the skin editor is loaded, let's make sure we can modify the current skin. currentSkin = skins.CurrentSkin.GetBoundCopy(); // schedule ensures this only happens when the skin editor is visible. // also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types). // probably something which will be factored out in a future database refactor so not too concerning for now. currentSkin.BindValueChanged(skin => { hasBegunMutating = false; Scheduler.AddOnce(skinChanged); }, true); SelectedComponents.BindCollectionChanged((_, __) => Scheduler.AddOnce(populateSettings), true); }
private void load() { Resources.AddStore(new DllResourceStore(@"GamesToGo.Game.dll")); Textures.AddStore(Host.CreateTextureLoaderStore(new OnlineStore())); Textures.AddStore(Host.CreateTextureLoaderStore(new StorageBackedResourceStore(store))); dependencies.CacheAs(this); base.Content.Add(content = new DrawSizePreservingFillContainer { TargetDrawSize = new Vector2(1080, 1920), Strategy = DrawSizePreservationStrategy.Minimum, }); content.Add(stack = new ScreenStack { RelativeSizeAxes = Axes.Both, Depth = 0, }); content.Add(api = new APIController()); dependencies.Cache(api); dependencies.Cache(stack); content.Add(infoOverlay = new SplashInfoOverlay(SplashPosition.Top, 150, 60) { Depth = -1 }); dependencies.Cache(infoOverlay); Invitations.BindCollectionChanged((_, e) => { switch (e.Action) { case NotifyCollectionChangedAction.Add: infoOverlay.Show(@$ "{e.NewItems.Cast<Invitation>().Last().Sender.Username} te ha invitado a jugar", Colour4.LightBlue); break; case NotifyCollectionChangedAction.Remove: break; } }); }
protected override void LoadComplete() { base.LoadComplete(); roomPlaylist.BindCollectionChanged((_, __) => InvalidateLayout()); }
private void load() { RelativeSizeAxes = Axes.Both; Children = new Drawable[] { new GridContainer { RelativeSizeAxes = Axes.Both, RowDimensions = new [] { new Dimension(), new Dimension(GridSizeMode.Absolute, 50), }, ColumnDimensions = new [] { new Dimension(), }, Content = new [] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, ClampExtension = 30, Child = turnsFillFlow = new FillFlowContainer <ActionDescriptor> { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, }, }, }, }, new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { new ActionTypeListing { OnSelection = selected => TurnActions.Add(Activator.CreateInstance(selected.GetType()) as EventAction), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, }, }, }, }, }, }, }; TurnActions.BindCollectionChanged((_, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: checkAdded(args.NewItems.Cast <EventAction>()); break; case NotifyCollectionChangedAction.Remove: checkRemoved(args.OldItems.Cast <EventAction>()); break; } }, true); }
private void load() { RelativeSizeAxes = Axes.Both; Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = Colour4.MediumPurple.Opacity(0.3f), }, new GridContainer { RelativeSizeAxes = Axes.Both, RowDimensions = new [] { new Dimension(), new Dimension(GridSizeMode.Absolute, 50), }, ColumnDimensions = new [] { new Dimension(), }, Content = new [] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, ClampExtension = 30, Child = conditionalFillFlow = new FillFlowContainer <ActionDescriptor> { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, }, }, }, }, new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = Colour4.Beige, }, new GamesToGoButton { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Size = new Vector2(200, 35), Text = @"Añadir nuevo", Action = () => victoryActions.Add(Activator.CreateInstance(typeof(PlayerWinsAction)) as EventAction), }, }, }, }, }, }, }; victoryActions.BindTo(project.VictoryConditions); victoryActions.BindCollectionChanged((_, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: checkAdded(args.NewItems.Cast <EventAction>()); break; case NotifyCollectionChangedAction.Remove: checkRemoved(args.OldItems.Cast <EventAction>()); break; } }, true); }
static GlobalStatistics() { statistics.BindCollectionChanged((o, e) => StatisticsChanged?.Invoke(o, e)); }
public LoungeBackgroundScreen() { SelectedRoom.BindValueChanged(onSelectedRoomChanged); playlist.BindCollectionChanged((_, __) => PlaylistItem = playlist.FirstOrDefault()); }
protected override void LoadComplete() { base.LoadComplete(); registeredActions.BindCollectionChanged((x, y) => rotatePlayer()); }
private void load() { RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Both; X = -1; InternalChildren = new Drawable[] { new InvitationsUpdater(), hideButton = new SurfaceButton { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, BackgroundColour = ColourInfo.GradientHorizontal(Colour4.Black.Opacity(0.6f).ToLinear(), Colour4.Black.Opacity(0f).ToLinear()), Width = 0.2f, Action = Hide, }, new Box { Anchor = Anchor.TopLeft, Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Both, Colour = Colour4.Black.Opacity(0.6f).ToLinear(), }, new Container { RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both, Width = .8f, BorderColour = Colour4.Black, BorderThickness = 3.5f, Masking = true, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = new Colour4(106, 100, 104, 255) }, new GridContainer { RelativeSizeAxes = Axes.Both, RowDimensions = new[] { new Dimension(GridSizeMode.Relative, .9f), new Dimension() }, ColumnDimensions = new[] { new Dimension() }, Content = new[] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Child = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, Children = new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Height = .3f, BorderColour = Colour4.Black, BorderThickness = 3.5f, Masking = true, Padding = new MarginPadding { Top = 80, Left = 20, Right = 20 }, Child = new SurfaceButton { Action = profileScreen, Children = new Drawable[] { new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, Padding = new MarginPadding() { Bottom = 40 }, Children = new Drawable[] { new CircularContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, BorderColour = Colour4.Black, BorderThickness = 3.5f, Masking = true, Size = new Vector2(300, 300), Child = userImage = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Texture = textures.Get("Images/gtg"), }, }, username = new SpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Text = "Guest", Font = new FontUsage(size: 80) }, }, }, }, }, }, new Container { RelativeSizeAxes = Axes.Both, Height = .15f, BorderColour = Colour4.Black, BorderThickness = 3.5f, Masking = true, Padding = new MarginPadding(20), Child = new SurfaceButton { Action = () => invitesScreen(), Children = new Drawable[] { new FillFlowContainer { RelativeSizeAxes = Axes.Both, Spacing = new Vector2(30), Children = new Drawable[] { numberContainer = new Container { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = Colour4.LightBlue, }, invitationsNumber = new SpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = new FontUsage(size: 70) }, } }, new SpriteText { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Text = "Invitaciones", Font = new FontUsage(size: 80) }, } } }, } }, new Container { RelativeSizeAxes = Axes.Both, Height = .15f, BorderColour = Colour4.Black, BorderThickness = 3.5f, Masking = true, Padding = new MarginPadding(20), Child = new SurfaceButton { Action = logout, Children = new Drawable[] { new SpriteText { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Text = "Cerrar Sesión", Font = new FontUsage(size: 80) }, }, } }, }, }, }, }, new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, BorderColour = Colour4.Black, BorderThickness = 3.5f, Masking = true, Padding = new MarginPadding(20), Children = new Drawable[] { new SpriteText { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Text = "GamesToGo", Font = new FontUsage(size: 80) }, }, }, }, }, }, }, }, profileOverlay, invitationsOverlay }; invitations.BindTo(game.Invitations); invitations.BindCollectionChanged((_, __) => changeInvitationsNumber()); localUser.BindTo(api.LocalUser); localUser.BindValueChanged(_ => changeUserData(), true); }
/// <summary> /// Applies a new <see cref="HitObject"/> to be represented by this <see cref="DrawableHitObject"/>. /// </summary> /// <param name="hitObject">The <see cref="HitObject"/> to apply.</param> /// <param name="lifetimeEntry">The <see cref="HitObjectLifetimeEntry"/> controlling the lifetime of <paramref name="hitObject"/>.</param> public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry) { free(); HitObject = hitObject ?? throw new InvalidOperationException($"Cannot apply a null {nameof(HitObject)}."); this.lifetimeEntry = lifetimeEntry; if (lifetimeEntry != null) { // Transfer lifetime from the entry. LifetimeStart = lifetimeEntry.LifetimeStart; LifetimeEnd = lifetimeEntry.LifetimeEnd; // Copy any existing result from the entry (required for rewind / judgement revert). Result = lifetimeEntry.Result; } else { LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } // Ensure this DHO has a result. Result ??= CreateResult(HitObject.CreateJudgement()) ?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); // Copy back the result to the entry for potential future retrieval. if (lifetimeEntry != null) { lifetimeEntry.Result = Result; } foreach (var h in HitObject.NestedHitObjects) { var drawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h) ?? CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); drawableNested.OnNewResult += onNewResult; drawableNested.OnRevertResult += onRevertResult; drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState; nestedHitObjects.Value.Add(drawableNested); AddNestedHitObject(drawableNested); drawableNested.OnParentReceived(this); } StartTimeBindable.BindTo(HitObject.StartTimeBindable); StartTimeBindable.BindValueChanged(onStartTimeChanged); if (HitObject is IHasComboInformation combo) { comboIndexBindable.BindTo(combo.ComboIndexBindable); } samplesBindable.BindTo(HitObject.SamplesBindable); samplesBindable.BindCollectionChanged(onSamplesChanged, true); HitObject.DefaultsApplied += onDefaultsApplied; OnApply(hitObject); HitObjectApplied?.Invoke(this); // If not loaded, the state update happens in LoadComplete(). Otherwise, the update is scheduled to allow for lifetime updates. if (IsLoaded) { Schedule(() => updateState(ArmedState.Idle, true)); } hasHitObjectApplied = true; }
/// <summary> /// Applies a new <see cref="HitObject"/> to be represented by this <see cref="DrawableHitObject"/>. /// </summary> /// <param name="hitObject">The <see cref="HitObject"/> to apply.</param> /// <param name="lifetimeEntry">The <see cref="HitObjectLifetimeEntry"/> controlling the lifetime of <paramref name="hitObject"/>.</param> public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry) { free(); HitObject = hitObject ?? throw new InvalidOperationException($"Cannot apply a null {nameof(HitObject)}."); this.lifetimeEntry = lifetimeEntry; if (lifetimeEntry != null) { // Transfer lifetime from the entry. LifetimeStart = lifetimeEntry.LifetimeStart; LifetimeEnd = lifetimeEntry.LifetimeEnd; // Copy any existing result from the entry (required for rewind / judgement revert). Result = lifetimeEntry.Result; } else { LifetimeStart = HitObject.StartTime - InitialLifetimeOffset; } // Ensure this DHO has a result. Result ??= CreateResult(HitObject.CreateJudgement()) ?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); // Copy back the result to the entry for potential future retrieval. if (lifetimeEntry != null) { lifetimeEntry.Result = Result; } foreach (var h in HitObject.NestedHitObjects) { var pooledDrawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h, this); var drawableNested = pooledDrawableNested ?? CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); // Only invoke the event for non-pooled DHOs, otherwise the event will be fired by the playfield. if (pooledDrawableNested == null) { OnNestedDrawableCreated?.Invoke(drawableNested); } drawableNested.OnNewResult += onNewResult; drawableNested.OnRevertResult += onRevertResult; drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState; // This is only necessary for non-pooled DHOs. For pooled DHOs, this is handled inside GetPooledDrawableRepresentation(). // Must be done before the nested DHO is added to occur before the nested Apply()! drawableNested.ParentHitObject = this; nestedHitObjects.Value.Add(drawableNested); AddNestedHitObject(drawableNested); } StartTimeBindable.BindTo(HitObject.StartTimeBindable); StartTimeBindable.BindValueChanged(onStartTimeChanged); if (HitObject is IHasComboInformation combo) { comboIndexBindable.BindTo(combo.ComboIndexBindable); } samplesBindable.BindTo(HitObject.SamplesBindable); samplesBindable.BindCollectionChanged(onSamplesChanged, true); HitObject.DefaultsApplied += onDefaultsApplied; OnApply(); HitObjectApplied?.Invoke(this); // If not loaded, the state update happens in LoadComplete(). if (IsLoaded) { if (Result.IsHit) { updateState(ArmedState.Hit, true); } else if (Result.HasResult) { updateState(ArmedState.Miss, true); } else { updateState(ArmedState.Idle, true); } } hasHitObjectApplied = true; }
public LoungeBackgroundScreen() { SelectedRoom.BindValueChanged(onSelectedRoomChanged); playlist.BindCollectionChanged((_, __) => PlaylistItem = playlist.GetCurrentItem()); }