using OpenDiablo2.Common; using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Models; using System; using System.Drawing; namespace OpenDiablo2.Core.UI { public sealed class Button : IButton { private readonly IMouseInfoProvider mouseInfoProvider; private readonly IRenderWindow renderWindow; private readonly ISoundProvider musicProvider; private readonly ButtonLayout buttonLayout; private readonly byte[] sfxButtonClick; public OnActivateDelegate OnActivate { get; set; } public OnToggleDelegate OnToggle { get; set; } private Point location = new Point(); public Point Location { get => location; set { location = value; sprite.Location = value; } } private readonly int buttonWidth; private readonly int buttonHeight; private readonly ISprite sprite; private readonly IFont font; private readonly ILabel label; private bool pressed = false; private bool active = false; // When true, button is actively being focus pressed private bool activeLock = false; // When true, we have locked the mouse from everything else public bool Toggled { get; private set; } = false; private Point labelOffset = new Point(); public Size ClickableRect { get; set; } public bool AllowFrameChange { get; set; } = true; private bool enabled = true; public bool Enabled { get => enabled; set { if (value == enabled) return; enabled = value; sprite.Darken = !enabled; } } private string text; public string Text { get => text; set { text = value; UpdateText(); } } public Button( ButtonLayout buttonLayout, IRenderWindow renderWindow, IMouseInfoProvider mouseInfoProvider, ISoundProvider soundProvider, IMPQProvider mpqProvider ) { this.buttonLayout = buttonLayout; this.renderWindow = renderWindow; this.mouseInfoProvider = mouseInfoProvider; this.musicProvider = soundProvider; font = renderWindow.LoadFont(ResourcePaths.FontExocet10, Palettes.Units); label = renderWindow.CreateLabel(font); sprite = renderWindow.LoadSprite(buttonLayout.ResourceName, buttonLayout.PaletteName, true); // TODO: Less stupid way of doing this would be nice buttonWidth = 0; buttonHeight = 0; for (int i = 0; i < buttonLayout.XSegments; i++) { sprite.Frame = i; buttonWidth += sprite.LocalFrameSize.Width; buttonHeight = Math.Max(buttonHeight, sprite.LocalFrameSize.Height); } label.MaxWidth = buttonWidth - 8; label.Alignment = Common.Enums.eTextAlign.Centered; sfxButtonClick = mpqProvider.GetBytes(ResourcePaths.SFXButtonClick); } public bool Toggle() { Toggled = !Toggled; OnToggle?.Invoke(Toggled); return Toggled; } public bool Toggle(bool isToggled) { if(Toggled != isToggled) { OnToggle?.Invoke(isToggled); Toggled = isToggled; } return isToggled; } public void Update() { if (!enabled) { // Prevent sticky locks if (activeLock && mouseInfoProvider.ReserveMouse) { activeLock = false; mouseInfoProvider.ReserveMouse = false; } active = false; return; } int clickWidth = ClickableRect.Width > 0 ? ClickableRect.Width : buttonWidth; int clickHeight = ClickableRect.Height > 0 ? ClickableRect.Height : buttonHeight; var hovered = mouseInfoProvider.MouseX >= location.X && mouseInfoProvider.MouseX < (location.X + clickWidth) && mouseInfoProvider.MouseY >= location.Y && mouseInfoProvider.MouseY < (location.Y + clickHeight); if (!activeLock && hovered && mouseInfoProvider.LeftMouseDown && !mouseInfoProvider.ReserveMouse) { // The button is being pressed down mouseInfoProvider.ReserveMouse = true; active = true; musicProvider.PlaySfx(sfxButtonClick); } else if (active && !mouseInfoProvider.LeftMouseDown) { mouseInfoProvider.ReserveMouse = false; active = false; if (hovered) { OnActivate?.Invoke(); if (buttonLayout.Toggleable) { Toggle(); } } } else if (!active && mouseInfoProvider.LeftMouseDown) { activeLock = true; } else if (activeLock && !mouseInfoProvider.LeftMouseDown) { activeLock = false; } pressed = hovered && mouseInfoProvider.LeftMouseDown && active; } public void Render() { var frame = buttonLayout.BaseFrame; if (AllowFrameChange) { if (Toggled && pressed) { frame = buttonLayout.BaseFrame + 3; } else if (pressed) { frame = buttonLayout.BaseFrame + 1; } else if (Toggled) { frame = buttonLayout.BaseFrame + 2; } } renderWindow.Draw(sprite, buttonLayout.XSegments, 1, frame); var offset = pressed ? -2 : 0; label.Location = new Point(location.X + offset + labelOffset.X, location.Y - offset + labelOffset.Y); renderWindow.Draw(label); } private void UpdateText() { label.Text = text; label.TextColor = Color.FromArgb(75, 75, 75); var offsetX = (buttonWidth / 2) - (label.TextArea.Width / 2); var offsetY = (buttonHeight / 2) - (label.TextArea.Height / 2); labelOffset = new Point(offsetX, offsetY - 5); } public void Dispose() { sprite.Dispose(); font.Dispose(); label.Dispose(); } } }