1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-09-26 13:15:56 -04:00

Added textbox. Added keyboard manager. Added name entry.

This commit is contained in:
Tim Sarbin 2018-11-23 20:51:32 -05:00
parent 4c020c13bc
commit 1983d0d40e
11 changed files with 221 additions and 9 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -8,6 +9,6 @@ namespace OpenDiablo2.Common.Interfaces
{ {
public interface IFont : IDisposable public interface IFont : IDisposable
{ {
Size CalculateSize(string text);
} }
} }

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Interfaces
{
public delegate void OnKeyPressed(char charcode);
public interface IKeyboardInfoProvider
{
OnKeyPressed KeyPressCallback { get; set; }
bool KeyIsPressed(int scancode);
}
}

View File

@ -74,6 +74,7 @@
<Compile Include="Enums\eMPQFormatVersion.cs" /> <Compile Include="Enums\eMPQFormatVersion.cs" />
<Compile Include="Interfaces\IFont.cs" /> <Compile Include="Interfaces\IFont.cs" />
<Compile Include="Interfaces\IGameEngine.cs" /> <Compile Include="Interfaces\IGameEngine.cs" />
<Compile Include="Interfaces\IKeyboardInfoProvider.cs" />
<Compile Include="Interfaces\ILabel.cs" /> <Compile Include="Interfaces\ILabel.cs" />
<Compile Include="Interfaces\IMPQProvider.cs" /> <Compile Include="Interfaces\IMPQProvider.cs" />
<Compile Include="Interfaces\IMusicProvider.cs" /> <Compile Include="Interfaces\IMusicProvider.cs" />

View File

@ -93,6 +93,7 @@ namespace OpenDiablo2.Common
public static string WideButtonBlank = "data\\global\\ui\\FrontEnd\\WideButtonBlank.dc6"; public static string WideButtonBlank = "data\\global\\ui\\FrontEnd\\WideButtonBlank.dc6";
public static string MediumButtonBlank = "data\\global\\ui\\FrontEnd\\MediumButtonBlank.dc6"; public static string MediumButtonBlank = "data\\global\\ui\\FrontEnd\\MediumButtonBlank.dc6";
public static string CancelButton = "data\\global\\ui\\FrontEnd\\CancelButtonBlank.dc6"; public static string CancelButton = "data\\global\\ui\\FrontEnd\\CancelButtonBlank.dc6";
public static string TextBox2 = "data\\global\\ui\\FrontEnd\\textbox2.dc6";
// --- Data --- // --- Data ---
// TODO: Doesn't sound right :) // TODO: Doesn't sound right :)

View File

@ -22,6 +22,8 @@ namespace OpenDiablo2.Core
builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance(); builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance();
builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance(); builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance();
builder.RegisterType<Button>().AsSelf().InstancePerDependency(); builder.RegisterType<Button>().AsSelf().InstancePerDependency();
builder.RegisterType<TextBox>().AsSelf().InstancePerDependency();
} }
} }
} }

View File

@ -79,6 +79,7 @@
<Compile Include="ResourceManager.cs" /> <Compile Include="ResourceManager.cs" />
<Compile Include="TextDictionary.cs" /> <Compile Include="TextDictionary.cs" />
<Compile Include="UI\Button.cs" /> <Compile Include="UI\Button.cs" />
<Compile Include="UI\TextBox.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj"> <ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">

View File

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenDiablo2.Common;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.Core.UI
{
public sealed class TextBox
{
private readonly IRenderWindow renderWindow;
private ISprite sprite;
private IFont font;
private ILabel label, linebar;
private float frameTime = 0f;
private Point location = new Point();
public Point Location
{
get => location;
set
{
if (location == value)
return;
location = value;
label.Location = new Point(value.X + 6, value.Y + 3);
linebar.Location = new Point(value.X + 6 + label.TextArea.Width, value.Y + 3);
sprite.Location = new Point(value.X, value.Y + sprite.LocalFrameSize.Height);
}
}
private string text = "";
public string Text
{
get => text;
set
{
if (text == value)
return;
text = value;
// Max width is 130
var newSize = font.CalculateSize(value);
if (newSize.Width < 130)
{
label.Text = value;
linebar.Location = new Point(location.X + 6 + newSize.Width, location.Y + 3);
return;
}
var newStr = value.Substring(1);
while(true)
{
newSize = font.CalculateSize(newStr);
if (newSize.Width >= 130)
{
newStr = newStr.Substring(1);
continue;
}
label.Text = newStr;
linebar.Location = new Point(location.X + 6 + newSize.Width, location.Y + 3);
break;
}
}
}
public TextBox(IRenderWindow renderWindow)
{
this.renderWindow = renderWindow;
sprite = renderWindow.LoadSprite(ResourcePaths.TextBox2, Palettes.Units);
font = renderWindow.LoadFont(ResourcePaths.FontFormal11, Palettes.Units);
label = renderWindow.CreateLabel(font);
linebar = renderWindow.CreateLabel(font);
linebar.Text = "_";
}
public void Update(long ms)
{
frameTime += ms / 500f;
while (frameTime >= 1f)
frameTime -= 1f;
}
public void Render()
{
renderWindow.Draw(sprite);
renderWindow.Draw(label);
if (frameTime < 0.5)
renderWindow.Draw(linebar);
}
}
}

