protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e) { IEventDataNode curHotChunk = null; double mouseX = e.GetPosition(this).X; ulong tick = TimeAxis.ScreenToTime(mouseX); // Look through the slicing markers first for (int i = 0; i < this.slicingMarkers.Length; i++) { var chunk = this.slicingMarkers[i]; // Slicing markers are only a pixel wide, so make them selectable w/ padding if (chunk.Visible) { var x = TimeAxis.TimeToScreen(chunk.StartTime); if (mouseX > (x - 3) && mouseX < (x + 3)) { curHotChunk = chunk; break; } } } if (curHotChunk == null) { for (int i = this.sortedNodes.Length - 1; i >= 0; i--) { var chunk = this.sortedNodes[i]; if (chunk.Visible && (tick >= chunk.StartTime) && (tick < chunk.StartTime + chunk.Duration)) { curHotChunk = chunk; break; } // Marker events are drawn at special minimum width, so make them selectable w/ padding if (chunk.Visible && chunk.Style == EventRenderStyle.ParentedMarker) { var x = TimeAxis.TimeToScreen(chunk.StartTime); if (mouseX > (x - 3) && mouseX < (x + 3)) { curHotChunk = chunk; break; } } } } if (curHotChunk != hotChunk) { // Hot chunk has changed hotChunk = curHotChunk; UpdateToolTip(); InvalidateVisual(); } base.OnMouseMove(e); }
protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e) { if (hotChunk != null) { hotChunk = null; toolTip.IsOpen = false; InvalidateVisual(); } base.OnMouseLeave(e); }
double?DrawSlicingMarker(DrawingContext drawingContext, IEventDataNode slicingMarker, double?lastDrawnSlicingMarkerX) { var x = TimeAxis.TimeToScreen(slicingMarker.StartTime); if (x >= 0 && x <= this.ActualWidth) { if (!lastDrawnSlicingMarkerX.HasValue || (x > lastDrawnSlicingMarkerX.Value + 3)) { drawingContext.DrawLine(this.slicingMarkerPen, new Point(x, verticalMargin / 3), new Point(x, this.ActualHeight - verticalMargin + 2)); return(x); } } return(lastDrawnSlicingMarkerX); }
public SelectionEventArgs(IEventDataNode node) { SelectedEvent = node; }
protected override void OnKeyDown(KeyEventArgs e) { bool right = (e.Key == Key.Right); bool left = (e.Key == Key.Left); bool up = (e.Key == Key.Up); bool down = (e.Key == Key.Down); // There might be a better nav model for keyboard multi-select... var selectedChunk = this.selectedChunks.FirstOrDefault(); if (right || left) { if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control)) { // Ctrl + L/R arrows: pan left and right int panScreen = (int)(this.ActualWidth / 8.0); if (right) { panScreen = -panScreen; } TimeAxis.PanByScreenDelta(panScreen); hotChunk = null; UpdateToolTip(); } else if (selectedChunk != null) { IEventDataNode newSelectedChunk = null; if (right) { newSelectedChunk = dataSource.NextNode(selectedChunk); } else if (left) { newSelectedChunk = dataSource.PreviousNode(selectedChunk); } // L/R arrows: select next/prev chunk if (newSelectedChunk != selectedChunk && newSelectedChunk != null) { SelectChunk(newSelectedChunk); } } } else if (up || down) { if (e.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Control)) { // Ctrl + U/D arrows: zoom in and out if (up) { TimeAxis.ZoomIn(); } else { TimeAxis.ZoomOut(); } } e.Handled = true; } base.OnKeyDown(e); }
ChunkDrawData DrawChunk(DrawingContext drawingContext, IEventDataNode chunk, Brush brush, Pen pen, ChunkDrawData previousChunkData, bool forceDraw) { var x1 = TimeAxis.TimeToScreen(chunk.StartTime); var x2 = Math.Max(x1, TimeAxis.TimeToScreen(chunk.StartTime + chunk.Duration)); // Max in case the duration is bogus if (x2 < 0 || x1 > this.ActualWidth) { if (previousChunkData != null) { drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect); } return(null); } var r = new Rect(new Point(x1, verticalMargin), new Size(x2 - x1, this.ActualHeight - (verticalMargin * 2))); var halfHeightRect = new Rect(new Point(x1, (this.ActualHeight - halfHeight) / 2), new Size(x2 - x1, halfHeight)); var style = chunk.Style; if (pen == null) { // Since there is no stroke (pen == null), this is a "normal" chunk with no border // (the bar has already been filled w/ border color) so deflate 1/2 pixel to simulate // a border stroke. r.Inflate(-0.5, -0.5); } else { r.Inflate(0, -0.5); } const int minWidth = 3; // Points-in-time (markers) are always 3px wide if (style == EventRenderStyle.ParentedMarker) { r = new Rect(x1 - Math.Floor((double)minWidth / 2), verticalMargin, minWidth, this.ActualHeight - (verticalMargin * 2)); } // Adjust rect height based on chunk nesting depth if (chunk.ZIndex > 0 && !r.IsEmpty && style != EventRenderStyle.HalfHeight) { double delta = chunk.ZIndex * 2; r.Inflate(0, -delta); } if (forceDraw) { // This is a special (hot or selected) chunk, so enforce a minimum width and always draw it. if (style == EventRenderStyle.HalfHeight) { r = halfHeightRect; } var width = r.Width; if (width < minWidth) { var halfGap = (minWidth - width) / 2; r = new Rect(new Point(r.Left - halfGap, r.Top), new Point(r.Right + halfGap, r.Bottom)); } drawingContext.DrawRectangle(brush, pen, r); return(null); } const double minDistance = 16; if (style == EventRenderStyle.HalfHeight) { r = halfHeightRect; } // Draw the chunk. We may need to merge it with the previous rect if there is one. if (previousChunkData != null) { // We have a previous rect. If it's far enough away from this one, then draw it. // OR, if this one is big enough BY ITSELF to be drawn, draw the previous one. // Otherwise, merge it with this one, and draw the result if it's wide enough. if (previousChunkData.Rect.Right < (r.Left - (minDistance / 4)) || (r.Width >= minDistance)) { drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect); } else { previousChunkData.Rect = Rect.Union(previousChunkData.Rect, r); previousChunkData.Fill = Brushes.Gray; if (previousChunkData.Rect.Width >= minDistance) { drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect); previousChunkData = null; } return(previousChunkData); } } if (r.Width >= minDistance) { drawingContext.DrawRectangle(brush, pen, r); previousChunkData = null; } else { previousChunkData = new ChunkDrawData { Fill = brush, Rect = r, Pen = pen }; } return(previousChunkData); }
void SelectChunk(IEventDataNode chunk) { dataSource.OnSelectionChangedInternal(chunk); this.Focus(); }
ChunkDrawData DrawChunk(DrawingContext drawingContext, IEventDataNode chunk, Brush brush, Pen pen, ChunkDrawData previousChunkData, bool forceDraw) { var x1 = TimeAxis.TimeToScreen(chunk.StartTime); var x2 = Math.Max(x1, TimeAxis.TimeToScreen(chunk.StartTime + chunk.Duration)); // Max in case the duration is bogus if (x2 < 0 || x1 > this.ActualWidth) { if (previousChunkData != null) { drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect); } return null; } var r = new Rect(new Point(x1, verticalMargin), new Size(x2 - x1, this.ActualHeight - (verticalMargin * 2))); var halfHeightRect = new Rect(new Point(x1, (this.ActualHeight - halfHeight) / 2), new Size(x2 - x1, halfHeight)); var style = chunk.Style; if (pen == null) { // Since there is no stroke (pen == null), this is a "normal" chunk with no border // (the bar has already been filled w/ border color) so deflate 1/2 pixel to simulate // a border stroke. r.Inflate(-0.5, -0.5); } else { r.Inflate(0, -0.5); } const int minWidth = 3; // Points-in-time (markers) are always 3px wide if (style == EventRenderStyle.ParentedMarker) { r = new Rect(x1 - Math.Floor((double)minWidth / 2), verticalMargin, minWidth, this.ActualHeight - (verticalMargin * 2)); } // Adjust rect height based on chunk nesting depth if (chunk.ZIndex > 0 && !r.IsEmpty && style != EventRenderStyle.HalfHeight) { double delta = chunk.ZIndex * 2; r.Inflate(0, -delta); } if (forceDraw) { // This is a special (hot or selected) chunk, so enforce a minimum width and always draw it. if (style == EventRenderStyle.HalfHeight) { r = halfHeightRect; } var width = r.Width; if (width < minWidth) { var halfGap = (minWidth - width) / 2; r = new Rect(new Point(r.Left - halfGap, r.Top), new Point(r.Right + halfGap, r.Bottom)); } drawingContext.DrawRectangle(brush, pen, r); return null; } const double minDistance = 16; if (style == EventRenderStyle.HalfHeight) { r = halfHeightRect; } // Draw the chunk. We may need to merge it with the previous rect if there is one. if (previousChunkData != null) { // We have a previous rect. If it's far enough away from this one, then draw it. // OR, if this one is big enough BY ITSELF to be drawn, draw the previous one. // Otherwise, merge it with this one, and draw the result if it's wide enough. if (previousChunkData.Rect.Right < (r.Left - (minDistance / 4)) || (r.Width >= minDistance)) { drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect); } else { previousChunkData.Rect = Rect.Union(previousChunkData.Rect, r); previousChunkData.Fill = Brushes.Gray; if (previousChunkData.Rect.Width >= minDistance) { drawingContext.DrawRectangle(previousChunkData.Fill, previousChunkData.Pen, previousChunkData.Rect); previousChunkData = null; } return previousChunkData; } } if (r.Width >= minDistance) { drawingContext.DrawRectangle(brush, pen, r); previousChunkData = null; } else { previousChunkData = new ChunkDrawData { Fill = brush, Rect = r, Pen = pen }; } return previousChunkData; }
double? DrawSlicingMarker(DrawingContext drawingContext, IEventDataNode slicingMarker, double? lastDrawnSlicingMarkerX) { var x = TimeAxis.TimeToScreen(slicingMarker.StartTime); if (x >= 0 && x <= this.ActualWidth) { if (!lastDrawnSlicingMarkerX.HasValue || (x > lastDrawnSlicingMarkerX.Value + 3)) { drawingContext.DrawLine(this.slicingMarkerPen, new Point(x, verticalMargin / 3), new Point(x, this.ActualHeight - verticalMargin + 2)); return x; } } return lastDrawnSlicingMarkerX; }