public override bool HandleRotation(float angle) { if (SelectedBlueprints.Count == 1) { // for single items, rotate around the origin rather than the selection centre. ((Drawable)SelectedBlueprints.First().Item).Rotation += angle; } else { var selectionQuad = getSelectionQuad(); foreach (var b in SelectedBlueprints) { var drawableItem = (Drawable)b.Item; var rotatedPosition = RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, selectionQuad.Centre, angle); updateDrawablePosition(drawableItem, rotatedPosition); drawableItem.Rotation += angle; } } // this isn't always the case but let's be lenient for now. return(true); }
private bool containsSelectionInCurrentBlueprintContainer() { var items = SelectedBlueprints.Select(x => x.Item); // check any selected items that is not in current blueprint container. return(SelectedItems.Any(x => items.Contains(x))); }
private MenuItem createMultiNoteDisplayPropertyMenuItem(IEnumerable <Note> selectedObject) { var display = selectedObject.Count(x => x.Display) >= selectedObject.Count(x => !x.Display); var displayText = display ? "Hide" : "Show"; return(new OsuMenuItem($"{displayText} {selectedObject.Count()} notes.", display ? MenuItemType.Destructive : MenuItemType.Standard, () => { SelectedBlueprints.OfType <NoteSelectionBlueprint>().ForEach(x => x.ChangeDisplay(!display)); })); }
private MenuItem createMultiNoteDisplayPropertyMenuItem(IEnumerable <Note> selectedObject) { var display = selectedObject.Count(x => x.Display) >= selectedObject.Count(x => !x.Display); var displayText = display ? "Hide" : "Show"; return(new OsuMenuItem($"{displayText} {selectedObject.Count()} notes.", display ? MenuItemType.Destructive : MenuItemType.Standard, () => { var selectedNotes = SelectedBlueprints.Select(x => x.Item).OfType <Note>().ToList(); noteManager.ChangeDisplay(selectedNotes, !display); })); }
public override bool HandleFlip(Direction direction) { var selectionQuad = GetSurroundingQuad(SelectedBlueprints.Select(b => b.ScreenSpaceSelectionPoint)); foreach (var b in SelectedBlueprints) { var drawableItem = (Drawable)b.Item; drawableItem.Position = drawableItem.Parent.ToLocalSpace(GetFlippedPosition(direction, selectionQuad, b.ScreenSpaceSelectionPoint)) - drawableItem.AnchorPosition; drawableItem.Scale *= new Vector2( direction == Direction.Horizontal ? -1 : 1, direction == Direction.Vertical ? -1 : 1 ); } return(true); }
/// <summary> /// Provide context menu items relevant to current selection. Calling base is not required. /// </summary> /// <param name="selection">The current selection.</param> /// <returns>The relevant menu items.</returns> protected override IEnumerable <MenuItem> GetContextMenuItemsForSelection(IEnumerable <SelectionBlueprint <HitObject> > selection) { if (SelectedBlueprints.All(b => b.Item is IHasComboInformation)) { yield return(new TernaryStateToggleMenuItem("New combo") { State = { BindTarget = SelectionNewComboState } }); } yield return(new OsuMenuItem("Sound") { Items = SelectionSampleStates.Select(kvp => new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray() }); }
/// <summary> /// Ensures that the position of hitobjects remains centred to the mouse position. /// E.g. The hitobject position will change if the editor scrolls while a hitobject is dragged. /// </summary> /// <param name="reference">The <see cref="CrossSelectionBlueprint"/> that received the drag event.</param> private void adjustOrigins(CrossSelectionBlueprint reference) { var referenceParent = (HitObjectContainer)reference.HitObject.Parent; float offsetFromReferenceOrigin = reference.DragPosition.Y - reference.HitObject.OriginPosition.Y; float targetPosition = referenceParent.ToLocalSpace(reference.ScreenSpaceDragPosition).Y - offsetFromReferenceOrigin; // Flip the vertical coordinate space when scrolling downwards if (scrollingInfo.Direction.Value == ScrollingDirection.Down) { targetPosition = targetPosition - referenceParent.DrawHeight; } float movementDelta = targetPosition - reference.HitObject.Position.Y; foreach (var b in SelectedBlueprints.OfType <CrossSelectionBlueprint>()) { b.HitObject.Y += movementDelta; } }
public override bool HandleScale(Vector2 scale, Anchor anchor) { // convert scale to screen space scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero); adjustScaleFromAnchor(ref scale, anchor); // the selection quad is always upright, so use an AABB rect to make mutating the values easier. var selectionRect = getSelectionQuad().AABBFloat; // If the selection has no area we cannot scale it if (selectionRect.Area == 0) { return(false); } // copy to mutate, as we will need to compare to the original later on. var adjustedRect = selectionRect; // first, remove any scale axis we are not interested in. if (anchor.HasFlagFast(Anchor.x1)) { scale.X = 0; } if (anchor.HasFlagFast(Anchor.y1)) { scale.Y = 0; } bool shouldAspectLock = // for now aspect lock scale adjustments that occur at corners.. (!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1)) // ..or if any of the selection have been rotated. // this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway). || SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation, 0)); if (shouldAspectLock) { if (anchor.HasFlagFast(Anchor.x1)) { // if dragging from the horizontal centre, only a vertical component is available. scale.X = scale.Y / selectionRect.Height * selectionRect.Width; } else { // in all other cases (arbitrarily) use the horizontal component for aspect lock. scale.Y = scale.X / selectionRect.Width * selectionRect.Height; } } if (anchor.HasFlagFast(Anchor.x0)) { adjustedRect.X -= scale.X; } if (anchor.HasFlagFast(Anchor.y0)) { adjustedRect.Y -= scale.Y; } adjustedRect.Width += scale.X; adjustedRect.Height += scale.Y; // scale adjust applied to each individual item should match that of the quad itself. var scaledDelta = new Vector2( MathF.Max(adjustedRect.Width / selectionRect.Width, 0), MathF.Max(adjustedRect.Height / selectionRect.Height, 0) ); foreach (var b in SelectedBlueprints) { var drawableItem = (Drawable)b.Item; // each drawable's relative position should be maintained in the scaled quad. var screenPosition = b.ScreenSpaceSelectionPoint; var relativePositionInOriginal = new Vector2( (screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width, (screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height ); var newPositionInAdjusted = new Vector2( adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X, adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y ); updateDrawablePosition(drawableItem, newPositionInAdjusted); drawableItem.Scale *= scaledDelta; } return(true); }
/// <summary> /// A screen-space quad surrounding all selected drawables, accounting for their full displayed size. /// </summary> /// <returns></returns> private Quad getSelectionQuad() => GetSurroundingQuad(SelectedBlueprints.SelectMany(b => b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray()));