View File

@ -2,6 +2,7 @@
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -30,5 +31,18 @@ namespace OpenDiablo2.SDL2_
{ {
sprite.Dispose(); sprite.Dispose();
} }
public Size CalculateSize(string text)
{
int w = 0;
int h = 0;
foreach(byte ch in text)
{
w += font.CharacterMetric[ch].Width;
h = Math.Max(h, font.CharacterMetric[ch].Height);
}
return new Size(w, h);
}
} }
} }

View File

@ -9,10 +9,11 @@ using System.IO;
using System.Drawing; using System.Drawing;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using Autofac; using Autofac;
using System.Runtime.InteropServices;
namespace OpenDiablo2.SDL2_ namespace OpenDiablo2.SDL2_
{ {
public sealed class SDL2RenderWindow : IRenderWindow, IRenderTarget, IMouseInfoProvider public sealed class SDL2RenderWindow : IRenderWindow, IRenderTarget, IMouseInfoProvider, IKeyboardInfoProvider
{ {
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
@ -25,6 +26,8 @@ namespace OpenDiablo2.SDL2_
public bool RightMouseDown { get; internal set; } = false; public bool RightMouseDown { get; internal set; } = false;
public bool ReserveMouse { get; set; } = false; public bool ReserveMouse { get; set; } = false;
public OnKeyPressed KeyPressCallback { get; set; }
private readonly IMPQProvider mpqProvider; private readonly IMPQProvider mpqProvider;
private readonly IPaletteProvider paletteProvider; private readonly IPaletteProvider paletteProvider;
private readonly IResourceManager resourceManager; private readonly IResourceManager resourceManager;
@ -51,8 +54,8 @@ namespace OpenDiablo2.SDL2_
if (renderer == IntPtr.Zero) if (renderer == IntPtr.Zero)
throw new ApplicationException($"Unable to create SDL Window: {SDL.SDL_GetError()}"); throw new ApplicationException($"Unable to create SDL Window: {SDL.SDL_GetError()}");
SDL.SDL_SetRenderDrawBlendMode(renderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL.SDL_SetRenderDrawBlendMode(renderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
SDL.SDL_ShowCursor(0); SDL.SDL_ShowCursor(0);
IsRunning = true; IsRunning = true;
@ -68,6 +71,14 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_Quit(); SDL.SDL_Quit();
} }
public unsafe bool KeyIsPressed(int scancode)
{
int numKeys;
byte* keys = (byte*)SDL.SDL_GetKeyboardState(out numKeys);
return keys[scancode] > 0;
}
public void Clear() public void Clear()
{ {
SDL.SDL_SetRenderTarget(renderer, IntPtr.Zero); SDL.SDL_SetRenderTarget(renderer, IntPtr.Zero);
@ -81,7 +92,7 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_RenderPresent(renderer); SDL.SDL_RenderPresent(renderer);
} }
public void Update() public unsafe void Update()
{ {
while (SDL.SDL_PollEvent(out SDL.SDL_Event evt) != 0) while (SDL.SDL_PollEvent(out SDL.SDL_Event evt) != 0)
{ {
@ -117,6 +128,16 @@ namespace OpenDiablo2.SDL2_
break; break;
} }
} }
else if (evt.type == SDL.SDL_EventType.SDL_KEYDOWN)
{
if (evt.key.keysym.sym == SDL.SDL_Keycode.SDLK_BACKSPACE && KeyPressCallback != null)
KeyPressCallback('\b');
}
else if (evt.type == SDL.SDL_EventType.SDL_TEXTINPUT)
{
KeyPressCallback?.Invoke(Marshal.PtrToStringAnsi((IntPtr)evt.text.text)[0]);
continue;
}
else if (evt.type == SDL.SDL_EventType.SDL_QUIT) else if (evt.type == SDL.SDL_EventType.SDL_QUIT)
{ {

View File

@ -53,14 +53,16 @@ namespace OpenDiablo2.Scenes
private readonly IMusicProvider musicProvider; private readonly IMusicProvider musicProvider;
private readonly ISceneManager sceneManager; private readonly ISceneManager sceneManager;
private readonly ITextDictionary textDictionary; private readonly ITextDictionary textDictionary;
private readonly IKeyboardInfoProvider keyboardInfoProvider;
private bool showEntryUi = false; private bool showEntryUi = false;
private eHero? selectedHero = null; private eHero? selectedHero = null;
private float secondTimer; private float secondTimer;
private ISprite backgroundSprite, campfireSprite; private ISprite backgroundSprite, campfireSprite;
private IFont headingFont, heroDescFont; private IFont headingFont, heroDescFont, uiFont;
private ILabel headingLabel, heroClassLabel, heroDesc1Label, heroDesc2Label, heroDesc3Label; private ILabel headingLabel, heroClassLabel, heroDesc1Label, heroDesc2Label, heroDesc3Label, characterNameLabel;
private Button exitButton, okButton; private Button exitButton, okButton;
private TextBox characterNameTextBox;
private Dictionary<eHero, HeroRenderInfo> heroRenderInfo = new Dictionary<eHero, HeroRenderInfo>(); private Dictionary<eHero, HeroRenderInfo> heroRenderInfo = new Dictionary<eHero, HeroRenderInfo>();
public SelectHeroClass( public SelectHeroClass(
@ -71,7 +73,9 @@ namespace OpenDiablo2.Scenes
IMusicProvider musicProvider, IMusicProvider musicProvider,
ISceneManager sceneManager, ISceneManager sceneManager,
Func<eButtonType, Button> createButton, Func<eButtonType, Button> createButton,
ITextDictionary textDictionary Func<TextBox> createTextBox,
ITextDictionary textDictionary,
IKeyboardInfoProvider keyboardInfoProvider
) )
{ {
this.renderWindow = renderWindow; this.renderWindow = renderWindow;
@ -80,6 +84,7 @@ namespace OpenDiablo2.Scenes
this.mouseInfoProvider = mouseInfoProvider; this.mouseInfoProvider = mouseInfoProvider;
this.sceneManager = sceneManager; this.sceneManager = sceneManager;
this.textDictionary = textDictionary; this.textDictionary = textDictionary;
this.keyboardInfoProvider = keyboardInfoProvider;
backgroundSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectBackground, Palettes.Fechar); backgroundSprite = renderWindow.LoadSprite(ResourcePaths.CharacterSelectBackground, Palettes.Fechar);
@ -195,6 +200,7 @@ namespace OpenDiablo2.Scenes
headingFont = renderWindow.LoadFont(ResourcePaths.Font30, Palettes.Units); headingFont = renderWindow.LoadFont(ResourcePaths.Font30, Palettes.Units);
heroDescFont = renderWindow.LoadFont(ResourcePaths.Font16, Palettes.Units); heroDescFont = renderWindow.LoadFont(ResourcePaths.Font16, Palettes.Units);
uiFont = renderWindow.LoadFont(ResourcePaths.Font16, Palettes.Units);
headingLabel = renderWindow.CreateLabel(headingFont); headingLabel = renderWindow.CreateLabel(headingFont);
headingLabel.Text = textDictionary.Translate("strSelectHeroClass"); headingLabel.Text = textDictionary.Translate("strSelectHeroClass");
@ -208,17 +214,26 @@ namespace OpenDiablo2.Scenes
heroDesc2Label = renderWindow.CreateLabel(heroDescFont); heroDesc2Label = renderWindow.CreateLabel(heroDescFont);
heroDesc3Label = renderWindow.CreateLabel(heroDescFont); heroDesc3Label = renderWindow.CreateLabel(heroDescFont);
characterNameLabel = renderWindow.CreateLabel(uiFont);
characterNameLabel.Text = "Character Name"; // TODO Translation table
characterNameLabel.Location = new Point(320, 475);
characterNameLabel.TextColor = Color.FromArgb(216, 196, 128);
exitButton = createButton(eButtonType.Medium); exitButton = createButton(eButtonType.Medium);
exitButton.Text = "EXIT"; exitButton.Text = "EXIT"; // TODO Translation table
exitButton.Location = new Point(30, 540); exitButton.Location = new Point(30, 540);
exitButton.OnActivate = OnExitClicked; exitButton.OnActivate = OnExitClicked;
okButton = createButton(eButtonType.Medium); okButton = createButton(eButtonType.Medium);
okButton.Text = "OK"; okButton.Text = "OK"; // TODO Translation table
okButton.Location = new Point(630, 540); okButton.Location = new Point(630, 540);
okButton.OnActivate = OnOkclicked; okButton.OnActivate = OnOkclicked;
okButton.Enabled = false; okButton.Enabled = false;
characterNameTextBox = createTextBox();
characterNameTextBox.Text = "";
characterNameTextBox.Location = new Point(320, 493);
} }
private void OnOkclicked() private void OnOkclicked()
@ -235,6 +250,9 @@ namespace OpenDiablo2.Scenes
heroRenderInfo[hero].Stance = eHeroStance.Idle; heroRenderInfo[hero].Stance = eHeroStance.Idle;
} }
showEntryUi = false; showEntryUi = false;
keyboardInfoProvider.KeyPressCallback = null;
characterNameTextBox.Text = "";
okButton.Enabled = false;
sceneManager.ChangeScene("Main Menu"); sceneManager.ChangeScene("Main Menu");
} }
@ -258,7 +276,11 @@ namespace OpenDiablo2.Scenes
exitButton.Render(); exitButton.Render();
if (showEntryUi) if (showEntryUi)
{
renderWindow.Draw(characterNameLabel);
okButton.Render(); okButton.Render();
characterNameTextBox.Render();
}
} }
private void RenderHeros() private void RenderHeros()
@ -312,6 +334,27 @@ namespace OpenDiablo2.Scenes
} }
private void OnKeyPressed(char charcode)
{
if (charcode == '\b')
{
if (characterNameTextBox.Text.Length == 0)
return;
characterNameTextBox.Text = characterNameTextBox.Text.Substring(0, characterNameTextBox.Text.Length - 1);
okButton.Enabled = characterNameTextBox.Text.Length >= 2;
return;
}
if (characterNameTextBox.Text.Length >= 15)
return;
if (!"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".Contains(charcode))
return;
characterNameTextBox.Text += charcode;
okButton.Enabled = characterNameTextBox.Text.Length >= 2;
}
public void Update(long ms) public void Update(long ms)
{ {
float seconds = ((float)ms / 1500f); float seconds = ((float)ms / 1500f);
@ -319,6 +362,9 @@ namespace OpenDiablo2.Scenes
while (secondTimer >= 1f) while (secondTimer >= 1f)
secondTimer -= 1f; secondTimer -= 1f;
if (keyboardInfoProvider.KeyPressCallback == null)
keyboardInfoProvider.KeyPressCallback = OnKeyPressed;
// Don't update hero selection if one of them is walking to or from the campfire // Don't update hero selection if one of them is walking to or from the campfire
var canSelect = heroRenderInfo.All(x => x.Value.Stance == eHeroStance.Idle || x.Value.Stance == eHeroStance.IdleSelected || x.Value.Stance == eHeroStance.Selected); var canSelect = heroRenderInfo.All(x => x.Value.Stance == eHeroStance.Idle || x.Value.Stance == eHeroStance.IdleSelected || x.Value.Stance == eHeroStance.Selected);
@ -332,6 +378,7 @@ namespace OpenDiablo2.Scenes
exitButton.Update(); exitButton.Update();
okButton.Update(); okButton.Update();
characterNameTextBox.Update(ms);
} }
private void UpdateHeroSelectionHover(eHero hero, long ms, bool canSelect) private void UpdateHeroSelectionHover(eHero hero, long ms, bool canSelect)

View File

@ -72,6 +72,13 @@ namespace OpenDiablo2
return (buttonType) => componentContext.Resolve<Button>(new NamedParameter("buttonLayout", ButtonLayout.Values[buttonType])); return (buttonType) => componentContext.Resolve<Button>(new NamedParameter("buttonLayout", ButtonLayout.Values[buttonType]));
}); });
/* Uncomment the below if we support multiple textbox types
containerBuilder.Register<Func<TextBox>>(c =>
{
var componentContext = c.Resolve<IComponentContext>();
return () => componentContext.Resolve<TextBox>();
});
*/
return containerBuilder; return containerBuilder;
} }