/// <summary> /// Returns whether the specified note has adjacent black keys next to it /// </summary> /// <param name="note">The white note</param> private static HasBlackKeys CheckIfWhiteNoteHasBlackKeys(NoteEnum note) { HasBlackKeys flags = HasBlackKeys.None; if (note == NoteEnum.None || IsSharp(note)) { return(flags); } if (IsSharp(note - 1)) { flags |= HasBlackKeys.Flat; } if (IsSharp(note + 1)) { flags |= HasBlackKeys.Sharp; } return(flags); }
/// <summary> /// Calculates the note at the specified position /// </summary> /// <param name="point">The point on the keyboard canvas</param> private NoteEnum GetNoteAtPoint(Point point) { int whiteKeyWidth = WhiteKeyWidth; Size clientSize = ClientSize; int x = (clientSize.Width - PianoWidth) / 2; if (point.X < x || point.X >= x + PianoWidth) { return(NoteEnum.None); } int xLoc = point.X - x; NoteEnum whiteNote = WhiteNoteIndexToNote(xLoc / whiteKeyWidth); int height = clientSize.Height - lineThickness; int blackKeyHeight = (height * 4) / 7; if (point.Y < blackKeyHeight) { int blackKeyWidth = (whiteKeyWidth * 9) / 14; int halfBlackKeyWidth = blackKeyWidth / 2; int blackKeyOffset = whiteKeyWidth - halfBlackKeyWidth; HasBlackKeys hasBlackKeys = CheckIfWhiteNoteHasBlackKeys(whiteNote); int locInNote = xLoc % whiteKeyWidth; if (locInNote <= halfBlackKeyWidth && (hasBlackKeys & HasBlackKeys.Flat) == HasBlackKeys.Flat) { return(whiteNote - 1); } else if (locInNote >= blackKeyOffset && (hasBlackKeys & HasBlackKeys.Sharp) == HasBlackKeys.Sharp) { return(whiteNote + 1); } else { return(whiteNote); } } else { return(whiteNote); } }
/// <summary> /// Draws the interactive keyboard /// </summary> protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.CompositingMode = CompositingMode.SourceCopy; g.CompositingQuality = CompositingQuality.HighSpeed; g.PixelOffsetMode = PixelOffsetMode.HighSpeed; g.SmoothingMode = SmoothingMode.HighSpeed; //calculate metrics int whiteKeyWidth = WhiteKeyWidth; int blackKeyWidth = (whiteKeyWidth * 9) / 14; int halfBlackKeyWidth = blackKeyWidth / 2; int blackKeyOffset = whiteKeyWidth - halfBlackKeyWidth; int pianoWidth = PianoWidth; Size clientSize = ClientSize; int x = (clientSize.Width - pianoWidth) / 2; int pianoEnd = x + pianoWidth; const int halfLineThickness = lineThickness / 2; int height = clientSize.Height - lineThickness; Rectangle clipRect = e.ClipRectangle; //fill piano background FillRectangle(g, WhiteKeyBrush, new Rectangle(x, halfLineThickness, pianoWidth, height), clipRect); int i, keyStart; //draw white keys List <Rectangle> pressedWhite = new List <Rectangle>(); Rectangle currentRect; NoteEnum currentNote; for (i = 0; i < WhiteKeyCount; ++i) { keyStart = x + i * whiteKeyWidth; currentNote = WhiteNoteIndexToNote(i); currentRect = new Rectangle(keyStart, halfLineThickness, whiteKeyWidth, height); if ((leftMouseDown && keyStart <= lastCursorLocation.X && keyStart + whiteKeyWidth > lastCursorLocation.X && !IsSharp(GetNoteAtPoint(lastCursorLocation))) || pressedNotes.ContainsKey(currentNote)) { pressedWhite.Add(currentRect); } if (i == MiddleCPos) { FillRectangle(g, MiddleCBrush, currentRect, clipRect); } g.DrawRectangle(KeyOutline, currentRect); } List <Rectangle> pressedBlack = new List <Rectangle>(); int temp; int blackKeyHeight = (height * 4) / 7; //draw black keys for (i = 0; i < WhiteKeyCount; ++i) { temp = i % 7; if (!(temp == 2 || temp == 6)) //skip every 2nd and 6th key { keyStart = x + i * whiteKeyWidth + blackKeyOffset; currentRect = new Rectangle(keyStart, lineThickness, blackKeyWidth, blackKeyHeight); if ((leftMouseDown && lastCursorLocation.Y < blackKeyHeight && keyStart <= lastCursorLocation.X && Math.Min(keyStart + blackKeyWidth, pianoEnd) > lastCursorLocation.X) || pressedNotes.ContainsKey(WhiteNoteIndexToNote(i) + 1)) { pressedBlack.Add(currentRect); } else { FillRectangle(g, BlackKeyBrush, currentRect, clipRect); } } } //draw pressed white keys Rectangle rect; for (i = 0; i < pressedWhite.Count; i++) { rect = pressedWhite[i]; FillRectangle(g, PressedKeyBrush, new Rectangle(rect.X + halfLineThickness, rect.Y + halfLineThickness, rect.Width - lineThickness, rect.Height - lineThickness), clipRect); NoteEnum note = WhiteNoteIndexToNote((rect.X - x) / whiteKeyWidth); HasBlackKeys hasBlackKeys = CheckIfWhiteNoteHasBlackKeys(note); if ((hasBlackKeys & HasBlackKeys.Flat) == HasBlackKeys.Flat && !pressedNotes.ContainsKey((NoteEnum)((int)note - 1))) { FillRectangle(g, BlackKeyBrush, new Rectangle(rect.X - halfBlackKeyWidth, lineThickness, blackKeyWidth, blackKeyHeight), clipRect); } if ((hasBlackKeys & HasBlackKeys.Sharp) == HasBlackKeys.Sharp && !pressedNotes.ContainsKey((NoteEnum)((int)note + 1))) { FillRectangle(g, BlackKeyBrush, new Rectangle(rect.X + blackKeyOffset, lineThickness, blackKeyWidth, blackKeyHeight), clipRect); } } //draw pressed black keys for (i = 0; i < pressedBlack.Count; i++) { rect = pressedBlack[i]; FillRectangle(g, PressedKeyBrush, rect, clipRect); rect.X += halfLineThickness; rect.Y -= halfLineThickness; rect.Width -= lineThickness; g.DrawRectangle(KeyOutline, rect); } if (backgroundBrush == null) { backgroundBrush = new SolidBrush(BackColor); } //draw outside piano background FillRectangle(g, backgroundBrush, new Rectangle(0, 0, x - 1, clientSize.Height), clipRect); FillRectangle(g, backgroundBrush, new Rectangle(x + pianoWidth + halfLineThickness, 0, clientSize.Width - (x + pianoWidth + halfLineThickness), clientSize.Height), clipRect); if (showHint) { //draw render text e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; e.Graphics.CompositingMode = CompositingMode.SourceOver; e.Graphics.CompositingQuality = CompositingQuality.HighQuality; e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; e.Graphics.SmoothingMode = SmoothingMode.HighQuality; using (GraphicsPath path = new GraphicsPath()) { Rectangle bounds = Bounds; bounds.X -= (Parent.Controls[0] == this ? Parent.Controls[1] : Parent.Controls[0]).Width; bounds.Y = blackKeyHeight; bounds.Height -= blackKeyHeight; path.AddString(Text, Font.FontFamily, (int)Font.Style, Font.Size, bounds, textFormat); e.Graphics.FillPath(TextBrush, path); e.Graphics.DrawPath(TextOutline, path); } } base.OnPaint(e); }