private void sendDownloadProgress() { var n = new ProgressNotification { Text = @"Downloading Haitai...", CompletionText = "Downloaded Haitai!", }; manager.Post(n); progressingNotifications.Add(n); }
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { if (scoreProcessor != null) { BindProcessor(scoreProcessor); } if (drawableRuleset != null) { BindDrawableRuleset(drawableRuleset); Progress.Objects = drawableRuleset.Objects; Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value; Progress.RequestSeek = time => RequestSeek(time); Progress.ReferenceClock = drawableRuleset.FrameStableClock; } ModDisplay.Current.Value = mods; configShowHud = config.GetBindable <bool>(OsuSetting.ShowInterface); if (!configShowHud.Value && !hasShownNotificationOnce) { hasShownNotificationOnce = true; notificationOverlay?.Post(new SimpleNotification { Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); } // start all elements hidden hideTargets.ForEach(d => d.Hide()); }
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { BindProcessor(scoreProcessor); BindDrawableRuleset(drawableRuleset); Progress.Objects = drawableRuleset.Objects; Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value; Progress.RequestSeek = time => RequestSeek(time); Progress.ReferenceClock = drawableRuleset.FrameStableClock; ModDisplay.Current.Value = mods; showHud = config.GetBindable <bool>(OsuSetting.ShowInterface); showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); showHud.TriggerChange(); if (!showHud.Value && !hasShownNotificationOnce) { hasShownNotificationOnce = true; notificationOverlay?.Post(new SimpleNotification { Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); } }
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { if (drawableRuleset != null) { BindDrawableRuleset(drawableRuleset); Progress.Objects = drawableRuleset.Objects; Progress.RequestSeek = time => RequestSeek(time); Progress.ReferenceClock = drawableRuleset.FrameStableClock; } ModDisplay.Current.Value = mods; configVisibilityMode = config.GetBindable <HUDVisibilityMode>(OsuSetting.HUDVisibilityMode); if (configVisibilityMode.Value == HUDVisibilityMode.Never && !hasShownNotificationOnce) { hasShownNotificationOnce = true; notificationOverlay?.Post(new SimpleNotification { Text = $"The score overlay is currently disabled. You can toggle this by pressing {config.LookupKeyBindings(GlobalAction.ToggleInGameInterface)}." }); } // start all elements hidden hideTargets.ForEach(d => d.Hide()); }
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay, OsuColour colours) { showHud = config.GetBindable <bool>(OsuSetting.ShowInterface); showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); showHud.TriggerChange(); if (!showHud && !hasShownNotificationOnce) { hasShownNotificationOnce = true; notificationOverlay?.Post(new SimpleNotification { Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); } // todo: the stuff below should probably not be in this base implementation, but in each individual class. ComboCounter.AccentColour = colours.BlueLighter; AccuracyCounter.AccentColour = colours.BlueLighter; ScoreCounter.AccentColour = colours.BlueLighter; var shd = HealthDisplay as StandardHealthDisplay; if (shd != null) { shd.AccentColour = colours.BlueLighter; shd.GlowColour = colours.BlueDarker; } }
protected void LoadScore(Score s) { scoreLoad?.Cancel(); var menu = intro.ChildScreen; if (menu == null) { scoreLoad = Schedule(() => LoadScore(s)); return; } if (!menu.IsCurrentScreen) { menu.MakeCurrent(); this.Delay(500).Schedule(() => LoadScore(s), out scoreLoad); return; } if (s.Beatmap == null) { notificationOverlay.Post(new SimpleNotification { Text = @"Tried to load a score for a beatmap we don't have!", Icon = FontAwesome.fa_life_saver, }); return; } Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); }
private void load(IAPIProvider api, NotificationOverlay notifications) { SpriteIcon icon; AddRange(new Drawable[] { icon = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Regular.Heart, Size = new Vector2(18), Shadow = false, }, loading = new LoadingLayer(true, false), }); Action = () => { // guaranteed by disabled state above. Debug.Assert(BeatmapSet.Value.OnlineBeatmapSetID != null); loading.Show(); request?.Cancel(); request = new PostBeatmapFavouriteRequest(BeatmapSet.Value.OnlineBeatmapSetID.Value, favourited.Value ? BeatmapFavouriteAction.UnFavourite : BeatmapFavouriteAction.Favourite); request.Success += () => { favourited.Toggle(); loading.Hide(); }; request.Failure += e => { notifications?.Post(new SimpleNotification { Text = e.Message, Icon = FontAwesome.Solid.Times, }); loading.Hide(); }; api.Queue(request); }; favourited.ValueChanged += favourited => icon.Icon = favourited.NewValue ? FontAwesome.Solid.Heart : FontAwesome.Regular.Heart; localUser.BindTo(api.LocalUser); localUser.BindValueChanged(_ => updateEnabled()); // must be run after setting the Action to ensure correct enabled state (setting an Action forces a button to be enabled). BeatmapSet.BindValueChanged(setInfo => { updateEnabled(); favourited.Value = setInfo.NewValue?.OnlineInfo?.HasFavourited ?? false; }, true); }
public async Task TakeScreenshotAsync() => await Task.Run(async() => { Interlocked.Increment(ref screenShotTasks); if (!captureMenuCursor.Value) { cursorVisibility.Value = false; // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value const int frames_to_wait = 3; int framesWaited = 0; ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => framesWaited++, 0, true); while (framesWaited < frames_to_wait) { Thread.Sleep(10); } waitDelegate.Cancel(); } using (var image = await host.TakeScreenshotAsync()) { Interlocked.Decrement(ref screenShotTasks); var fileName = getFileName(); if (fileName == null) { return; } var stream = storage.GetStream(fileName, FileAccess.Write); switch (screenshotFormat.Value) { case ScreenshotFormat.Png: image.SaveAsPng(stream); break; case ScreenshotFormat.Jpg: image.SaveAsJpeg(stream); break; default: throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); } notificationOverlay.Post(new SimpleNotification { Text = $"{fileName} saved!", Activated = () => { storage.OpenInNativeExplorer(); return(true); } }); } });
private void load(OsuGame game, NotificationOverlay notifications) { // will be null in tests this.game = game; showNotImplementedError = () => notifications?.Post(new SimpleNotification { Text = @"This link type is not yet supported!", Icon = FontAwesome.fa_life_saver, }); }
/// <summary> /// Perform an action only after returning to the main menu. /// Eagerly tries to exit the current screen until it succeeds. /// </summary> /// <param name="action">The action to perform once we are in the correct state.</param> /// <param name="taskName">The task name to display in a notification (if we can't immediately reach the main menu state).</param> /// <param name="targetScreen">An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.</param> /// <param name="bypassScreenAllowChecks">Whether checking <see cref="IOsuScreen.AllowExternalScreenChange"/> should be bypassed.</param> private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false) { performFromMainMenuTask?.Cancel(); // if the current screen does not allow screen changing, give the user an option to try again later. if (!bypassScreenAllowChecks && (ScreenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false) { notifications.Post(new SimpleNotification { Text = $"Click here to {taskName}", Activated = () => { performFromMainMenu(action, taskName, targetScreen, true); return(true); } }); return; } CloseAllOverlays(false); // we may already be at the target screen type. if (targetScreen != null && ScreenStack.CurrentScreen?.GetType() == targetScreen) { action(); return; } // all conditions have been met to continue with the action. if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled) { action(); return; } // menuScreen may not be initialised yet (null check required). menuScreen?.MakeCurrent(); performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName)); }
private void load(OsuGame game, NotificationOverlay notifications, ChannelManager channelManager) { // will be null in tests this.game = game; this.channelManager = channelManager; showNotImplementedError = () => notifications?.Post(new SimpleNotification { Text = @"This link type is not yet supported!", Icon = FontAwesome.Solid.LifeRing, }); }
private void onMulti() { if (!api.IsLoggedIn) { notifications?.Post(new SimpleNotification { Text = "You gotta be logged in to multi 'yo!", Icon = FontAwesome.Solid.Globe }); return; } OnMulti?.Invoke(); }
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { showHud = config.GetBindable <bool>(OsuSetting.ShowInterface); showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration); showHud.TriggerChange(); if (!showHud.Value && !hasShownNotificationOnce) { hasShownNotificationOnce = true; notificationOverlay?.Post(new SimpleNotification { Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); } }
protected override void LoadComplete() { base.LoadComplete(); var version = game.Version; var lastVersion = config.Get <string>(OsuSetting.Version); if (game.IsDeployedBuild && version != lastVersion) { config.Set(OsuSetting.Version, version); // only show a notification if we've previously saved a version to the config file (ie. not the first run). if (!string.IsNullOrEmpty(lastVersion)) { notificationOverlay.Post(new UpdateCompleteNotification(version)); } } }
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { BindProcessor(scoreProcessor); BindDrawableRuleset(drawableRuleset); Progress.Objects = drawableRuleset.Objects; Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value; Progress.RequestSeek = time => RequestSeek(time); Progress.ReferenceClock = drawableRuleset.FrameStableClock; ModDisplay.Current.Value = mods; showHud = config.GetBindable <bool>(OsuSetting.ShowInterface); showHud.BindValueChanged(visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration, easing), true); ShowHealthbar.BindValueChanged(healthBar => { if (healthBar.NewValue) { HealthDisplay.FadeIn(duration, easing); topScoreContainer.MoveToY(30, duration, easing); } else { HealthDisplay.FadeOut(duration, easing); topScoreContainer.MoveToY(0, duration, easing); } }, true); if (!showHud.Value && !hasShownNotificationOnce) { hasShownNotificationOnce = true; notificationOverlay?.Post(new SimpleNotification { Text = @"游戏界面已隐藏,按Shift+Tab可重新显示." }); } }
public void Import(params string[] paths) { notification.Post(new SimpleNotification { Text = @"Would you like to import """ + System.IO.Path.GetFileNameWithoutExtension(paths[0]) + @"""", Activated = ImportAsBeatmap, Important = true }); bool ImportAsBeatmap() { Track track = collection.CurrentBeatmap.Value.Song; if (track.IsRunning) { track.Stop(); } PushToEditor?.Invoke(paths[0]); return(true); } }
public async void TakeScreenshotAsync() { using (var bitmap = await host.TakeScreenshotAsync()) { var fileName = getFileName(); if (fileName == null) { return; } var stream = storage.GetStream(fileName, FileAccess.Write); switch (screenshotFormat.Value) { case ScreenshotFormat.Png: bitmap.Save(stream, ImageFormat.Png); break; case ScreenshotFormat.Jpg: bitmap.Save(stream, ImageFormat.Jpeg); break; default: throw new ArgumentOutOfRangeException(nameof(screenshotFormat)); } notificationOverlay.Post(new SimpleNotification { Text = $"{fileName} saved!", Activated = () => { storage.OpenInNativeExplorer(); return(true); } }); } }
protected override void LoadComplete() { base.LoadComplete(); // hook up notifications to components. BeatmapManager.PostNotification = n => notificationOverlay?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; AddRange(new Drawable[] { new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, ActionRequested = action => volume.Adjust(action) }, mainContent = new Container { RelativeSizeAxes = Axes.Both, }, volume = new VolumeControl(), overlayContent = new Container { RelativeSizeAxes = Axes.Both }, new OnScreenDisplay(), }); LoadComponentAsync(screenStack = new Loader(), d => { screenStack.ModePushed += screenAdded; screenStack.Exited += screenRemoved; mainContent.Add(screenStack); }); //overlay elements LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(settings = new MainSettings { GetToolbarHeight = () => ToolbarOffset, Depth = -1 }, overlayContent.Add); LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); LoadComponentAsync(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); LoadComponentAsync(musicController = new MusicController { Depth = -4, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); LoadComponentAsync(notificationOverlay = new NotificationOverlay { Depth = -4, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); LoadComponentAsync(dialogOverlay = new DialogOverlay { Depth = -5, }, overlayContent.Add); Logger.NewEntry += entry => { if (entry.Level < LogLevel.Important) { return; } notificationOverlay.Post(new SimpleNotification { Text = $@"{entry.Level}: {entry.Message}" }); }; dependencies.Cache(settings); dependencies.Cache(social); dependencies.Cache(direct); dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); dependencies.Cache(beatmapSetOverlay); dependencies.Cache(notificationOverlay); dependencies.Cache(dialogOverlay); // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; foreach (var overlay in singleDisplayOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } foreach (var c in singleDisplayOverlays) { if (c == overlay) { continue; } c.State = Visibility.Hidden; } }; } LoadComponentAsync(Toolbar = new Toolbar { Depth = -4, OnHome = delegate { hideAllOverlays(); intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); settings.StateChanged += delegate { switch (settings.State) { case Visibility.Hidden: intro.MoveToX(0, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); break; case Visibility.Visible: intro.MoveToX(SettingsOverlay.SIDEBAR_WIDTH / 2, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); break; } }; Cursor.State = Visibility.Hidden; }
public Task TakeScreenshotAsync() => Task.Run(async() => { Interlocked.Increment(ref screenShotTasks); if (!captureMenuCursor.Value) { cursorVisibility.Value = false; // We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value const int frames_to_wait = 3; int framesWaited = 0; using (var framesWaitedEvent = new ManualResetEventSlim(false)) { ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => { if (framesWaited++ < frames_to_wait) { // ReSharper disable once AccessToDisposedClosure framesWaitedEvent.Set(); } }, 10, true); framesWaitedEvent.Wait(); waitDelegate.Cancel(); } } using (var image = await host.TakeScreenshotAsync()) { if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false) { cursorVisibility.Value = true; } var fileName = getFileName(); if (fileName == null) { return; } var stream = storage.GetStream(fileName, FileAccess.Write); switch (screenshotFormat.Value) { case ScreenshotFormat.Png: image.SaveAsPng(stream); break; case ScreenshotFormat.Jpg: image.SaveAsJpeg(stream); break; default: throw new InvalidOperationException($"Unknown enum member {nameof(ScreenshotFormat)} {screenshotFormat.Value}."); } notificationOverlay.Post(new SimpleNotification { Text = $"{fileName} saved!", Activated = () => { storage.OpenInNativeExplorer(); return(true); } }); } });
protected override void LoadComplete() { base.LoadComplete(); // The next time this is updated is in UpdateAfterChildren, which occurs too late and results // in the cursor being shown for a few frames during the intro. // This prevents the cursor from showing until we have a screen with CursorVisible = true MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications?.Post(n); SkinManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PresentBeatmap = PresentBeatmap; AddRange(new Drawable[] { new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, ActionRequested = action => volume.Adjust(action), ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise), }, screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays) { RelativeSizeAxes = Axes.Both, }, mainContent = new Container { RelativeSizeAxes = Axes.Both, }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, idleTracker = new IdleTracker(6000) }); loadComponentSingleFile(screenStack = new Loader(), d => { screenStack.ModePushed += screenAdded; screenStack.Exited += screenRemoved; screenContainer.Add(screenStack); }); loadComponentSingleFile(Toolbar = new Toolbar { Depth = -5, OnHome = delegate { CloseAllOverlays(false); intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); loadComponentSingleFile(screenshotManager, Add); //overlay elements loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal); loadComponentSingleFile(chatOverlay = new ChatOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(settings = new MainSettings { GetToolbarHeight = () => ToolbarOffset, Depth = -1 }, overlayContent.Add); loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); loadComponentSingleFile(musicController = new MusicController { Depth = -5, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); loadComponentSingleFile(notifications = new NotificationOverlay { GetToolbarHeight = () => ToolbarOffset, Depth = -4, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); loadComponentSingleFile(accountCreation = new AccountCreationOverlay { Depth = -6, }, overlayContent.Add); loadComponentSingleFile(dialogOverlay = new DialogOverlay { Depth = -7, }, overlayContent.Add); loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener { Depth = -8, }, overlayContent.Add); dependencies.Cache(idleTracker); dependencies.Cache(settings); dependencies.Cache(onscreenDisplay); dependencies.Cache(social); dependencies.Cache(direct); dependencies.Cache(chatOverlay); dependencies.Cache(channelManager); dependencies.Cache(userProfile); dependencies.Cache(musicController); dependencies.Cache(beatmapSetOverlay); dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); dependencies.Cache(accountCreation); chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; overlays.AddRange(singleDisplaySideOverlays); foreach (var overlay in singleDisplaySideOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; overlays.AddRange(informationalOverlays); foreach (var overlay in informationalOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, social, direct }; overlays.AddRange(singleDisplayOverlays); foreach (var overlay in singleDisplayOverlays) { overlay.StateChanged += state => { // informational overlays should be dismissed on a show or hide of a full overlay. informationalOverlays.ForEach(o => o.Hide()); if (state == Visibility.Hidden) { return; } singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } OverlayActivationMode.ValueChanged += v => { if (v != OverlayActivation.All) { CloseAllOverlays(); } }; void updateScreenOffset() { float offset = 0; if (settings.State == Visibility.Visible) { offset += ToolbarButton.WIDTH / 2; } if (notifications.State == Visibility.Visible) { offset -= ToolbarButton.WIDTH / 2; } screenContainer.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); } settings.StateChanged += _ => updateScreenOffset(); notifications.StateChanged += _ => updateScreenOffset(); }
protected void LoadScore(ScoreInfo score, bool silent) { if (silent) { return; } scoreLoad?.Cancel(); var menu = intro.ChildScreen; if (menu == null) { scoreLoad = Schedule(() => LoadScore(score, false)); return; } var databasedScore = ScoreManager.GetScore(score); var databasedScoreInfo = databasedScore.ScoreInfo; if (databasedScore.Replay == null) { Logger.Log("The loaded score has no replay data.", LoggingTarget.Information); return; } var databasedBeatmap = BeatmapManager.QueryBeatmap(b => b.ID == databasedScoreInfo.Beatmap.ID); if (databasedBeatmap == null) { Logger.Log("Tried to load a score for a beatmap we don't have!", LoggingTarget.Information); return; } if (!currentScreen.AllowExternalScreenChange) { notifications.Post(new SimpleNotification { Text = $"Click here to watch {databasedScoreInfo.User.Username} on {databasedScoreInfo.Beatmap}", Activated = () => { loadScore(); return(true); } }); return; } loadScore(); void loadScore() { if (!menu.IsCurrentScreen) { menu.MakeCurrent(); this.Delay(500).Schedule(loadScore, out scoreLoad); return; } ruleset.Value = databasedScoreInfo.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); Beatmap.Value.Mods.Value = databasedScoreInfo.Mods; currentScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); } }
protected override void LoadComplete() { base.LoadComplete(); // The next time this is updated is in UpdateAfterChildren, which occurs too late and results // in the cursor being shown for a few frames during the intro. // This prevents the cursor from showing until we have a screen with CursorVisible = true MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false; // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications?.Post(n); SkinManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); ScoreManager.PostNotification = n => notifications?.Post(n); ScoreManager.GetStableStorage = GetStorageForStableInstall; ScoreManager.PresentImport = items => PresentScore(items.First()); Container logoContainer; BackButton.Receptor receptor; dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); AddRange(new Drawable[] { new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, ActionRequested = action => volume.Adjust(action), ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise), }, screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays) { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { receptor = new BackButton.Receptor(), ScreenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, BackButton = new BackButton(receptor) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Action = () => { if ((ScreenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true) { ScreenStack.Exit(); } } }, logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, overlayContent = new Container { RelativeSizeAxes = Axes.Both }, rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both }, idleTracker }); ScreenStack.ScreenPushed += screenPushed; ScreenStack.ScreenExited += screenExited; loadComponentSingleFile(osuLogo, logo => { logoContainer.Add(logo); // Loader has to be created after the logo has finished loading as Loader performs logo transformations on entering. ScreenStack.Push(CreateLoader().With(l => l.RelativeSizeAxes = Axes.Both)); }); loadComponentSingleFile(Toolbar = new Toolbar { OnHome = delegate { CloseAllOverlays(false); menuScreen?.MakeCurrent(); }, }, d => { topMostOverlayContent.Add(d); toolbarElements.Add(d); }); loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); loadComponentSingleFile(new OnScreenDisplay(), Add, true); loadComponentSingleFile(musicController = new MusicController(), Add, true); loadComponentSingleFile(notifications = new NotificationOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); loadComponentSingleFile(screenshotManager, Add); //overlay elements loadComponentSingleFile(direct = new DirectOverlay(), overlayContent.Add, true); loadComponentSingleFile(social = new SocialOverlay(), overlayContent.Add, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); loadComponentSingleFile(Settings = new SettingsOverlay { GetToolbarHeight = () => ToolbarOffset }, leftFloatingOverlayContent.Add, true); var changelogOverlay = loadComponentSingleFile(new ChangelogOverlay(), overlayContent.Add, true); loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add, true); loadComponentSingleFile(new LoginOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); loadComponentSingleFile(new NowPlayingOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, d => { rightFloatingOverlayContent.Add(d); toolbarElements.Add(d); }, true); loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add); chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications }; overlays.AddRange(singleDisplaySideOverlays); foreach (var overlay in singleDisplaySideOverlays) { overlay.State.ValueChanged += state => { if (state.NewValue == Visibility.Hidden) { return; } singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; overlays.AddRange(informationalOverlays); foreach (var overlay in informationalOverlays) { overlay.State.ValueChanged += state => { if (state.NewValue == Visibility.Hidden) { return; } informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, social, direct, changelogOverlay }; overlays.AddRange(singleDisplayOverlays); foreach (var overlay in singleDisplayOverlays) { overlay.State.ValueChanged += state => { // informational overlays should be dismissed on a show or hide of a full overlay. informationalOverlays.ForEach(o => o.Hide()); if (state.NewValue == Visibility.Hidden) { return; } singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } OverlayActivationMode.ValueChanged += mode => { if (mode.NewValue != OverlayActivation.All) { CloseAllOverlays(); } }; void updateScreenOffset() { float offset = 0; if (Settings.State.Value == Visibility.Visible) { offset += ToolbarButton.WIDTH / 2; } if (notifications.State.Value == Visibility.Visible) { offset -= ToolbarButton.WIDTH / 2; } screenContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint); } Settings.State.ValueChanged += _ => updateScreenOffset(); notifications.State.ValueChanged += _ => updateScreenOffset(); }
protected override void LoadComplete() { base.LoadComplete(); dependencies.Cache(this); AddRange(new Drawable[] { screenContainer = new BufferedContainer { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { screenStack = new RhythmicScreenStack { RelativeSizeAxes = Axes.Both }, } }, rightFloatingOverlayContent = new BufferedContainer { RelativeSizeAxes = Axes.Both }, topMostOverlayContent = new BufferedContainer { RelativeSizeAxes = Axes.Both }, }); screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; dependencies.Cache(beatmaps); loadComponentSingleFile(Toolbar = new Toolbar(screenContainer) { OnHome = delegate { CloseAllOverlays(false); menuScreen?.MakeCurrent(); }, }, d => { topMostOverlayContent.Add(d); toolbarElements.Add(d); }); loadComponentSingleFile(musicController = new MusicController(screenContainer) { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, m => { rightFloatingOverlayContent.Add(m); overlays.Add(m); }); loadComponentSingleFile(notifications = new NotificationOverlay(screenContainer) { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, n => { rightFloatingOverlayContent.Add(n); overlays.Add(n); }); notifications.Post(new SimpleNotification { Text = "Test Notification", Description = "And here’s some text that you can almost barely read!" }); Toolbar.ToggleVisibility(); dependencies.Cache(menuScreen = new MainMenu(screenContainer)); screenStack.Push(new Loader()); dependencies.Cache(musicController); dependencies.Cache(notifications); OverlayActivationMode.ValueChanged += mode => { if (mode.NewValue != OverlayActivation.All) { CloseAllOverlays(); } }; EditorBeatmapManager editorBeatmapManager; dependencies.Cache(editorBeatmapManager = new EditorBeatmapManager()); Add(editorBeatmapManager); fileImporters.Add(editorBeatmapManager); editorBeatmapManager.PushToEditor += b => { ProgressNotification notification = new ProgressNotification { State = ProgressNotificationState.Active }; BeatmapMeta convertedBeatmap = Import(notification, b); if (convertedBeatmap != null) { beatmaps.CurrentBeatmap.Value = convertedBeatmap; screenStack.Push(new Editor()); } }; BeatmapMeta Import(ProgressNotification notification, string songPath) { notification.Progress = 0; notification.Text = "Import is initialising..."; if (notification.State == ProgressNotificationState.Cancelled) { return(null); } try { string text = "Importing " + Path.GetFileNameWithoutExtension(songPath); notification.Text = text; FileStream file = File.OpenRead(songPath); BeatmapMeta beatmap = new BeatmapMeta { Song = new TrackBass(file), SongUrl = songPath, Metadata = new BeatmapMetadata { Song = new SongMetadata(), Level = new LevelMetadata() } }; beatmaps.Beatmaps.Add(beatmap); return(beatmap); } catch (Exception e) { e = e.InnerException ?? e; Logger.Error(e, $@"Could not import ({Path.GetFileName(songPath)})"); } return(null); } }
protected override void LoadComplete() { base.LoadComplete(); // hook up notifications to components. BeatmapManager.PostNotification = n => notificationOverlay?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; AddRange(new Drawable[] { new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, ActionRequested = delegate(InputState state) { volume.Adjust(state); } }, mainContent = new Container { RelativeSizeAxes = Axes.Both, }, volume = new VolumeControl(), overlayContent = new Container { RelativeSizeAxes = Axes.Both }, new OnScreenDisplay(), }); LoadComponentAsync(screenStack = new Loader(), d => { screenStack.ModePushed += screenAdded; screenStack.Exited += screenRemoved; mainContent.Add(screenStack); }); //overlay elements LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add); LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); LoadComponentAsync(musicController = new MusicController { Depth = -3, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); LoadComponentAsync(notificationOverlay = new NotificationOverlay { Depth = -3, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); LoadComponentAsync(dialogOverlay = new DialogOverlay { Depth = -5, }, overlayContent.Add); Logger.NewEntry += entry => { if (entry.Level < LogLevel.Important) { return; } notificationOverlay.Post(new SimpleNotification { Text = $@"{entry.Level}: {entry.Message}" }); }; dependencies.Cache(settings); dependencies.Cache(social); dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); dependencies.Cache(notificationOverlay); dependencies.Cache(dialogOverlay); // ensure both overlays aren't presented at the same time chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State; social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State; LoadComponentAsync(Toolbar = new Toolbar { Depth = -4, OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); settings.StateChanged += delegate { switch (settings.State) { case Visibility.Hidden: intro.MoveToX(0, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); break; case Visibility.Visible: intro.MoveToX(SettingsOverlay.SIDEBAR_WIDTH / 2, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); break; } }; Cursor.State = Visibility.Hidden; }
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, RankingsOverlay rankings, OsuConfigManager config, SessionStatics statics) { holdDelay = config.GetBindable <float>(OsuSetting.UIHoldActivationDelay); loginDisplayed = statics.GetBindable <bool>(Static.LoginOverlayDisplayed); if (host.CanExit) { AddInternal(exitConfirmOverlay = new ExitConfirmOverlay { Action = () => { if (holdDelay.Value > 0) { confirmAndExit(); } else { this.Exit(); } } }); } AddRangeInternal(new[] { buttonsContainer = new ParallaxContainer { ParallaxAmount = 0.01f, Children = new Drawable[] { buttons = new ButtonSystem { OnEdit = delegate { Beatmap.SetDefault(); this.Push(new Editor()); }, OnSolo = onSolo, OnMultiplayer = () => { this.Push(new Multiplayer()); notifications?.Post(new SimpleNotification { Text = "警告: 本版本爲非官方發行版 osu!lazer\n這會影響到遊玩多人模式的功能.", Icon = FontAwesome.Solid.ExclamationTriangle }); }, OnPlaylists = () => { this.Push(new Playlists()); notifications?.Post(new SimpleNotification { Text = "警告: 本版本爲非官方發行版 osu!lazer\n這會影響到遊玩多人模式的功能.", Icon = FontAwesome.Solid.ExclamationTriangle }); }, OnExit = confirmAndExit, } } }, sideFlashes = new MenuSideFlashes(), songTicker = new SongTicker { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Margin = new MarginPadding { Right = 15, Top = 5 } }, exitConfirmOverlay?.CreateProxy() ?? Drawable.Empty() }); buttons.StateChanged += state => { switch (state) { case ButtonSystemState.Initial: case ButtonSystemState.Exit: Background.FadeColour(Color4.White, 500, Easing.OutSine); break; default: Background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); break; } }; buttons.OnSettings = () => settings?.ToggleVisibility(); buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility(); LoadComponentAsync(background = new BackgroundScreenDefault()); preloadSongSelect(); }
protected override void LoadComplete() { // this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer. dependencies.Cache(screenshotManager = new ScreenshotManager()); base.LoadComplete(); // The next time this is updated is in UpdateAfterChildren, which occurs too late and results // in the cursor being shown for a few frames during the intro. // This prevents the cursor from showing until we have a screen with CursorVisible = true MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; // hook up notifications to components. SkinManager.PostNotification = n => notifications?.Post(n); BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; AddRange(new Drawable[] { new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, ActionRequested = action => volume.Adjust(action) }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, }); loadComponentSingleFile(screenStack = new Loader(), d => { screenStack.ModePushed += screenAdded; screenStack.Exited += screenRemoved; mainContent.Add(screenStack); }); loadComponentSingleFile(Toolbar = new Toolbar { Depth = -5, OnHome = delegate { hideAllOverlays(); intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); loadComponentSingleFile(screenshotManager, Add); //overlay elements loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(settings = new MainSettings { GetToolbarHeight = () => ToolbarOffset, Depth = -1 }, overlayContent.Add); loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); loadComponentSingleFile(musicController = new MusicController { Depth = -4, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); loadComponentSingleFile(notifications = new NotificationOverlay { GetToolbarHeight = () => ToolbarOffset, Depth = -4, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); loadComponentSingleFile(dialogOverlay = new DialogOverlay { Depth = -6, }, overlayContent.Add); forwardLoggedErrorsToNotifications(); dependencies.Cache(settings); dependencies.Cache(onscreenDisplay); dependencies.Cache(social); dependencies.Cache(direct); dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); dependencies.Cache(beatmapSetOverlay); dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; foreach (var overlay in singleDisplayOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } foreach (var c in singleDisplayOverlays) { if (c == overlay) { continue; } c.State = Visibility.Hidden; } }; } var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; foreach (var overlay in singleDisplaySideOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } foreach (var c in singleDisplaySideOverlays) { if (c == overlay) { continue; } c.State = Visibility.Hidden; } }; } // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; foreach (var overlay in informationalOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } foreach (var c in informationalOverlays) { if (c == overlay) { continue; } c.State = Visibility.Hidden; } }; } void updateScreenOffset() { float offset = 0; if (settings.State == Visibility.Visible) { offset += ToolbarButton.WIDTH / 2; } if (notifications.State == Visibility.Visible) { offset -= ToolbarButton.WIDTH / 2; } screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); } settings.StateChanged += _ => updateScreenOffset(); notifications.StateChanged += _ => updateScreenOffset(); notifications.Enabled.BindTo(AllowOpeningOverlays); HideOverlaysOnEnter.ValueChanged += hide => { //central game screen change logic. if (hide) { hideAllOverlays(); musicController.State = Visibility.Hidden; Toolbar.State = Visibility.Hidden; } else { Toolbar.State = Visibility.Visible; } }; }
protected override void LoadComplete() { base.LoadComplete(); // hook up notifications to components. BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; AddRange(new Drawable[] { new VolumeControlReceptor { RelativeSizeAxes = Axes.Both, ActionRequested = action => volume.Adjust(action) }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, }); loadComponentSingleFile(screenStack = new Loader(), d => { screenStack.ModePushed += screenAdded; screenStack.Exited += screenRemoved; mainContent.Add(screenStack); }); loadComponentSingleFile(Toolbar = new Toolbar { Depth = -5, OnHome = delegate { hideAllOverlays(); intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); loadComponentSingleFile(volume = new VolumeControl(), Add); loadComponentSingleFile(new OnScreenDisplay(), Add); //overlay elements loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(settings = new MainSettings { GetToolbarHeight = () => ToolbarOffset, Depth = -1 }, overlayContent.Add); loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); loadComponentSingleFile(musicController = new MusicController { Depth = -4, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); loadComponentSingleFile(notifications = new NotificationOverlay { GetToolbarHeight = () => ToolbarOffset, Depth = -4, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, overlayContent.Add); loadComponentSingleFile(dialogOverlay = new DialogOverlay { Depth = -6, }, overlayContent.Add); Logger.NewEntry += entry => { if (entry.Level < LogLevel.Important) { return; } notifications.Post(new SimpleNotification { Text = $@"{entry.Level}: {entry.Message}" }); }; dependencies.Cache(settings); dependencies.Cache(social); dependencies.Cache(direct); dependencies.Cache(chat); dependencies.Cache(userProfile); dependencies.Cache(musicController); dependencies.Cache(beatmapSetOverlay); dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; foreach (var overlay in singleDisplayOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } foreach (var c in singleDisplayOverlays) { if (c == overlay) { continue; } c.State = Visibility.Hidden; } }; } // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; foreach (var overlay in informationalOverlays) { overlay.StateChanged += state => { if (state == Visibility.Hidden) { return; } foreach (var c in informationalOverlays) { if (c == overlay) { continue; } c.State = Visibility.Hidden; } }; } void updateScreenOffset() { float offset = 0; if (settings.State == Visibility.Visible) { offset += ToolbarButton.WIDTH / 2; } if (notifications.State == Visibility.Visible) { offset -= ToolbarButton.WIDTH / 2; } screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); } settings.StateChanged += _ => updateScreenOffset(); notifications.StateChanged += _ => updateScreenOffset(); Cursor.State = Visibility.Hidden; }