mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge branch 'master' into distance-snapping-test
This commit is contained in:
@ -9,7 +9,8 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Layout;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
@ -41,17 +42,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
InternalChild = Box = CreateBox();
|
||||
}
|
||||
|
||||
protected virtual Drawable CreateBox() => new Container
|
||||
{
|
||||
Masking = true,
|
||||
BorderColour = Color4.White,
|
||||
BorderThickness = SelectionBox.BORDER_RADIUS,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.1f
|
||||
}
|
||||
};
|
||||
protected virtual Drawable CreateBox() => new BoxWithBorders();
|
||||
|
||||
private RectangleF? dragRectangle;
|
||||
|
||||
@ -111,5 +102,75 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
public override void Show() => State = Visibility.Visible;
|
||||
|
||||
public event Action<Visibility> StateChanged;
|
||||
|
||||
public class BoxWithBorders : CompositeDrawable
|
||||
{
|
||||
private readonly LayoutValue cache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||
|
||||
public BoxWithBorders()
|
||||
{
|
||||
AddLayout(cache);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!cache.IsValid)
|
||||
{
|
||||
createContent();
|
||||
cache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
private void createContent()
|
||||
{
|
||||
if (DrawSize == Vector2.Zero)
|
||||
{
|
||||
ClearInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Make lines the same width independent of display resolution.
|
||||
float lineThickness = DrawWidth > 0
|
||||
? DrawWidth / ScreenSpaceDrawQuad.Width * 2
|
||||
: DrawHeight / ScreenSpaceDrawQuad.Height * 2;
|
||||
|
||||
Padding = new MarginPadding(-lineThickness / 2);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = lineThickness,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = lineThickness,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = lineThickness,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = lineThickness,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.1f
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osuTK;
|
||||
|
||||
@ -15,7 +18,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
/// <summary>
|
||||
/// A scrollable list which displays the <see cref="PlaylistItem"/>s in a <see cref="Room"/>.
|
||||
/// </summary>
|
||||
public class DrawableRoomPlaylist : OsuRearrangeableListContainer<PlaylistItem>
|
||||
public class DrawableRoomPlaylist : OsuRearrangeableListContainer<PlaylistItem>, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently-selected item. Selection is visually represented with a border.
|
||||
@ -169,5 +172,78 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
});
|
||||
|
||||
protected virtual DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new DrawableRoomPlaylistItem(item);
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// schedules added as the properties may change value while the drawable items haven't been created yet.
|
||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(scrollToSelection));
|
||||
Items.BindCollectionChanged((_, __) => Scheduler.AddOnce(scrollToSelection), true);
|
||||
}
|
||||
|
||||
private void scrollToSelection()
|
||||
{
|
||||
// SelectedItem and ItemMap/drawable items are managed separately,
|
||||
// so if the item can't be unmapped to a drawable, don't try to scroll to it.
|
||||
// best effort is made to not drop any updates, by subscribing to both sources.
|
||||
if (SelectedItem.Value == null || !ItemMap.TryGetValue(SelectedItem.Value, out var drawableItem))
|
||||
return;
|
||||
|
||||
// ScrollIntoView does not handle non-loaded items appropriately, delay scroll until the item finishes loading.
|
||||
// see: https://github.com/ppy/osu-framework/issues/5158
|
||||
if (!drawableItem.IsLoaded)
|
||||
drawableItem.OnLoadComplete += _ => ScrollContainer.ScrollIntoView(drawableItem);
|
||||
else
|
||||
ScrollContainer.ScrollIntoView(drawableItem);
|
||||
}
|
||||
|
||||
#region Key selection logic (shared with BeatmapCarousel and RoomsContainer)
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SelectNext:
|
||||
selectNext(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SelectPrevious:
|
||||
selectNext(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
private void selectNext(int direction)
|
||||
{
|
||||
if (!AllowSelection)
|
||||
return;
|
||||
|
||||
var visibleItems = ListContainer.AsEnumerable().Where(r => r.IsPresent);
|
||||
|
||||
PlaylistItem item;
|
||||
|
||||
if (SelectedItem.Value == null)
|
||||
item = visibleItems.FirstOrDefault()?.Model;
|
||||
else
|
||||
{
|
||||
if (direction < 0)
|
||||
visibleItems = visibleItems.Reverse();
|
||||
|
||||
item = visibleItems.SkipWhile(r => r.Model != SelectedItem.Value).Skip(1).FirstOrDefault()?.Model;
|
||||
}
|
||||
|
||||
// we already have a valid selection only change selection if we still have a room to switch to.
|
||||
if (item != null)
|
||||
SelectedItem.Value = item;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
#region Key selection logic (shared with BeatmapCarousel)
|
||||
#region Key selection logic (shared with BeatmapCarousel and DrawableRoomPlaylist)
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
|
@ -12,13 +12,14 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
@ -28,20 +29,22 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||
|
||||
private readonly Button button;
|
||||
private HoldButton button;
|
||||
|
||||
public Action Action
|
||||
{
|
||||
set => button.Action = value;
|
||||
}
|
||||
public Action Action { get; set; }
|
||||
|
||||
private readonly OsuSpriteText text;
|
||||
private OsuSpriteText text;
|
||||
|
||||
public HoldForMenuButton()
|
||||
{
|
||||
Direction = FillDirection.Horizontal;
|
||||
Spacing = new Vector2(20, 0);
|
||||
Margin = new MarginPadding(10);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(Player player)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
@ -50,25 +53,20 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
button = new Button
|
||||
button = new HoldButton(player?.Configuration.AllowRestart == false)
|
||||
{
|
||||
HoverGained = () => text.FadeIn(500, Easing.OutQuint),
|
||||
HoverLost = () => text.FadeOut(500, Easing.OutQuint),
|
||||
IsPaused = { BindTarget = IsPaused }
|
||||
IsPaused = { BindTarget = IsPaused },
|
||||
Action = () => Action(),
|
||||
}
|
||||
};
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
private Bindable<double> activationDelay;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
activationDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
||||
activationDelay.BindValueChanged(v =>
|
||||
button.HoldActivationDelay.BindValueChanged(v =>
|
||||
{
|
||||
text.Text = v.NewValue > 0
|
||||
? "hold for menu"
|
||||
@ -102,7 +100,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
}
|
||||
|
||||
private class Button : HoldToConfirmContainer, IKeyBindingHandler<GlobalAction>
|
||||
private class HoldButton : HoldToConfirmContainer, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private SpriteIcon icon;
|
||||
private CircularProgress circularProgress;
|
||||
@ -115,6 +113,16 @@ namespace osu.Game.Screens.Play.HUD
|
||||
public Action HoverGained;
|
||||
public Action HoverLost;
|
||||
|
||||
private const double shake_duration = 20;
|
||||
|
||||
private bool pendingAnimation;
|
||||
private ScheduledDelegate shakeOperation;
|
||||
|
||||
public HoldButton(bool isDangerousAction)
|
||||
: base(isDangerousAction)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
@ -161,11 +169,38 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
private void bind()
|
||||
{
|
||||
circularProgress.Current.BindTo(Progress);
|
||||
Progress.ValueChanged += progress => icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f);
|
||||
((IBindable<double>)circularProgress.Current).BindTo(Progress);
|
||||
Progress.ValueChanged += progress =>
|
||||
{
|
||||
icon.Scale = new Vector2(1 + (float)progress.NewValue * 0.2f);
|
||||
|
||||
if (IsDangerousAction)
|
||||
{
|
||||
Colour = Interpolation.ValueAt(progress.NewValue, Color4.White, Color4.Red, 0, 1, Easing.OutQuint);
|
||||
|
||||
if (progress.NewValue > 0 && progress.NewValue < 1)
|
||||
{
|
||||
shakeOperation ??= Scheduler.AddDelayed(shake, shake_duration, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Child.MoveTo(Vector2.Zero, shake_duration * 2, Easing.OutQuint);
|
||||
shakeOperation?.Cancel();
|
||||
shakeOperation = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private bool pendingAnimation;
|
||||
private void shake()
|
||||
{
|
||||
const float shake_magnitude = 8;
|
||||
|
||||
Child.MoveTo(new Vector2(
|
||||
RNG.NextSingle(-1, 1) * (float)Progress.Value * shake_magnitude,
|
||||
RNG.NextSingle(-1, 1) * (float)Progress.Value * shake_magnitude
|
||||
), shake_duration);
|
||||
}
|
||||
|
||||
protected override void Confirm()
|
||||
{
|
||||
|
Reference in New Issue
Block a user