mirror of
https://github.com/ppy/osu.git
synced 2026-05-19 05:49:52 +08:00
239951eda9
- Closes https://github.com/ppy/osu/issues/35389 Same as: https://github.com/ppy/osu/blob/2efe0c95e63817f312f5fb12cc60dd56bee0023b/osu.Game/Screens/Edit/Editor.cs#L1173-L1180 There's also seeking hit objects and sample points, but the seeks are relatively close to each other and probably useless when playing(?). If we want to make those cases not stuck at the same point in time, I believe the leniency should be lower than 1000 ms. With the above, that is why I just copy-pasted the code, as we may want to have different leniencies. Edit: forgot the automated label thing, will not label next time
157 lines
5.8 KiB
C#
157 lines
5.8 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.UserInterface;
|
|
using osu.Framework.Input.Bindings;
|
|
using osu.Framework.Input.Events;
|
|
using osu.Framework.Timing;
|
|
using osu.Game.Graphics.UserInterface;
|
|
using osu.Game.Input.Bindings;
|
|
using osu.Game.Localisation;
|
|
using osu.Game.Overlays;
|
|
using osu.Game.Screens.Edit.Components.Menus;
|
|
|
|
namespace osu.Game.Screens.Edit
|
|
{
|
|
public partial class BookmarkController : Component, IKeyBindingHandler<GlobalAction>
|
|
{
|
|
/// <summary>
|
|
/// Bookmarks menu item (with submenu containing options). Should be added to the <see cref="Editor"/>'s global menu.
|
|
/// </summary>
|
|
public EditorMenuItem Menu { get; private set; }
|
|
|
|
[Resolved]
|
|
private EditorClock clock { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private EditorBeatmap editorBeatmap { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private IDialogOverlay? dialogOverlay { get; set; }
|
|
|
|
private readonly BindableList<int> bookmarks = new BindableList<int>();
|
|
|
|
private readonly EditorMenuItem removeBookmarkMenuItem;
|
|
private readonly EditorMenuItem seekToPreviousBookmarkMenuItem;
|
|
private readonly EditorMenuItem seekToNextBookmarkMenuItem;
|
|
private readonly EditorMenuItem resetBookmarkMenuItem;
|
|
|
|
public BookmarkController()
|
|
{
|
|
Menu = new EditorMenuItem(EditorStrings.Bookmarks)
|
|
{
|
|
Items = new MenuItem[]
|
|
{
|
|
new EditorMenuItem(EditorStrings.AddBookmark, MenuItemType.Standard, addBookmarkAtCurrentTime)
|
|
{
|
|
Hotkey = new Hotkey(GlobalAction.EditorAddBookmark),
|
|
},
|
|
removeBookmarkMenuItem = new EditorMenuItem(EditorStrings.RemoveClosestBookmark, MenuItemType.Destructive, removeClosestBookmark)
|
|
{
|
|
Hotkey = new Hotkey(GlobalAction.EditorRemoveClosestBookmark)
|
|
},
|
|
seekToPreviousBookmarkMenuItem = new EditorMenuItem(EditorStrings.SeekToPreviousBookmark, MenuItemType.Standard, () => seekBookmark(-1))
|
|
{
|
|
Hotkey = new Hotkey(GlobalAction.EditorSeekToPreviousBookmark)
|
|
},
|
|
seekToNextBookmarkMenuItem = new EditorMenuItem(EditorStrings.SeekToNextBookmark, MenuItemType.Standard, () => seekBookmark(1))
|
|
{
|
|
Hotkey = new Hotkey(GlobalAction.EditorSeekToNextBookmark)
|
|
},
|
|
resetBookmarkMenuItem = new EditorMenuItem(EditorStrings.ResetBookmarks, MenuItemType.Destructive, () => dialogOverlay?.Push(new BookmarkResetDialog(editorBeatmap)))
|
|
}
|
|
};
|
|
}
|
|
|
|
protected override void LoadComplete()
|
|
{
|
|
base.LoadComplete();
|
|
|
|
bookmarks.BindTo(editorBeatmap.Bookmarks);
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
bool hasAnyBookmark = bookmarks.Count > 0;
|
|
bool hasBookmarkCloseEnoughForDeletion = bookmarks.Any(b => Math.Abs(b - clock.CurrentTimeAccurate) < 2000);
|
|
|
|
removeBookmarkMenuItem.Action.Disabled = !hasBookmarkCloseEnoughForDeletion;
|
|
seekToPreviousBookmarkMenuItem.Action.Disabled = !hasAnyBookmark;
|
|
seekToNextBookmarkMenuItem.Action.Disabled = !hasAnyBookmark;
|
|
resetBookmarkMenuItem.Action.Disabled = !hasAnyBookmark;
|
|
}
|
|
|
|
private void addBookmarkAtCurrentTime()
|
|
{
|
|
int bookmark = (int)clock.CurrentTimeAccurate;
|
|
int idx = bookmarks.BinarySearch(bookmark);
|
|
if (idx < 0)
|
|
bookmarks.Insert(~idx, bookmark);
|
|
}
|
|
|
|
private void removeClosestBookmark()
|
|
{
|
|
if (removeBookmarkMenuItem.Action.Disabled)
|
|
return;
|
|
|
|
int closestBookmark = bookmarks.MinBy(b => Math.Abs(b - clock.CurrentTimeAccurate));
|
|
bookmarks.Remove(closestBookmark);
|
|
}
|
|
|
|
private void seekBookmark(int direction)
|
|
{
|
|
// In the case of a backwards seek while playing, it can be hard to jump before a bookmark.
|
|
// Adding some lenience here makes it more user-friendly.
|
|
double seekLenience = clock.IsRunning ? 1000 * ((IAdjustableClock)clock).Rate : 0;
|
|
|
|
int? targetBookmark = direction < 1
|
|
? bookmarks.Cast<int?>().LastOrDefault(b => b < clock.CurrentTimeAccurate - seekLenience)
|
|
: bookmarks.Cast<int?>().FirstOrDefault(b => b > clock.CurrentTimeAccurate);
|
|
|
|
if (targetBookmark != null)
|
|
clock.SeekSmoothlyTo(targetBookmark.Value);
|
|
}
|
|
|
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
|
{
|
|
switch (e.Action)
|
|
{
|
|
case GlobalAction.EditorSeekToPreviousBookmark:
|
|
seekBookmark(-1);
|
|
return true;
|
|
|
|
case GlobalAction.EditorSeekToNextBookmark:
|
|
seekBookmark(1);
|
|
return true;
|
|
}
|
|
|
|
if (e.Repeat)
|
|
return false;
|
|
|
|
switch (e.Action)
|
|
{
|
|
case GlobalAction.EditorAddBookmark:
|
|
addBookmarkAtCurrentTime();
|
|
return true;
|
|
|
|
case GlobalAction.EditorRemoveClosestBookmark:
|
|
removeClosestBookmark();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
|
{
|
|
}
|
|
}
|
|
}
|