mirror of
https://github.com/osukey/osukey.git
synced 2025-08-06 16:13:57 +09:00
Simplify and rearrange SkinSelectionHandler
The file has been restructured and reworded such that there are as few
differences as possible from b36b40cb34
.
This commit is contained in:
@ -20,87 +20,6 @@ namespace osu.Game.Skinning.Editor
|
|||||||
{
|
{
|
||||||
public class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
|
public class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
|
||||||
{
|
{
|
||||||
private const string closest_text = @"Closest";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The hash code of the "Closest" menu item in the anchor point context menu.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Needs only be constant and distinct from any <see cref="Anchor"/> values.</remarks>
|
|
||||||
private static readonly int closest_text_hash = closest_text.GetHashCode();
|
|
||||||
|
|
||||||
private static readonly Dictionary<int, string> anchor_menu_items;
|
|
||||||
private static readonly Dictionary<int, string> origin_menu_items;
|
|
||||||
|
|
||||||
static SkinSelectionHandler()
|
|
||||||
{
|
|
||||||
var anchorMenuSubset = new[]
|
|
||||||
{
|
|
||||||
Anchor.TopLeft,
|
|
||||||
Anchor.TopCentre,
|
|
||||||
Anchor.TopRight,
|
|
||||||
Anchor.CentreLeft,
|
|
||||||
Anchor.Centre,
|
|
||||||
Anchor.CentreRight,
|
|
||||||
Anchor.BottomLeft,
|
|
||||||
Anchor.BottomCentre,
|
|
||||||
Anchor.BottomRight,
|
|
||||||
};
|
|
||||||
|
|
||||||
var texts = anchorMenuSubset.Select(a => a.ToString()).Prepend(closest_text).ToArray();
|
|
||||||
var hashes = anchorMenuSubset.Cast<int>().Prepend(closest_text_hash).ToArray();
|
|
||||||
|
|
||||||
var anchorPairs = hashes.Zip(texts, (k, v) => new KeyValuePair<int, string>(k, v)).ToArray();
|
|
||||||
anchor_menu_items = new Dictionary<int, string>(anchorPairs);
|
|
||||||
var originPairs = anchorPairs.Where(pair => pair.Key != closest_text_hash);
|
|
||||||
origin_menu_items = new Dictionary<int, string>(originPairs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Anchor getClosestAnchorForDrawable(Drawable drawable)
|
|
||||||
{
|
|
||||||
var parent = drawable.Parent;
|
|
||||||
if (parent == null)
|
|
||||||
return drawable.Anchor;
|
|
||||||
|
|
||||||
var screenPosition = getOriginPositionFromQuad(drawable.ScreenSpaceDrawQuad, drawable.Origin);
|
|
||||||
var absolutePosition = parent.ToLocalSpace(screenPosition);
|
|
||||||
var factor = parent.RelativeToAbsoluteFactor;
|
|
||||||
|
|
||||||
var result = default(Anchor);
|
|
||||||
|
|
||||||
result |= getTieredComponent(absolutePosition.X / factor.X, Anchor.x0, Anchor.x1, Anchor.x2);
|
|
||||||
result |= getTieredComponent(absolutePosition.Y / factor.Y, Anchor.y0, Anchor.y1, Anchor.y2);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Anchor getTieredComponent(float component, Anchor tier0, Anchor tier1, Anchor tier2)
|
|
||||||
{
|
|
||||||
if (component >= 2 / 3f)
|
|
||||||
return tier2;
|
|
||||||
|
|
||||||
if (component >= 1 / 3f)
|
|
||||||
return tier1;
|
|
||||||
|
|
||||||
return tier0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2 getOriginPositionFromQuad(in Quad quad, Anchor origin)
|
|
||||||
{
|
|
||||||
var result = quad.TopLeft;
|
|
||||||
|
|
||||||
if (origin.HasFlagFast(Anchor.x2))
|
|
||||||
result.X += quad.Width;
|
|
||||||
else if (origin.HasFlagFast(Anchor.x1))
|
|
||||||
result.X += quad.Width / 2f;
|
|
||||||
|
|
||||||
if (origin.HasFlagFast(Anchor.y2))
|
|
||||||
result.Y += quad.Height;
|
|
||||||
else if (origin.HasFlagFast(Anchor.y1))
|
|
||||||
result.Y += quad.Height / 2f;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinEditor skinEditor { get; set; }
|
private SkinEditor skinEditor { get; set; }
|
||||||
|
|
||||||
@ -267,41 +186,48 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint<ISkinnableDrawable>> selection)
|
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint<ISkinnableDrawable>> selection)
|
||||||
{
|
{
|
||||||
static int checkAnchor(ISkinnableDrawable item)
|
var closestItem = new TernaryStateRadioMenuItem("Closest", MenuItemType.Standard, _ => applyClosestAnchor())
|
||||||
{
|
{
|
||||||
if (item.OverridesClosestAnchor)
|
State = { Value = GetStateFromSelection(selection, c => !c.Item.OverridesClosestAnchor) }
|
||||||
{
|
|
||||||
var drawable = (Drawable)item;
|
|
||||||
return (int)drawable.Anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return closest_text_hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return new OsuMenuItem(nameof(Anchor))
|
|
||||||
{
|
|
||||||
Items = createAnchorItems(anchor_menu_items, checkAnchor, applyAnchor).ToArray()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
yield return new OsuMenuItem(nameof(Origin))
|
yield return new OsuMenuItem("Anchor")
|
||||||
{
|
{
|
||||||
// Origins can't be "closest" so we just cast to int
|
Items = createAnchorItems((i, a) => i.OverridesClosestAnchor && ((Drawable)i).Anchor == a, applyAnchor)
|
||||||
Items = createAnchorItems(origin_menu_items, d => (int)((Drawable)d).Origin, applyOrigin).ToArray()
|
.Prepend(closestItem)
|
||||||
|
.ToArray()
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new OsuMenuItem("Origin")
|
||||||
|
{
|
||||||
|
Items = createAnchorItems((i, o) => ((Drawable)i).Origin == o, applyOrigin).ToArray()
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var item in base.GetContextMenuItemsForSelection(selection))
|
foreach (var item in base.GetContextMenuItemsForSelection(selection))
|
||||||
yield return item;
|
yield return item;
|
||||||
|
|
||||||
IEnumerable<TernaryStateMenuItem> createAnchorItems(IDictionary<int, string> items, Func<ISkinnableDrawable, int> checkFunction, Action<int> applyFunction) =>
|
IEnumerable<TernaryStateMenuItem> createAnchorItems(Func<ISkinnableDrawable, Anchor, bool> checkFunction, Action<Anchor> applyFunction)
|
||||||
items.Select(pair =>
|
{
|
||||||
|
var displayableAnchors = new[]
|
||||||
{
|
{
|
||||||
var (hash, text) = pair;
|
Anchor.TopLeft,
|
||||||
|
Anchor.TopCentre,
|
||||||
return new TernaryStateRadioMenuItem(text, MenuItemType.Standard, _ => applyFunction(hash))
|
Anchor.TopRight,
|
||||||
|
Anchor.CentreLeft,
|
||||||
|
Anchor.Centre,
|
||||||
|
Anchor.CentreRight,
|
||||||
|
Anchor.BottomLeft,
|
||||||
|
Anchor.BottomCentre,
|
||||||
|
Anchor.BottomRight,
|
||||||
|
};
|
||||||
|
return displayableAnchors.Select(a =>
|
||||||
|
{
|
||||||
|
return new TernaryStateRadioMenuItem(a.ToString(), MenuItemType.Standard, _ => applyFunction(a))
|
||||||
{
|
{
|
||||||
State = { Value = GetStateFromSelection(selection, c => checkFunction(c.Item) == hash) }
|
State = { Value = GetStateFromSelection(selection, c => checkFunction(c.Item, a)) }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updateDrawablePosition(Drawable drawable, Vector2 screenSpacePosition)
|
private static void updateDrawablePosition(Drawable drawable, Vector2 screenSpacePosition)
|
||||||
@ -310,10 +236,8 @@ namespace osu.Game.Skinning.Editor
|
|||||||
drawable.Parent.ToLocalSpace(screenSpacePosition) - drawable.AnchorPosition;
|
drawable.Parent.ToLocalSpace(screenSpacePosition) - drawable.AnchorPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyOrigin(int hash)
|
private void applyOrigin(Anchor anchor)
|
||||||
{
|
{
|
||||||
var anchor = (Anchor)hash;
|
|
||||||
|
|
||||||
foreach (var item in SelectedItems)
|
foreach (var item in SelectedItems)
|
||||||
{
|
{
|
||||||
var drawable = (Drawable)item;
|
var drawable = (Drawable)item;
|
||||||
@ -321,8 +245,6 @@ namespace osu.Game.Skinning.Editor
|
|||||||
var previousOrigin = drawable.OriginPosition;
|
var previousOrigin = drawable.OriginPosition;
|
||||||
drawable.Origin = anchor;
|
drawable.Origin = anchor;
|
||||||
drawable.Position += drawable.OriginPosition - previousOrigin;
|
drawable.Position += drawable.OriginPosition - previousOrigin;
|
||||||
|
|
||||||
updateDrawableAnchorIfUsingClosest(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,22 +255,75 @@ namespace osu.Game.Skinning.Editor
|
|||||||
private Quad getSelectionQuad() =>
|
private Quad getSelectionQuad() =>
|
||||||
GetSurroundingQuad(SelectedBlueprints.SelectMany(b => b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray()));
|
GetSurroundingQuad(SelectedBlueprints.SelectMany(b => b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray()));
|
||||||
|
|
||||||
private void applyAnchor(int hash)
|
private void applyAnchor(Anchor anchor)
|
||||||
{
|
{
|
||||||
foreach (var item in SelectedItems)
|
foreach (var item in SelectedItems)
|
||||||
{
|
{
|
||||||
var drawable = (Drawable)item;
|
var drawable = (Drawable)item;
|
||||||
|
|
||||||
var (overridesClosest, anchor) =
|
item.OverridesClosestAnchor = true;
|
||||||
hash == closest_text_hash
|
|
||||||
? (false, getClosestAnchorForDrawable(drawable))
|
|
||||||
: (true, (Anchor)hash);
|
|
||||||
|
|
||||||
item.OverridesClosestAnchor = overridesClosest;
|
|
||||||
updateDrawableAnchor(drawable, anchor);
|
updateDrawableAnchor(drawable, anchor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyClosestAnchor()
|
||||||
|
{
|
||||||
|
foreach (var item in SelectedItems)
|
||||||
|
{
|
||||||
|
var drawable = (Drawable)item;
|
||||||
|
|
||||||
|
item.OverridesClosestAnchor = false;
|
||||||
|
updateDrawableAnchor(drawable, getClosestAnchorForDrawable(drawable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Anchor getClosestAnchorForDrawable(Drawable drawable)
|
||||||
|
{
|
||||||
|
var parent = drawable.Parent;
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
return drawable.Anchor;
|
||||||
|
|
||||||
|
var screenPosition = getOriginPositionFromQuad(drawable.ScreenSpaceDrawQuad, drawable.Origin);
|
||||||
|
var absolutePosition = parent.ToLocalSpace(screenPosition);
|
||||||
|
var factor = parent.RelativeToAbsoluteFactor;
|
||||||
|
|
||||||
|
var result = default(Anchor);
|
||||||
|
|
||||||
|
result |= getTieredComponent(absolutePosition.X / factor.X, Anchor.x0, Anchor.x1, Anchor.x2);
|
||||||
|
result |= getTieredComponent(absolutePosition.Y / factor.Y, Anchor.y0, Anchor.y1, Anchor.y2);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector2 getOriginPositionFromQuad(in Quad quad, Anchor origin)
|
||||||
|
{
|
||||||
|
var result = quad.TopLeft;
|
||||||
|
|
||||||
|
if (origin.HasFlagFast(Anchor.x2))
|
||||||
|
result.X += quad.Width;
|
||||||
|
else if (origin.HasFlagFast(Anchor.x1))
|
||||||
|
result.X += quad.Width / 2f;
|
||||||
|
|
||||||
|
if (origin.HasFlagFast(Anchor.y2))
|
||||||
|
result.Y += quad.Height;
|
||||||
|
else if (origin.HasFlagFast(Anchor.y1))
|
||||||
|
result.Y += quad.Height / 2f;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Anchor getTieredComponent(float component, Anchor tier0, Anchor tier1, Anchor tier2)
|
||||||
|
{
|
||||||
|
if (component >= 2 / 3f)
|
||||||
|
return tier2;
|
||||||
|
|
||||||
|
if (component >= 1 / 3f)
|
||||||
|
return tier1;
|
||||||
|
|
||||||
|
return tier0;
|
||||||
|
}
|
||||||
|
|
||||||
private static void updateDrawableAnchor(Drawable drawable, Anchor anchor)
|
private static void updateDrawableAnchor(Drawable drawable, Anchor anchor)
|
||||||
{
|
{
|
||||||
var previousAnchor = drawable.AnchorPosition;
|
var previousAnchor = drawable.AnchorPosition;
|
||||||
|
Reference in New Issue
Block a user