#region license // This file is part of Vocaluxe. // // Vocaluxe is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Vocaluxe is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Vocaluxe. If not, see . #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using VocaluxeLib.Menu.SingNotes; using VocaluxeLib.Menu.SongMenu; namespace VocaluxeLib.Menu { public abstract class CObjectInteractions { protected readonly List _Elements = new List(); private int _Selection; private Point _PrevMouse; protected bool _Active; protected readonly COrderedDictionaryLite _Buttons; protected readonly COrderedDictionaryLite _Texts; protected readonly COrderedDictionaryLite _Backgrounds; protected readonly COrderedDictionaryLite _Statics; protected readonly COrderedDictionaryLite _SelectSlides; protected readonly COrderedDictionaryLite _SongMenus; protected readonly COrderedDictionaryLite _Lyrics; protected readonly COrderedDictionaryLite _SingNotes; protected readonly COrderedDictionaryLite _NameSelections; protected readonly COrderedDictionaryLite _Equalizers; protected readonly COrderedDictionaryLite _Playlists; protected readonly COrderedDictionaryLite _ParticleEffects; protected readonly COrderedDictionaryLite _ProgressBars; protected readonly COrderedDictionaryLite _RatingPopups; private readonly SColorF _HighlightColor = new SColorF(1, 0, 0, 0); protected CObjectInteractions() { _Backgrounds = new COrderedDictionaryLite(this); _Buttons = new COrderedDictionaryLite(this); _Texts = new COrderedDictionaryLite(this); _Statics = new COrderedDictionaryLite(this); _SelectSlides = new COrderedDictionaryLite(this); _SongMenus = new COrderedDictionaryLite(this); _Lyrics = new COrderedDictionaryLite(this); _SingNotes = new COrderedDictionaryLite(this); _NameSelections = new COrderedDictionaryLite(this); _Equalizers = new COrderedDictionaryLite(this); _Playlists = new COrderedDictionaryLite(this); _ParticleEffects = new COrderedDictionaryLite(this); _ProgressBars = new COrderedDictionaryLite(this); _RatingPopups = new COrderedDictionaryLite(this); } public virtual void Init() { _Selection = -1; _PrevMouse = new Point(0, 0); _Active = false; } protected virtual void _ClearElements() { _Elements.Clear(); _Backgrounds.Clear(); _Buttons.Clear(); _Texts.Clear(); _Statics.Clear(); _SelectSlides.Clear(); _SongMenus.Clear(); _Lyrics.Clear(); _SingNotes.Clear(); _NameSelections.Clear(); _Equalizers.Clear(); _Playlists.Clear(); _ParticleEffects.Clear(); _ProgressBars.Clear(); _RatingPopups.Clear(); } private void _SetSelected(int newSelection) { var el = _GetElement(_Selection); if (newSelection == _Selection) { // Don't change current selection if (el == null) { _Selection = -1; } else if (!el.Selected) { el.Selected = true; } return; } if (el != null) { el.Selected = false; } _Selection = newSelection; el = _GetElement(_Selection); if (el != null) { el.Selected = true; } } #region MenuHandler public virtual bool HandleInput(SKeyEvent keyEvent) { if (!CBase.Settings.IsTabNavigation()) { if (keyEvent.Key == Keys.Left) { if (_IsSelectionValid() && _Elements[_Selection].Type == EType.SelectSlide && keyEvent.Mod != EModifier.Shift) { keyEvent.Handled = PrevValue(); } else { keyEvent.Handled = _NextElement(keyEvent); } return true; } else if (keyEvent.Key == Keys.Right) { if (_IsSelectionValid() && _Elements[_Selection].Type == EType.SelectSlide && keyEvent.Mod != EModifier.Shift) { keyEvent.Handled = NextValue(); } else { keyEvent.Handled = _NextElement(keyEvent); } return true; } else if (keyEvent.Key == Keys.Up || keyEvent.Key == Keys.Down) { keyEvent.Handled = _NextElement(keyEvent); return true; } } else { if (keyEvent.Key == Keys.Tab) { if (keyEvent.Mod == EModifier.Shift) { PrevElement(); } else { NextElement(); } return true; } if (keyEvent.Key == Keys.Left) { PrevValue(); return true; } else if (keyEvent.Key == Keys.Right) { NextValue(); return true; } } return false; } public virtual bool HandleMouse(SMouseEvent mouseEvent) { ProcessMouseMove(mouseEvent.X, mouseEvent.Y); if (mouseEvent.LB) { ProcessMouseClick(mouseEvent.X, mouseEvent.Y); } _PrevMouse.X = mouseEvent.X; _PrevMouse.Y = mouseEvent.Y; return true; } public virtual bool HandleInputThemeEditor(SKeyEvent keyEvent) { var dx = 0; var dy = 0; if (!keyEvent.KeyPressed) { switch (keyEvent.Key) { case Keys.Up: dy = -1; break; case Keys.Down: dy = 1; break; case Keys.Right: dx = 1; break; case Keys.Left: dx = -1; break; case Keys.Tab: _NextElement(keyEvent); return true; default: return false; } } else { return false; } if ((keyEvent.Mod & EModifier.Ctrl) != EModifier.Ctrl) { dx *= 5; dy *= 5; } if ((keyEvent.Mod & EModifier.Alt) == EModifier.Alt) { _MoveElement(dx, dy); } else if ((keyEvent.Mod & EModifier.Shift) == EModifier.Shift) { _ResizeElement(dx, dy); } else { if (_IsSelectionValid()) { _GetElement(_Selection).Highlighted = false; } if (_NextElement(keyEvent)) { _GetElement(_Selection).Highlighted = true; } } return true; } public virtual bool HandleMouseThemeEditor(SMouseEvent mouseEvent) { if (_IsSelectionValid()) { _GetElement(_Selection).Highlighted = false; } var mouse = new Point(mouseEvent.X, mouseEvent.Y); if ((mouseEvent.Mod & EModifier.Ctrl) != EModifier.Ctrl) { // Clip to raster mouse.X = (int)(Math.Round((double)mouse.X / 5) * 5); mouse.Y = (int)(Math.Round((double)mouse.Y / 5) * 5); } var mouseDX = mouse.X - _PrevMouse.X; var mouseDY = mouse.Y - _PrevMouse.Y; _PrevMouse.X = mouseEvent.X; _PrevMouse.Y = mouseEvent.Y; if (mouseEvent.LBH) { if (mouseEvent.Mod == EModifier.Shift) { _ResizeElement(mouseDX, mouseDX); } else { _MoveElement(mouseDX, mouseDY); } } else { ProcessMouseMove(mouseEvent.X, mouseEvent.Y); } return true; } #endregion MenuHandler private bool _IsSelectionValid() { return _Selection >= 0 && _Selection < _Elements.Count; } protected IMenuElement _GetElement(CInteraction element) { switch (element.Type) { case EType.Background: return _Backgrounds[element.Num]; case EType.Button: return _Buttons[element.Num]; case EType.SelectSlide: return _SelectSlides[element.Num]; case EType.Text: return _Texts[element.Num]; case EType.Static: return _Statics[element.Num]; case EType.SongMenu: return _SongMenus[element.Num]; case EType.Lyric: return _Lyrics[element.Num]; case EType.SingNote: return _SingNotes[element.Num]; case EType.NameSelection: return _NameSelections[element.Num]; case EType.Equalizer: return _Equalizers[element.Num]; case EType.Playlist: return _Playlists[element.Num]; case EType.ParticleEffect: return _ParticleEffects[element.Num]; case EType.ProgressBar: return _ProgressBars[element.Num]; case EType.RatingPopup: return _RatingPopups[element.Num]; } throw new ArgumentException("Invalid element type: " + element.Type); } private IMenuElement _GetElement(int element) { return element >= 0 && element < _Elements.Count ? _GetElement(_Elements[element]) : null; } #region Drawing public virtual void Draw() { if (!_Active) { return; } if (_Elements.Count <= 0) { return; } var items = new List(); for (var i = 0; i < _Elements.Count; i++) { if (_IsVisible(i)) { var zs = new SZSort { Id = i, Z = _GetZValue(i) }; items.Add(zs); } } if (items.Count <= 0) { return; } items.Sort((s1, s2) => s2.Z.CompareTo(s1.Z)); for (var i = 0; i < items.Count; i++) { var el = _GetElement(items[i].Id); if (el.Highlighted) { CBase.Drawing.DrawRect(_HighlightColor, el.MaxRect); } el.Draw(); } } #endregion Drawing #region Element-Adding protected void _AddBackground(CBackground bg, String key = null) { _AddElement(_Backgrounds.Add(bg, key), EType.Background); } protected void _AddButton(CButton button, String key = null) { _AddElement(_Buttons.Add(button, key), EType.Button); } protected void _AddSelectSlide(CSelectSlide slide, String key = null) { _AddElement(_SelectSlides.Add(slide, key), EType.SelectSlide); } protected void _AddStatic(CStatic stat, String key = null) { _AddElement(_Statics.Add(stat, key), EType.Static); } protected void _AddText(CText text, String key = null) { _AddElement(_Texts.Add(text, key), EType.Text); } protected void _AddSongMenu(ISongMenu songmenu, String key = null) { _AddElement(_SongMenus.Add(songmenu, key), EType.SongMenu); } protected void _AddLyric(CLyric lyric, String key = null) { _AddElement(_Lyrics.Add(lyric, key), EType.Lyric); } protected void _AddSingNote(CSingNotes sn, String key = null) { _AddElement(_SingNotes.Add(sn, key), EType.SingNote); } protected void _AddNameSelection(CNameSelection ns, String key = null) { _AddElement(_NameSelections.Add(ns, key), EType.NameSelection); } protected void _AddEqualizer(CEqualizer eq, String key = null) { _AddElement(_Equalizers.Add(eq, key), EType.Equalizer); } protected void _AddPlaylist(CPlaylist pls, String key = null) { _AddElement(_Playlists.Add(pls, key), EType.Playlist); } protected void _AddParticleEffect(CParticleEffect pe, String key = null) { _AddElement(_ParticleEffects.Add(pe, key), EType.ParticleEffect); } protected void _AddProgressBar(CProgressBar pb, String key = null) { _AddElement(_ProgressBars.Add(pb, key), EType.ProgressBar); } protected void _AddRatingPopup(CRatingPopup rp, String key = null) { _AddElement(_RatingPopups.Add(rp, key), EType.RatingPopup); } #endregion Element-Adding #region InteractionHandling protected void _SelectElement(IMenuElement element) { if (!element.Visible) { return; } for (var i = 0; i < _Elements.Count; i++) { if (_GetElement(i) == element) { _SetSelected(i); return; } } } public void ProcessMouseClick(int x, int y) { if (!_IsSelectionValid()) { return; } if (_Elements[_Selection].Type == EType.SelectSlide) { if (_SelectSlides[_Elements[_Selection].Num].Visible) { _SelectSlides[_Elements[_Selection].Num].ProcessMouseLBClick(x, y); } } } public void ProcessMouseMove(int x, int y) { _SelectByMouse(x, y); if (!_IsSelectionValid()) { return; } if (_Elements[_Selection].Type == EType.SelectSlide) { if (_SelectSlides[_Elements[_Selection].Num].Visible) { _SelectSlides[_Elements[_Selection].Num].ProcessMouseMove(x, y); } } } private void _SelectByMouse(int x, int y) { var z = CBase.Settings.GetZFar(); var element = -1; for (var i = 0; i < _Elements.Count; i++) { if (!_IsSelectable(i)) { continue; } if (!_IsMouseOverElement(x, y, _Elements[i])) { continue; } if (_GetZValue(i) > z) { continue; } z = _GetZValue(i); element = i; } _SetSelected(element); } protected bool _IsMouseOverCurSelection(SMouseEvent mouseEvent) { if (!_IsSelectionValid()) { return false; } return _IsMouseOverElement(mouseEvent.X, mouseEvent.Y, _Elements[_Selection]); } private bool _IsMouseOverElement(int x, int y, CInteraction interact) { var result = CHelper.IsInBounds(_GetRect(interact), x, y); if (result) { return true; } if (interact.Type == EType.SelectSlide) { return CHelper.IsInBounds(_SelectSlides[interact.Num].RectArrowLeft, x, y) || CHelper.IsInBounds(_SelectSlides[interact.Num].RectArrowRight, x, y); } return false; } public void NextElement() { var element = _Selection; for (var i = 0; i < _Elements.Count; i++) { element++; if (element >= _Elements.Count) { element = 0; } if (_IsSelectable(element)) { break; } } _SetSelected(element); } public void PrevElement() { Debug.Assert(_Selection == 0 || _Selection < _Elements.Count); var element = _Selection; for (var i = 0; i < _Elements.Count; i++) { element--; if (element < 0) { element = _Elements.Count - 1; } if (_IsSelectable(element)) { break; } } _SetSelected(element); } /// /// Selects the next best element in a menu. /// /// /// private bool _NextElement(SKeyEvent key) { int element; switch (key.Key) { case Keys.Up: element = _GetNextElement(EDirection.Up); break; case Keys.Right: element = _GetNextElement(EDirection.Right); break; case Keys.Down: element = _GetNextElement(EDirection.Down); break; case Keys.Left: element = _GetNextElement(EDirection.Left); break; case Keys.Tab: if (key.Mod == EModifier.Shift) { PrevElement(); } else { NextElement(); } return true; default: return false; } if (element < 0) { return false; } // select the new element _SetSelected(element); return true; } private int _GetNextElement(EDirection direction) { var distance = float.MaxValue; var min = -1; var currentRect = _IsSelectionValid() ? _GetRect(_Selection) : new SRectF(_PrevMouse.X, _PrevMouse.Y, 1, 1, 1); for (var i = 0; i < _Elements.Count; i++) { if (i != _Selection && _IsSelectable(i)) { var dist = _GetDistanceDirect(direction, currentRect, _GetRect(i)); if (dist >= 0f && dist < distance) { distance = dist; min = i; } } } if (min >= 0) { return min; } for (var i = 0; i < _Elements.Count; i++) { if (i != _Selection && _IsSelectable(i)) { var dist = _GetDistance180(direction, currentRect, _GetRect(i)); if (dist >= 0f && dist < distance) { distance = dist; min = i; } } } if (min >= 0) { return min; } switch (direction) { case EDirection.Up: currentRect = new SRectF(currentRect.X, CBase.Settings.GetRenderH(), 1, 1, currentRect.Z); break; case EDirection.Down: currentRect = new SRectF(currentRect.X, 0, 1, 1, currentRect.Z); break; case EDirection.Left: currentRect = new SRectF(CBase.Settings.GetRenderW(), currentRect.Y, 1, 1, currentRect.Z); break; case EDirection.Right: currentRect = new SRectF(0, currentRect.Y, 1, 1, currentRect.Z); break; } for (var i = 0; i < _Elements.Count; i++) { if (i != _Selection && _IsSelectable(i)) { var dist = _GetDistance180(direction, currentRect, _GetRect(i)); if (dist >= 0f && dist < distance) { distance = dist; min = i; } } } return min; } private static float _GetDistanceDirect(EDirection direction, SRectF current, SRectF other) { switch (direction) { case EDirection.Up: case EDirection.Down: if (!other.X.IsInRange(current.X, current.Right) && !other.Right.IsInRange(current.X, current.Right) && !current.X.IsInRange(other.X, other.Right)) { return float.MaxValue; } break; case EDirection.Left: case EDirection.Right: if (!other.Y.IsInRange(current.Y, current.Bottom) && !other.Bottom.IsInRange(current.Y, current.Bottom) && !current.Y.IsInRange(other.Y, other.Bottom)) { return float.MaxValue; } break; } return _GetDistance180(direction, current, other); } private static float _GetDistance180(EDirection direction, SRectF current, SRectF other) { var source = new PointF(current.X + current.W / 2f, current.Y + current.H / 2f); var dest = new PointF(other.X + other.W / 2f, other.Y + other.H / 2f); var vector = new PointF(dest.X - source.X, dest.Y - source.Y); var distance = (float)Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y); switch (direction) { case EDirection.Up: if (vector.Y < 0f) { return distance; } break; case EDirection.Down: if (vector.Y > 0f) { return distance; } break; case EDirection.Left: if (vector.X < 0f) { return distance; } break; case EDirection.Right: if (vector.X > 0f) { return distance; } break; } return float.MaxValue; } /// /// Selects the next value in a menu element. /// /// True if the next value is selected. False if either there is no next value or the element does not provide such a method. public bool NextValue() { if (_IsSelectionValid() && _Elements[_Selection].Type == EType.SelectSlide) { return _SelectSlides[_Elements[_Selection].Num].SelectNextValue(); } return false; } /// /// Selects the previous value in a menu element. /// /// /// True if the previous value is selected. False if either there is no previous value or the element does not provide such a method. /// public bool PrevValue() { if (_IsSelectionValid() && _Elements[_Selection].Type == EType.SelectSlide) { return _SelectSlides[_Elements[_Selection].Num].SelectPrevValue(); } return false; } private void _AddElement(int num, EType type) { _Elements.Add(new CInteraction(num, type)); if (_IsSelectable(_Selection)) { _SetSelected(_Selection); } else { NextElement(); } } #endregion InteractionHandling #region Element-property getters private bool _IsVisible(int element) { var el = _GetElement(element); return el != null && el.Visible; } private bool _IsSelectable(int element) { var el = _GetElement(element); return el != null && (el.Selectable || CBase.Settings.GetProgramState() == EProgramState.EditTheme); } private SRectF _GetRect(CInteraction element) { return _GetElement(element).Rect; } private SRectF _GetRect(int element) { return _GetRect(_Elements[element]); } private float _GetZValue(int element) { return _GetElement(_Elements[element]).Rect.Z; } #endregion #region Theme Handling private void _MoveElement(int stepX, int stepY) { var el = _GetElement(_Selection); if (el != null) { el.MoveElement(stepX, stepY); } } private void _ResizeElement(int stepW, int stepH) { var el = _GetElement(_Selection); if (el != null) { el.ResizeElement(stepW, stepH); } } #endregion Theme Handling } }