From 61d6c663f49d3b5631e394fd6148fdb5a43e7d41 Mon Sep 17 00:00:00 2001 From: Hivie Date: Thu, 23 Jul 2026 19:07:46 +0100 Subject: [PATCH 1/4] add ability to sync bookmarks/preview across difficulties --- osu.Game/Localisation/EditorDialogsStrings.cs | 15 ++ osu.Game/Localisation/EditorStrings.cs | 15 ++ .../Edit/SyncTimingConfirmationDialog.cs | 18 ++ .../Edit/Timing/ControlPointSettings.cs | 1 + osu.Game/Screens/Edit/Timing/SyncSection.cs | 171 ++++++++++++++++++ 5 files changed, 220 insertions(+) create mode 100644 osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs create mode 100644 osu.Game/Screens/Edit/Timing/SyncSection.cs diff --git a/osu.Game/Localisation/EditorDialogsStrings.cs b/osu.Game/Localisation/EditorDialogsStrings.cs index 2d58dab2d77d..79dc60c866e4 100644 --- a/osu.Game/Localisation/EditorDialogsStrings.cs +++ b/osu.Game/Localisation/EditorDialogsStrings.cs @@ -94,6 +94,21 @@ public static class EditorDialogsStrings /// public static LocalisableString SnapAllNotesConfirmationBody => new TranslatableString(getKey(@"snap_all_notes_confirmation_body"), @"Every hit object in this difficulty will move to the nearest tick for your current snap divisor. Slider and hold durations may change. You can undo this from the editor history."); + /// + /// "This overwrites the selected options on all other difficulties. This cannot be undone." + /// + public static LocalisableString SyncTimingConfirmationBody => new TranslatableString(getKey(@"sync_timing_confirmation_body"), @"This overwrites the selected options on all other difficulties. This cannot be undone."); + + /// + /// "Bookmarks" + /// + public static LocalisableString SyncTimingOptionBookmarks => new TranslatableString(getKey(@"sync_timing_option_bookmarks"), @"Bookmarks"); + + /// + /// "Preview point" + /// + public static LocalisableString SyncTimingOptionPreviewPoint => new TranslatableString(getKey(@"sync_timing_option_preview_point"), @"Preview point"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 5d3fae0ea775..db6578cd2db6 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -309,6 +309,21 @@ public static class EditorStrings /// public static LocalisableString SampleSet => new TranslatableString(getKey(@"sample_set"), @"Sample set"); + /// + /// "Sync" + /// + public static LocalisableString Sync => new TranslatableString(getKey(@"sync"), @"Sync"); + + /// + /// "Sync to all difficulties..." + /// + public static LocalisableString SyncToAllDifficulties => new TranslatableString(getKey(@"sync_to_all_difficulties"), @"Sync to all difficulties..."); + + /// + /// "Copy bookmarks and/or preview point from this difficulty to all others." + /// + public static LocalisableString SyncToAllDifficultiesTooltip => new TranslatableString(getKey(@"sync_to_all_difficulties_tooltip"), @"Copy bookmarks and/or preview point from this difficulty to all others."); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs b/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs new file mode 100644 index 000000000000..b26f72a54079 --- /dev/null +++ b/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Game.Localisation; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Screens.Edit +{ + public partial class SyncTimingConfirmationDialog : DangerousActionDialog + { + public SyncTimingConfirmationDialog(Action syncAction) + { + BodyText = EditorDialogsStrings.SyncTimingConfirmationBody; + DangerousAction = syncAction; + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs index b76723378f50..42575c1fb98b 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs @@ -13,6 +13,7 @@ public partial class ControlPointSettings : EditorRoundedScreenSettings new GroupSection(), new TimingSection(), new EffectSection(), + new SyncSection(), }; } } diff --git a/osu.Game/Screens/Edit/Timing/SyncSection.cs b/osu.Game/Screens/Edit/Timing/SyncSection.cs new file mode 100644 index 000000000000..1b74b31c57ef --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/SyncSection.cs @@ -0,0 +1,171 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Screens.Edit.Timing +{ + internal partial class SyncSection : CompositeDrawable + { + private readonly BindableBool syncBookmarks = new BindableBool(true); + private readonly BindableBool syncPreviewPoint = new BindableBool(true); + + private RoundedButton syncButton = null!; + + private const float header_height = 50; + + [Resolved] + private EditorBeatmap beatmap { get; set; } = null!; + + [Resolved] + private Editor? editor { get; set; } + + [Resolved] + private BeatmapManager beatmaps { get; set; } = null!; + + [Resolved] + private IBindable working { get; set; } = null!; + + [Resolved] + private IDialogOverlay? dialogOverlay { get; set; } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Masking = true; + CornerRadius = 5; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Background4, + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Height = header_height, + Padding = new MarginPadding { Horizontal = 10 }, + Child = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = EditorStrings.Sync, + } + }, + new Container + { + Y = header_height, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = new FillFlowContainer + { + Padding = new MarginPadding(10) { Top = 0 }, + Spacing = new Vector2(10), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new FormCheckBox + { + Caption = EditorDialogsStrings.SyncTimingOptionBookmarks, + Current = { BindTarget = syncBookmarks }, + }, + new FormCheckBox + { + Caption = EditorDialogsStrings.SyncTimingOptionPreviewPoint, + Current = { BindTarget = syncPreviewPoint }, + }, + syncButton = new RoundedButton + { + RelativeSizeAxes = Axes.X, + Text = EditorStrings.SyncToAllDifficulties, + TooltipText = EditorStrings.SyncToAllDifficultiesTooltip, + Action = () => dialogOverlay?.Push(new SyncTimingConfirmationDialog(syncToAllOtherDifficulties)), + } + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + syncBookmarks.BindValueChanged(_ => updateEnabledState()); + syncPreviewPoint.BindValueChanged(_ => updateEnabledState()); + updateEnabledState(); + } + + private void updateEnabledState() + { + bool canSyncToOtherDifficulties = working.Value.BeatmapSetInfo.Beatmaps.Count > 1; + + syncBookmarks.Disabled = !canSyncToOtherDifficulties; + syncPreviewPoint.Disabled = !canSyncToOtherDifficulties; + + syncButton.Enabled.Value = canSyncToOtherDifficulties + && (syncBookmarks.Value || syncPreviewPoint.Value); + } + + private void syncToAllOtherDifficulties() + { + if (working.Value.BeatmapSetInfo.Beatmaps.Count <= 1) + return; + + if (!syncBookmarks.Value && !syncPreviewPoint.Value) + return; + + var set = working.Value.BeatmapSetInfo; + var current = beatmap.BeatmapInfo; + int[] sourceBookmarks = beatmap.Bookmarks.ToArray(); + int sourcePreviewTime = beatmap.BeatmapInfo.Metadata.PreviewTime; + + foreach (var beatmapInfo in set.Beatmaps) + { + if (beatmapInfo.Equals(current)) + continue; + + try + { + var targetWorking = beatmaps.GetWorkingBeatmap(beatmapInfo); + var playable = targetWorking.GetPlayableBeatmap(beatmapInfo.Ruleset); + + if (syncBookmarks.Value) + playable.Bookmarks = sourceBookmarks.ToArray(); + + if (syncPreviewPoint.Value) + beatmapInfo.Metadata.PreviewTime = sourcePreviewTime; + + beatmaps.Save(beatmapInfo, playable, targetWorking.GetSkin(), targetWorking.Storyboard); + } + catch (Exception e) + { + Logger.Error(e, $@"Failed to sync bookmarks/preview to {beatmapInfo.GetDisplayTitle()}"); + return; + } + } + + editor?.SaveAndReload(withDialog: false); + } + } +} From 8849c071b5bf6ccbc563004ad3cfd576af89ca0e Mon Sep 17 00:00:00 2001 From: Hivie Date: Thu, 23 Jul 2026 19:11:56 +0100 Subject: [PATCH 2/4] space --- osu.Game/Screens/Edit/Timing/SyncSection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/SyncSection.cs b/osu.Game/Screens/Edit/Timing/SyncSection.cs index 1b74b31c57ef..867ffd79fd14 100644 --- a/osu.Game/Screens/Edit/Timing/SyncSection.cs +++ b/osu.Game/Screens/Edit/Timing/SyncSection.cs @@ -123,8 +123,7 @@ private void updateEnabledState() syncBookmarks.Disabled = !canSyncToOtherDifficulties; syncPreviewPoint.Disabled = !canSyncToOtherDifficulties; - syncButton.Enabled.Value = canSyncToOtherDifficulties - && (syncBookmarks.Value || syncPreviewPoint.Value); + syncButton.Enabled.Value = canSyncToOtherDifficulties && (syncBookmarks.Value || syncPreviewPoint.Value); } private void syncToAllOtherDifficulties() From adb5f0448e2a40ea22464ee6421ffd3b4ff2bc3d Mon Sep 17 00:00:00 2001 From: Hivie Date: Thu, 30 Jul 2026 12:58:13 +0100 Subject: [PATCH 3/4] move sync to timing menu --- osu.Game/Localisation/EditorDialogsStrings.cs | 5 + osu.Game/Localisation/EditorStrings.cs | 10 -- osu.Game/Screens/Edit/Editor.cs | 50 ++++++ .../Edit/SyncTimingConfirmationDialog.cs | 38 +++- .../Edit/Timing/ControlPointSettings.cs | 1 - osu.Game/Screens/Edit/Timing/SyncSection.cs | 170 ------------------ 6 files changed, 91 insertions(+), 183 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Timing/SyncSection.cs diff --git a/osu.Game/Localisation/EditorDialogsStrings.cs b/osu.Game/Localisation/EditorDialogsStrings.cs index 79dc60c866e4..898ba1dbf8aa 100644 --- a/osu.Game/Localisation/EditorDialogsStrings.cs +++ b/osu.Game/Localisation/EditorDialogsStrings.cs @@ -94,6 +94,11 @@ public static class EditorDialogsStrings /// public static LocalisableString SnapAllNotesConfirmationBody => new TranslatableString(getKey(@"snap_all_notes_confirmation_body"), @"Every hit object in this difficulty will move to the nearest tick for your current snap divisor. Slider and hold durations may change. You can undo this from the editor history."); + /// + /// "Sync to all difficulties?" + /// + public static LocalisableString SyncTimingConfirmationHeader => new TranslatableString(getKey(@"sync_timing_confirmation_header"), @"Sync to all difficulties?"); + /// /// "This overwrites the selected options on all other difficulties. This cannot be undone." /// diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index db6578cd2db6..d12dfbb04af3 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -309,21 +309,11 @@ public static class EditorStrings /// public static LocalisableString SampleSet => new TranslatableString(getKey(@"sample_set"), @"Sample set"); - /// - /// "Sync" - /// - public static LocalisableString Sync => new TranslatableString(getKey(@"sync"), @"Sync"); - /// /// "Sync to all difficulties..." /// public static LocalisableString SyncToAllDifficulties => new TranslatableString(getKey(@"sync_to_all_difficulties"), @"Sync to all difficulties..."); - /// - /// "Copy bookmarks and/or preview point from this difficulty to all others." - /// - public static LocalisableString SyncToAllDifficultiesTooltip => new TranslatableString(getKey(@"sync_to_all_difficulties_tooltip"), @"Copy bookmarks and/or preview point from this difficulty to all others."); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 862cd1fa7b10..0bdc420eb69e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -451,6 +451,10 @@ private void load(OsuConfigManager config) Items = new MenuItem[] { new EditorMenuItem(EditorStrings.SetPreviewPointToCurrent, MenuItemType.Standard, SetPreviewPointToCurrentTime), + new EditorMenuItem(EditorStrings.SyncToAllDifficulties, MenuItemType.Destructive, confirmSyncTimingToAllDifficulties) + { + Action = { Disabled = loadableBeatmap.BeatmapSetInfo.Beatmaps.Count < 2 } + }, new EditorMenuItem(EditorStrings.SnapAllNotesToCurrentSnapDivisor, MenuItemType.Destructive, confirmSnapAllHitObjectsToCurrentDivisor), bookmarkController.Menu, } @@ -1037,6 +1041,52 @@ protected void SetPreviewPointToCurrentTime() editorBeatmap.PreviewTime.Value = (int)clock.CurrentTime; } + private void confirmSyncTimingToAllDifficulties() + { + dialogOverlay.Push(new SyncTimingConfirmationDialog(syncTimingToAllOtherDifficulties)); + } + + private void syncTimingToAllOtherDifficulties(bool syncBookmarks, bool syncPreviewPoint) + { + if (Beatmap.Value.BeatmapSetInfo.Beatmaps.Count <= 1) + return; + + if (!syncBookmarks && !syncPreviewPoint) + return; + + var set = Beatmap.Value.BeatmapSetInfo; + var current = editorBeatmap.BeatmapInfo; + int[] sourceBookmarks = editorBeatmap.Bookmarks.ToArray(); + int sourcePreviewTime = editorBeatmap.BeatmapInfo.Metadata.PreviewTime; + + foreach (var beatmapInfo in set.Beatmaps) + { + if (beatmapInfo.Equals(current)) + continue; + + try + { + var targetWorking = beatmapManager.GetWorkingBeatmap(beatmapInfo); + var playable = targetWorking.GetPlayableBeatmap(beatmapInfo.Ruleset); + + if (syncBookmarks) + playable.Bookmarks = sourceBookmarks.ToArray(); + + if (syncPreviewPoint) + beatmapInfo.Metadata.PreviewTime = sourcePreviewTime; + + beatmapManager.Save(beatmapInfo, playable, targetWorking.GetSkin(), targetWorking.Storyboard); + } + catch (Exception e) + { + Logger.Error(e, $@"Failed to sync bookmarks/preview to {beatmapInfo.GetDisplayTitle()}"); + return; + } + } + + SaveAndReload(withDialog: false); + } + private void confirmSnapAllHitObjectsToCurrentDivisor() { dialogOverlay.Push(new SnapAllNotesConfirmationDialog(SnapAllHitObjectsToCurrentDivisor)); diff --git a/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs b/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs index b26f72a54079..ecbaea3679b0 100644 --- a/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs +++ b/osu.Game/Screens/Edit/SyncTimingConfirmationDialog.cs @@ -2,17 +2,51 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays.Dialog; +using osuTK; namespace osu.Game.Screens.Edit { public partial class SyncTimingConfirmationDialog : DangerousActionDialog { - public SyncTimingConfirmationDialog(Action syncAction) + private readonly BindableBool syncBookmarks = new BindableBool(true); + private readonly BindableBool syncPreviewPoint = new BindableBool(true); + + public SyncTimingConfirmationDialog(Action syncAction) { + HeaderText = EditorDialogsStrings.SyncTimingConfirmationHeader; BodyText = EditorDialogsStrings.SyncTimingConfirmationBody; - DangerousAction = syncAction; + + DangerousAction = () => syncAction(syncBookmarks.Value, syncPreviewPoint.Value); + + MainContent.Child = new FillFlowContainer + { + Margin = new MarginPadding { Top = 20 }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 400, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new OsuCheckbox + { + LabelText = EditorDialogsStrings.SyncTimingOptionBookmarks, + Current = { BindTarget = syncBookmarks }, + }, + new OsuCheckbox + { + LabelText = EditorDialogsStrings.SyncTimingOptionPreviewPoint, + Current = { BindTarget = syncPreviewPoint }, + }, + } + }; } } } diff --git a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs index 42575c1fb98b..b76723378f50 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointSettings.cs @@ -13,7 +13,6 @@ public partial class ControlPointSettings : EditorRoundedScreenSettings new GroupSection(), new TimingSection(), new EffectSection(), - new SyncSection(), }; } } diff --git a/osu.Game/Screens/Edit/Timing/SyncSection.cs b/osu.Game/Screens/Edit/Timing/SyncSection.cs deleted file mode 100644 index 867ffd79fd14..000000000000 --- a/osu.Game/Screens/Edit/Timing/SyncSection.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Logging; -using osu.Game.Beatmaps; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterfaceV2; -using osu.Game.Localisation; -using osu.Game.Overlays; -using osuTK; - -namespace osu.Game.Screens.Edit.Timing -{ - internal partial class SyncSection : CompositeDrawable - { - private readonly BindableBool syncBookmarks = new BindableBool(true); - private readonly BindableBool syncPreviewPoint = new BindableBool(true); - - private RoundedButton syncButton = null!; - - private const float header_height = 50; - - [Resolved] - private EditorBeatmap beatmap { get; set; } = null!; - - [Resolved] - private Editor? editor { get; set; } - - [Resolved] - private BeatmapManager beatmaps { get; set; } = null!; - - [Resolved] - private IBindable working { get; set; } = null!; - - [Resolved] - private IDialogOverlay? dialogOverlay { get; set; } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Masking = true; - CornerRadius = 5; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colours.Background4, - RelativeSizeAxes = Axes.Both, - }, - new Container - { - RelativeSizeAxes = Axes.X, - Height = header_height, - Padding = new MarginPadding { Horizontal = 10 }, - Child = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = EditorStrings.Sync, - } - }, - new Container - { - Y = header_height, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new FillFlowContainer - { - Padding = new MarginPadding(10) { Top = 0 }, - Spacing = new Vector2(10), - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new FormCheckBox - { - Caption = EditorDialogsStrings.SyncTimingOptionBookmarks, - Current = { BindTarget = syncBookmarks }, - }, - new FormCheckBox - { - Caption = EditorDialogsStrings.SyncTimingOptionPreviewPoint, - Current = { BindTarget = syncPreviewPoint }, - }, - syncButton = new RoundedButton - { - RelativeSizeAxes = Axes.X, - Text = EditorStrings.SyncToAllDifficulties, - TooltipText = EditorStrings.SyncToAllDifficultiesTooltip, - Action = () => dialogOverlay?.Push(new SyncTimingConfirmationDialog(syncToAllOtherDifficulties)), - } - } - } - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - syncBookmarks.BindValueChanged(_ => updateEnabledState()); - syncPreviewPoint.BindValueChanged(_ => updateEnabledState()); - updateEnabledState(); - } - - private void updateEnabledState() - { - bool canSyncToOtherDifficulties = working.Value.BeatmapSetInfo.Beatmaps.Count > 1; - - syncBookmarks.Disabled = !canSyncToOtherDifficulties; - syncPreviewPoint.Disabled = !canSyncToOtherDifficulties; - - syncButton.Enabled.Value = canSyncToOtherDifficulties && (syncBookmarks.Value || syncPreviewPoint.Value); - } - - private void syncToAllOtherDifficulties() - { - if (working.Value.BeatmapSetInfo.Beatmaps.Count <= 1) - return; - - if (!syncBookmarks.Value && !syncPreviewPoint.Value) - return; - - var set = working.Value.BeatmapSetInfo; - var current = beatmap.BeatmapInfo; - int[] sourceBookmarks = beatmap.Bookmarks.ToArray(); - int sourcePreviewTime = beatmap.BeatmapInfo.Metadata.PreviewTime; - - foreach (var beatmapInfo in set.Beatmaps) - { - if (beatmapInfo.Equals(current)) - continue; - - try - { - var targetWorking = beatmaps.GetWorkingBeatmap(beatmapInfo); - var playable = targetWorking.GetPlayableBeatmap(beatmapInfo.Ruleset); - - if (syncBookmarks.Value) - playable.Bookmarks = sourceBookmarks.ToArray(); - - if (syncPreviewPoint.Value) - beatmapInfo.Metadata.PreviewTime = sourcePreviewTime; - - beatmaps.Save(beatmapInfo, playable, targetWorking.GetSkin(), targetWorking.Storyboard); - } - catch (Exception e) - { - Logger.Error(e, $@"Failed to sync bookmarks/preview to {beatmapInfo.GetDisplayTitle()}"); - return; - } - } - - editor?.SaveAndReload(withDialog: false); - } - } -} From 0e8cc1893f3347c163104ead2fd05ba5c8e8c977 Mon Sep 17 00:00:00 2001 From: Hivie Date: Thu, 30 Jul 2026 13:00:18 +0100 Subject: [PATCH 4/4] remove ellipsis --- osu.Game/Localisation/EditorStrings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index d12dfbb04af3..e21eca1e3274 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -310,9 +310,9 @@ public static class EditorStrings public static LocalisableString SampleSet => new TranslatableString(getKey(@"sample_set"), @"Sample set"); /// - /// "Sync to all difficulties..." + /// "Sync to all difficulties" /// - public static LocalisableString SyncToAllDifficulties => new TranslatableString(getKey(@"sync_to_all_difficulties"), @"Sync to all difficulties..."); + public static LocalisableString SyncToAllDifficulties => new TranslatableString(getKey(@"sync_to_all_difficulties"), @"Sync to all difficulties"); private static string getKey(string key) => $@"{prefix}:{key}"; }