private void TableKeyDown(object source, TableKeyEventArgs te) { try { // Fix: keyboard shortcuts are forbidden during a DnD if (_isDragging) { te.Handled = te.KeyEventArgs.Handled = true; return; } KeyEventArgs e = te.KeyEventArgs; switch (e.Key) { case Key.PageUp: Program.GameEngine.Table.BringToFront(Card); e.Handled = te.Handled = true; break; case Key.PageDown: Program.GameEngine.Table.SendToBack(Card); e.Handled = te.Handled = true; break; case Key.P: if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control) && !Card.FaceUp) { if (Card != null) { Card.Peek(); } break; } goto default; default: // Look for a custom shortcut in the game definition ActionShortcut[] shortcuts = Card.Group.CardShortcuts; ActionShortcut match = shortcuts.FirstOrDefault(shortcut => shortcut.Key.Matches(this, te.KeyEventArgs)); if (match != null && Card.Group.CanManipulate()) { // Look for cards to execute it upon, shortcuts are applied to selection first IEnumerable <Card> targets; if (!Selection.IsEmpty()) { targets = Selection.Cards; } else if (Card.CanManipulate()) { targets = Selection.ExtendToSelection(Card); } else { break; } // If the card is on the table, extract the cursor position Point?pos = GroupControl is TableControl ? ((TableControl)GroupControl).MousePosition() : (Point?)null; if (match.ActionDef.AsAction().Execute != null) { ScriptEngine.ExecuteOnCards(match.ActionDef.AsAction().Execute, targets, pos); } else if (match.ActionDef.AsAction().BatchExecute != null) { ScriptEngine.ExecuteOnBatch(match.ActionDef.AsAction().BatchExecute, targets, pos); } e.Handled = te.Handled = true; break; } // Look for a "Move to" shortcut Group group = Player.LocalPlayer.Groups.FirstOrDefault( g => g.MoveToShortcut != null && g.MoveToShortcut.Matches(this, te.KeyEventArgs)); bool toBottom = false; // If no group is found, try to match a shortcut with "Alt" and use it as "Move to bottom" if (group == null) { group = Player.LocalPlayer.Groups.FirstOrDefault( g => g.MoveToShortcut != null && new KeyGesture(g.MoveToShortcut.Key, g.MoveToShortcut.Modifiers | ModifierKeys.Alt). Matches(this, te.KeyEventArgs)); if (group is Pile) { toBottom = true; } } if (group != null && group.CanManipulate()) { Action <Card> moveAction = toBottom ? (c => c.MoveTo(@group, true, @group.Count)) : new Action <Card>(c => c.MoveTo(group, true)); if (!Selection.IsEmpty()) { Selection.ForEachModifiable(moveAction); } else if (count.IsMouseOver) { for (int i = MultipleCards.Count - 1; i >= 0; --i) { var c = (Card)MultipleCards[i]; if (c.CanManipulate()) { moveAction(c); } } } else if (Card.CanManipulate()) { moveAction(Card); } else { break; } e.Handled = te.Handled = true; break; } break; } } catch (Exception e) { Log.Warn("TableKeyDown Error", e); } }
protected void DragCardStarted() { DraggedCards.Clear(); // in theory draggedCards should already be empty, but it's better to recover if there was a problem during last DnD if (!Selection.IsEmpty()) { DraggedCards.AddRange(Selection.Cards); } else if (_isOverCount) { DraggedCards.AddRange(MultipleCards.Cast <Card>()); } else if (Card != null) { DraggedCards.Add(Card); } else { return; } // Fix: Card == null can occur, e.g. hold the mouse down but don't move, dismiss the card with a keyboard shortcut (e.g. Delete) and move the mouse after that (with the left button still down). _isDragging = true; // Keep control of the card and the group it's in foreach (Card c in DraggedCards) { c.KeepControl(); } Card.Group.KeepControl(); // Hides the card view RaiseEvent(new CardEventArgs(CardHoveredEvent, this)); // Starts the drag-and-drop ScaleFactor = TransformToAncestor(_mainWin).TransformBounds(new Rect(0, 0, 1, 1)).Size; //bool rot90 = (Card.Orientation & CardOrientation.Rot90) != 0; _mouseOffset = new Vector(_mousePt.X * Program.GameEngine.Definition.CardWidth / ActualWidth, _mousePt.Y * Program.GameEngine.Definition.CardHeight / ActualHeight); // Create adorners var mwn = _mainWin.Content as Visual; AdornerLayer layer = null; if (mwn != null) { layer = AdornerLayer.GetAdornerLayer(mwn); } double offset = 0; double step = ActualWidth * 1.05; // HACK: if the selected card is in HandControl, its ContentPresenter has a RenderTransform, // which we must account for if (GroupControl is HandControl) { var parent = VisualTreeHelper.GetParent(this) as ContentPresenter; if (parent != null) { step = 1.05 * parent.RenderTransform.TransformBounds(new Rect(0, 0, ActualWidth, 0)).Width; } } foreach (CardControl cardCtrl in Selection.GetCardControls(GroupControl, this)) { // Create an adorner if (Card.Group is Table) { var overlay = new CardDragAdorner(cardCtrl, _mouseOffset); OverlayElements.Add(overlay); if (layer != null) { layer.Add(overlay); } } else { // If there are multiple cards but they don't come from the table, // layout the adorners properly var overlay = new CardDragAdorner(this, cardCtrl, _mouseOffset); OverlayElements.Add(overlay); if (cardCtrl != this) { offset += step; overlay.OffsetBy(offset, 0); overlay.CollapseTo(0, 0, false); } if (layer != null) { layer.Add(overlay); } } // Make the card translucent cardCtrl.Opacity = 0.4; } if (!Selection.IsEmpty() || !_isOverCount) { return; } // Create additional fake adorners when dragging from the count label offset = 0; foreach (Card c in MultipleCards.Skip(1)) { offset += step; var overlay = new CardDragAdorner(this, _mouseOffset); overlay.OffsetBy(offset, 0); overlay.CollapseTo(0, 0, false); OverlayElements.Add(overlay); if (layer != null) { layer.Add(overlay); } } }
protected override void OnCardDropped(object sender, CardsEventArgs e) { e.Handled = e.CanDrop = true; var cardCtrl = e.OriginalSource as CardControl; int delta = Program.GameEngine.Definition.CardHeight - Program.GameEngine.Definition.CardWidth; Table table = Program.GameEngine.Table; Vector mouseOffset; if (cardCtrl != null && (cardCtrl.IsInverted || (Player.LocalPlayer.InvertedTable && !cardCtrl.IsOnTableCanvas))) { mouseOffset = new Vector(Program.GameEngine.Definition.CardWidth - e.MouseOffset.X, Program.GameEngine.Definition.CardHeight - e.MouseOffset.Y); } else { mouseOffset = e.MouseOffset; } Point pt = MousePosition(); pt -= mouseOffset; if (Selection.IsEmpty() || !(Selection.Source is Table)) { if (Program.GameSettings.UseTwoSidedTable && (e.ClickedCard.Orientation & CardOrientation.Rot90) != 0) { // We have to offset the position if we cross the middle line bool newPosInverted = IsInInvertedZone(pt.Y); if (cardCtrl != null && (!cardCtrl.IsInverted && newPosInverted)) { pt.X += delta; pt.Y += delta; } else if (cardCtrl != null && (cardCtrl.IsInverted && !newPosInverted)) { pt.X -= delta; pt.Y -= delta; } } int idx = table.GetCardIndex(e.ClickedCard); if (idx != -1 || table.Visibility != DataNew.Entities.GroupVisibility.Undefined) { e.FaceUp = e.ClickedCard.FaceUp; } if (idx == -1) { idx = table.Cards.Count; } e.ClickedCard.MoveToTable((int)pt.X, (int)pt.Y, e.FaceUp != null && e.FaceUp.Value, idx, false); // If there were other cards (i.e. dragging from a count number in GroupWindow), move them accordingly double xOffset = Program.GameEngine.Definition.CardWidth * 1.05; foreach (Card c in e.Cards.Where(c => c != e.ClickedCard)) { pt.X += xOffset; c.MoveToTable((int)pt.X, (int)pt.Y, e.FaceUp != null && e.FaceUp.Value, idx, false); } } else { // There are multiple cards, coming from the table. Offset them accordingly double dx = pt.X - e.ClickedCard.X; double dy = pt.Y - e.ClickedCard.Y; foreach (Card c in Selection.Cards) { int x = (int)(c.X + dx), y = (int)(c.Y + dy); int idx = table.GetCardIndex(c); // If the card is tapped and has crossed the middle line in a two-sided table, we have to adjust its position if (Program.GameSettings.UseTwoSidedTable && (c.Orientation & CardOrientation.Rot90) != 0) { bool oldPosInverted = IsInInvertedZone(c.Y); bool newPosInverted = IsInInvertedZone(y); if (!oldPosInverted && newPosInverted) { x += delta; y += delta; } else if (oldPosInverted && !newPosInverted) { x -= delta; y -= delta; } } c.MoveToTable(x, y, c.FaceUp, idx, false); } } }