Start modularising chat input widget out.

This commit is contained in:
UnknownShadow200 2016-10-27 21:07:59 +11:00
parent 793dd93f0a
commit bd12b17e8c
6 changed files with 155 additions and 135 deletions

View File

@ -59,16 +59,7 @@ namespace ClassicalSharp.Gui {
return (axisSize - elemSize) / 2 + offset; return (axisSize - elemSize) / 2 + offset;
} }
protected bool IsValidInputChar( char c ) { public static bool Contains( int recX, int recY, int width, int height, int x, int y ) {
if( c >= ' ' && c <= '~' ) return true; // ascii
bool isCP437 = Utils.ControlCharReplacements.IndexOf( c ) >= 0 ||
Utils.ExtendedCharReplacements.IndexOf( c ) >= 0;
bool supportsCP437 = game.Server.SupportsFullCP437;
return supportsCP437 && isCP437;
}
protected static bool Contains( int recX, int recY, int width, int height, int x, int y ) {
return x >= recX && y >= recY && x < recX + width && y < recY + height; return x >= recX && y >= recY && x < recX + width && y < recY + height;
} }
} }

View File

@ -19,7 +19,7 @@ namespace ClassicalSharp.Gui.Screens {
HudScreen hud; HudScreen hud;
int chatLines; int chatLines;
ChatTextWidget announcement; ChatTextWidget announcement;
TextInputWidget textInput; InputWidget textInput;
TextGroupWidget status, bottomRight, normalChat, clientStatus; TextGroupWidget status, bottomRight, normalChat, clientStatus;
bool suppressNextPress = true; bool suppressNextPress = true;
int chatIndex; int chatIndex;
@ -50,7 +50,7 @@ namespace ClassicalSharp.Gui.Screens {
} }
void ConstructWidgets() { void ConstructWidgets() {
textInput = new TextInputWidget( game, chatFont ); textInput = new InputWidget( game, chatFont );
textInput.YOffset = 5; textInput.YOffset = 5;
altText = new AltTextInputWidget( game, chatFont, textInput ); altText = new AltTextInputWidget( game, chatFont, textInput );
altText.Init(); altText.Init();
@ -420,7 +420,7 @@ namespace ClassicalSharp.Gui.Screens {
game.Gui.ShowWarning( warning ); game.Gui.ShowWarning( warning );
} else if( game.ClickableChat ) { } else if( game.ClickableChat ) {
for( int i = 0; i < text.Length; i++ ) { for( int i = 0; i < text.Length; i++ ) {
if( !IsValidInputChar( text[i] ) ) { if( !Utils.IsValidInputChar( text[i], game ) ) {
game.Chat.Add( "&eChatline contained characters that can't be sent on this server." ); game.Chat.Add( "&eChatline contained characters that can't be sent on this server." );
return true; return true;
} }

View File

@ -9,7 +9,7 @@ using Android.Graphics;
namespace ClassicalSharp.Gui.Widgets { namespace ClassicalSharp.Gui.Widgets {
public sealed partial class AltTextInputWidget : Widget { public sealed partial class AltTextInputWidget : Widget {
public AltTextInputWidget( Game game, Font font, TextInputWidget parent ) : base( game ) { public AltTextInputWidget( Game game, Font font, InputWidget parent ) : base( game ) {
HorizontalAnchor = Anchor.LeftOrTop; HorizontalAnchor = Anchor.LeftOrTop;
VerticalAnchor = Anchor.BottomOrRight; VerticalAnchor = Anchor.BottomOrRight;
this.font = font; this.font = font;
@ -25,7 +25,7 @@ namespace ClassicalSharp.Gui.Widgets {
public Texture texture; public Texture texture;
readonly Font font; readonly Font font;
TextInputWidget parent; InputWidget parent;
Size elementSize; Size elementSize;
public void SetActive( bool active ) { public void SetActive( bool active ) {

View File

@ -1,15 +1,16 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT // ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System; using System;
using System.Drawing; using System.Drawing;
using OpenTK.Input;
#if ANDROID #if ANDROID
using Android.Graphics; using Android.Graphics;
#endif #endif
namespace ClassicalSharp.Gui.Widgets { namespace ClassicalSharp.Gui.Widgets {
public sealed partial class TextInputWidget : Widget { public sealed class InputWidget : Widget {
const int lines = 3; internal const int lines = 3;
public TextInputWidget( Game game, Font font ) : base( game ) { public InputWidget( Game game, Font font ) : base( game ) {
HorizontalAnchor = Anchor.LeftOrTop; HorizontalAnchor = Anchor.LeftOrTop;
VerticalAnchor = Anchor.BottomOrRight; VerticalAnchor = Anchor.BottomOrRight;
typingLogPos = game.Chat.InputLog.Count; // Index of newest entry + 1. typingLogPos = game.Chat.InputLog.Count; // Index of newest entry + 1.
@ -26,13 +27,16 @@ namespace ClassicalSharp.Gui.Widgets {
defaultHeight = Height = defSize.Height; defaultHeight = Height = defSize.Height;
this.font = font; this.font = font;
inputHandler = new InputWidgetHandler( game, this );
} }
Texture inputTex, caretTex, prefixTex; InputWidgetHandler inputHandler;
int caretPos = -1, typingLogPos = 0; internal Texture inputTex, caretTex, prefixTex;
int defaultCaretWidth, defaultWidth, defaultHeight; internal int caretPos = -1, typingLogPos = 0;
internal int defaultCaretWidth, defaultWidth, defaultHeight;
internal WrappableStringBuffer buffer; internal WrappableStringBuffer buffer;
readonly Font font; internal readonly Font font;
internal string originalText;
FastColour caretCol; FastColour caretCol;
static FastColour backColour = new FastColour( 0, 0, 0, 127 ); static FastColour backColour = new FastColour( 0, 0, 0, 127 );
@ -57,15 +61,15 @@ namespace ClassicalSharp.Gui.Widgets {
caretTex.Render( gfx, caretCol ); caretTex.Render( gfx, caretCol );
} }
string[] parts = new string[lines]; internal string[] parts = new string[lines];
int[] partLens = new int[lines]; int[] partLens = new int[lines];
Size[] sizes = new Size[lines]; Size[] sizes = new Size[lines];
int maxWidth = 0; int maxWidth = 0;
int indexX, indexY; int indexX, indexY;
bool shownWarning; bool shownWarning;
int LineLength { get { return game.Server.SupportsPartialMessages ? 64 : 62; } } internal int LineLength { get { return game.Server.SupportsPartialMessages ? 64 : 62; } }
int TotalChars { get { return LineLength * lines; } } internal int TotalChars { get { return LineLength * lines; } }
public override void Init() { public override void Init() {
X = 5; X = 5;
@ -98,7 +102,7 @@ namespace ClassicalSharp.Gui.Widgets {
CalculateCaretData(); CalculateCaretData();
} }
void CalculateCaretData() { internal void CalculateCaretData() {
if( caretPos >= buffer.Length ) caretPos = -1; if( caretPos >= buffer.Length ) caretPos = -1;
buffer.MakeCoords( caretPos, partLens, out indexX, out indexY ); buffer.MakeCoords( caretPos, partLens, out indexX, out indexY );
DrawTextArgs args = new DrawTextArgs( null, font, true ); DrawTextArgs args = new DrawTextArgs( null, font, true );
@ -285,5 +289,25 @@ namespace ClassicalSharp.Gui.Widgets {
} }
Recreate(); Recreate();
} }
public override bool HandlesKeyPress( char key ) {
if( game.HideGui ) return true;
if( Utils.IsValidInputChar( key, game ) ) {
AppendChar( key );
return true;
}
return false;
}
public override bool HandlesKeyDown( Key key ) {
if( game.HideGui ) return key < Key.F1 || key > Key.F35;
return inputHandler.HandlesKeyDown( key );
}
public override bool HandlesMouseClick( int mouseX, int mouseY, MouseButton button ) {
return inputHandler.HandlesMouseClick( mouseX, mouseY, button );
}
} }
} }

View File

@ -6,21 +6,17 @@ using ClassicalSharp.Entities;
using OpenTK.Input; using OpenTK.Input;
namespace ClassicalSharp.Gui.Widgets { namespace ClassicalSharp.Gui.Widgets {
public sealed partial class TextInputWidget : Widget { public sealed class InputWidgetHandler {
public override bool HandlesKeyPress( char key ) { Game game;
if( game.HideGui ) return true; InputWidget w;
if( IsValidInputChar( key ) ) { public InputWidgetHandler( Game game, InputWidget w ) {
AppendChar( key ); this.game = game;
return true; this.w = w;
}
return false;
} }
public override bool HandlesKeyDown( Key key ) { public bool HandlesKeyDown( Key key ) {
if( game.HideGui )
return key < Key.F1 || key > Key.F35;
bool clipboardDown = OpenTK.Configuration.RunningOnMacOS ? bool clipboardDown = OpenTK.Configuration.RunningOnMacOS ?
(game.IsKeyDown( Key.WinLeft ) || game.IsKeyDown( Key.WinRight )) (game.IsKeyDown( Key.WinLeft ) || game.IsKeyDown( Key.WinRight ))
: (game.IsKeyDown( Key.ControlLeft ) || game.IsKeyDown( Key.ControlRight )); : (game.IsKeyDown( Key.ControlLeft ) || game.IsKeyDown( Key.ControlRight ));
@ -40,9 +36,9 @@ namespace ClassicalSharp.Gui.Widgets {
} }
void TabKey() { void TabKey() {
int pos = caretPos == -1 ? buffer.Length - 1 : caretPos; int pos = w.caretPos == -1 ? w.buffer.Length - 1 : w.caretPos;
int start = pos; int start = pos;
char[] value = buffer.value; char[] value = w.buffer.value;
while( start >= 0 && IsNameChar( value[start] ) ) while( start >= 0 && IsNameChar( value[start] ) )
start--; start--;
@ -64,12 +60,12 @@ namespace ClassicalSharp.Gui.Widgets {
} }
if( matches.Count == 1 ) { if( matches.Count == 1 ) {
if( caretPos == -1 ) pos++; if( w.caretPos == -1 ) pos++;
int len = pos - start; int len = pos - start;
for( int i = 0; i < len; i++ ) for( int i = 0; i < len; i++ )
buffer.DeleteAt( start ); w.buffer.DeleteAt( start );
if( caretPos != -1 ) caretPos -= len; if( w.caretPos != -1 ) w.caretPos -= len;
AppendText( matches[0] ); w.AppendText( matches[0] );
} else if( matches.Count > 1 ) { } else if( matches.Count > 1 ) {
StringBuffer sb = new StringBuffer( Utils.StringLength ); StringBuffer sb = new StringBuffer( Utils.StringLength );
int index = 0; int index = 0;
@ -94,149 +90,148 @@ namespace ClassicalSharp.Gui.Widgets {
void BackspaceKey( bool controlDown ) { void BackspaceKey( bool controlDown ) {
if( controlDown ) { if( controlDown ) {
if( caretPos == -1 ) caretPos = buffer.Length - 1; if( w.caretPos == -1 ) w.caretPos = w.buffer.Length - 1;
int len = buffer.GetBackLength( caretPos ); int len = w.buffer.GetBackLength( w.caretPos );
if( len == 0 ) return; if( len == 0 ) return;
caretPos -= len; w.caretPos -= len;
if( caretPos < 0 ) caretPos = 0; if( w.caretPos < 0 ) w.caretPos = 0;
for( int i = 0; i <= len; i++ ) for( int i = 0; i <= len; i++ )
buffer.DeleteAt( caretPos ); w.buffer.DeleteAt( w.caretPos );
if( caretPos >= buffer.Length ) caretPos = -1; if( w.caretPos >= w.buffer.Length ) w.caretPos = -1;
if( caretPos == -1 && buffer.Length > 0 ) { if( w.caretPos == -1 && w.buffer.Length > 0 ) {
buffer.value[buffer.Length] = ' '; w.buffer.value[w.buffer.Length] = ' ';
} else if( caretPos >= 0 && buffer.value[caretPos] != ' ' ) { } else if( w.caretPos >= 0 && w.buffer.value[w.caretPos] != ' ' ) {
buffer.InsertAt( caretPos, ' ' ); w.buffer.InsertAt( w.caretPos, ' ' );
} }
Recreate(); w.Recreate();
} else if( !buffer.Empty && caretPos != 0 ) { } else if( !w.buffer.Empty && w.caretPos != 0 ) {
int index = caretPos == -1 ? buffer.Length - 1 : caretPos; int index = w.caretPos == -1 ? w.buffer.Length - 1 : w.caretPos;
if( CheckColour( index - 1 ) ) { if( CheckColour( index - 1 ) ) {
DeleteChar(); // backspace XYZ%e to XYZ DeleteChar(); // backspace XYZ%e to XYZ
} else if( CheckColour( index - 2 ) ) { } else if( CheckColour( index - 2 ) ) {
DeleteChar(); DeleteChar(); // backspace XYZ%eH to XYZ DeleteChar(); DeleteChar(); // backspace XYZ%eH to XYZ
} }
DeleteChar(); DeleteChar();
Recreate(); w.Recreate();
} }
} }
bool CheckColour( int index ) { bool CheckColour( int index ) {
if( index < 0 ) return false; if( index < 0 ) return false;
char code = buffer.value[index], col = buffer.value[index + 1]; char code = w.buffer.value[index], col = w.buffer.value[index + 1];
return (code == '%' || code == '&') && game.Drawer2D.ValidColour( col ); return (code == '%' || code == '&') && game.Drawer2D.ValidColour( col );
} }
void DeleteChar() { void DeleteChar() {
if( caretPos == -1 ) { if( w.caretPos == -1 ) {
buffer.DeleteAt( buffer.Length - 1 ); w.buffer.DeleteAt( w.buffer.Length - 1 );
} else { } else {
caretPos--; w.caretPos--;
buffer.DeleteAt( caretPos ); w.buffer.DeleteAt( w.caretPos );
} }
} }
void DeleteKey() { void DeleteKey() {
if( !buffer.Empty && caretPos != -1 ) { if( !w.buffer.Empty && w.caretPos != -1 ) {
buffer.DeleteAt( caretPos ); w.buffer.DeleteAt( w.caretPos );
if( caretPos >= buffer.Length ) caretPos = -1; if( w.caretPos >= w.buffer.Length ) w.caretPos = -1;
Recreate(); w.Recreate();
} }
} }
void LeftKey( bool controlDown ) { void LeftKey( bool controlDown ) {
if( controlDown ) { if( controlDown ) {
if( caretPos == -1 ) if( w.caretPos == -1 )
caretPos = buffer.Length - 1; w.caretPos = w.buffer.Length - 1;
caretPos -= buffer.GetBackLength( caretPos ); w.caretPos -= w.buffer.GetBackLength( w.caretPos );
CalculateCaretData(); w.CalculateCaretData();
return; return;
} }
if( !buffer.Empty ) { if( !w.buffer.Empty ) {
if( caretPos == -1 ) caretPos = buffer.Length; if( w.caretPos == -1 ) w.caretPos = w.buffer.Length;
caretPos--; w.caretPos--;
if( caretPos < 0 ) caretPos = 0; if( w.caretPos < 0 ) w.caretPos = 0;
CalculateCaretData(); w.CalculateCaretData();
} }
} }
void RightKey( bool controlDown ) { void RightKey( bool controlDown ) {
if( controlDown ) { if( controlDown ) {
caretPos += buffer.GetForwardLength( caretPos ); w.caretPos += w.buffer.GetForwardLength( w.caretPos );
if( caretPos >= buffer.Length ) caretPos = -1; if( w.caretPos >= w.buffer.Length ) w.caretPos = -1;
CalculateCaretData(); w.CalculateCaretData();
return; return;
} }
if( !buffer.Empty && caretPos != -1 ) { if( !w.buffer.Empty && w.caretPos != -1 ) {
caretPos++; w.caretPos++;
if( caretPos >= buffer.Length ) caretPos = -1; if( w.caretPos >= w.buffer.Length ) w.caretPos = -1;
CalculateCaretData(); w.CalculateCaretData();
} }
} }
string originalText;
void UpKey( bool controlDown ) { void UpKey( bool controlDown ) {
if( controlDown ) { if( controlDown ) {
int pos = caretPos == -1 ? buffer.Length : caretPos; int pos = w.caretPos == -1 ? w.buffer.Length : w.caretPos;
if( pos < LineLength ) return; if( pos < w.LineLength ) return;
caretPos = pos - LineLength; w.caretPos = pos - w.LineLength;
CalculateCaretData(); w.CalculateCaretData();
return; return;
} }
if( typingLogPos == game.Chat.InputLog.Count ) if( w.typingLogPos == game.Chat.InputLog.Count )
originalText = buffer.ToString(); w.originalText = w.buffer.ToString();
if( game.Chat.InputLog.Count > 0 ) { if( game.Chat.InputLog.Count > 0 ) {
typingLogPos--; w.typingLogPos--;
if( typingLogPos < 0 ) typingLogPos = 0; if( w.typingLogPos < 0 ) w.typingLogPos = 0;
buffer.Clear(); w.buffer.Clear();
buffer.Append( 0, game.Chat.InputLog[typingLogPos] ); w.buffer.Append( 0, game.Chat.InputLog[w.typingLogPos] );
caretPos = -1; w.caretPos = -1;
Recreate(); w.Recreate();
} }
} }
void DownKey( bool controlDown ) { void DownKey( bool controlDown ) {
if( controlDown ) { if( controlDown ) {
if( caretPos == -1 || caretPos >= (lines - 1) * LineLength ) return; if( w.caretPos == -1 || w.caretPos >= (w.parts.Length - 1) * w.LineLength ) return;
caretPos += LineLength; w.caretPos += w.LineLength;
CalculateCaretData(); w.CalculateCaretData();
return; return;
} }
if( game.Chat.InputLog.Count > 0 ) { if( game.Chat.InputLog.Count > 0 ) {
typingLogPos++; w.typingLogPos++;
buffer.Clear(); w.buffer.Clear();
if( typingLogPos >= game.Chat.InputLog.Count ) { if( w.typingLogPos >= game.Chat.InputLog.Count ) {
typingLogPos = game.Chat.InputLog.Count; w.typingLogPos = game.Chat.InputLog.Count;
if( originalText != null ) if( w.originalText != null )
buffer.Append( 0, originalText ); w.buffer.Append( 0, w.originalText );
} else { } else {
buffer.Append( 0, game.Chat.InputLog[typingLogPos] ); w.buffer.Append( 0, game.Chat.InputLog[w.typingLogPos] );
} }
caretPos = -1; w.caretPos = -1;
Recreate(); w.Recreate();
} }
} }
void HomeKey() { void HomeKey() {
if( buffer.Empty ) return; if( w.buffer.Empty ) return;
caretPos = 0; w.caretPos = 0;
CalculateCaretData(); w.CalculateCaretData();
} }
void EndKey() { void EndKey() {
caretPos = -1; w.caretPos = -1;
CalculateCaretData(); w.CalculateCaretData();
} }
static char[] trimChars = {'\r', '\n', '\v', '\f', ' ', '\t', '\0'}; static char[] trimChars = {'\r', '\n', '\v', '\f', ' ', '\t', '\0'};
bool OtherKey( Key key ) { bool OtherKey( Key key ) {
if( key == Key.V && buffer.Length < TotalChars ) { if( key == Key.V && w.buffer.Length < w.TotalChars ) {
string text = null; string text = null;
try { try {
text = game.window.ClipboardText.Trim( trimChars ); text = game.window.ClipboardText.Trim( trimChars );
@ -251,18 +246,18 @@ namespace ClassicalSharp.Gui.Widgets {
game.Chat.Add( null, MessageType.ClientStatus4 ); game.Chat.Add( null, MessageType.ClientStatus4 );
for( int i = 0; i < text.Length; i++ ) { for( int i = 0; i < text.Length; i++ ) {
if( IsValidInputChar( text[i] ) ) continue; if( Utils.IsValidInputChar( text[i], game ) ) continue;
const string warning = "&eClipboard contained some characters that can't be sent."; const string warning = "&eClipboard contained some characters that can't be sent.";
game.Chat.Add( warning, MessageType.ClientStatus4 ); game.Chat.Add( warning, MessageType.ClientStatus4 );
text = RemoveInvalidChars( text ); text = RemoveInvalidChars( text );
break; break;
} }
AppendText( text ); w.AppendText( text );
return true; return true;
} else if( key == Key.C ) { } else if( key == Key.C ) {
if( buffer.Empty ) return true; if( w.buffer.Empty ) return true;
try { try {
game.window.ClipboardText = buffer.ToString(); game.window.ClipboardText = w.buffer.ToString();
} catch( Exception ex ) { } catch( Exception ex ) {
ErrorHandler.LogError( "Copy to clipboard", ex ); ErrorHandler.LogError( "Copy to clipboard", ex );
const string warning = "&cError while trying to copy to clipboard."; const string warning = "&cError while trying to copy to clipboard.";
@ -278,28 +273,28 @@ namespace ClassicalSharp.Gui.Widgets {
int length = 0; int length = 0;
for( int i = 0; i < input.Length; i++ ) { for( int i = 0; i < input.Length; i++ ) {
char c = input[i]; char c = input[i];
if( !IsValidInputChar( c ) ) continue; if( !Utils.IsValidInputChar( c, game ) ) continue;
chars[length++] = c; chars[length++] = c;
} }
return new String( chars, 0, length ); return new String( chars, 0, length );
} }
public override bool HandlesMouseClick( int mouseX, int mouseY, MouseButton button ) { public bool HandlesMouseClick( int mouseX, int mouseY, MouseButton button ) {
if( button == MouseButton.Left ) if( button == MouseButton.Left )
SetCaretToCursor( mouseX, mouseY ); SetCaretToCursor( mouseX, mouseY );
return true; return true;
} }
unsafe void SetCaretToCursor( int mouseX, int mouseY ) { unsafe void SetCaretToCursor( int mouseX, int mouseY ) {
mouseX -= inputTex.X1; mouseY -= inputTex.Y1; mouseX -= w.inputTex.X1; mouseY -= w.inputTex.Y1;
DrawTextArgs args = new DrawTextArgs( null, font, true ); DrawTextArgs args = new DrawTextArgs( null, w.font, true );
IDrawer2D drawer = game.Drawer2D; IDrawer2D drawer = game.Drawer2D;
int offset = 0, elemHeight = defaultHeight; int offset = 0, elemHeight = w.defaultHeight;
string oneChar = new String( 'A', 1 ); string oneChar = new String( 'A', 1 );
for( int y = 0; y < lines; y++ ) { for( int y = 0; y < w.parts.Length; y++ ) {
string line = parts[y]; string line = w.parts[y];
int xOffset = y == 0 ? defaultWidth : 0; int xOffset = y == 0 ? w.defaultWidth : 0;
if( line == null ) continue; if( line == null ) continue;
for( int x = 0; x < line.Length; x++ ) { for( int x = 0; x < line.Length; x++ ) {
@ -311,15 +306,15 @@ namespace ClassicalSharp.Gui.Widgets {
args.Text = oneChar; args.Text = oneChar;
int elemWidth = drawer.MeasureChatSize( ref args ).Width; int elemWidth = drawer.MeasureChatSize( ref args ).Width;
if( Contains( trimmedWidth, y * elemHeight, elemWidth, elemHeight, mouseX, mouseY ) ) { if( GuiElement.Contains( trimmedWidth, y * elemHeight, elemWidth, elemHeight, mouseX, mouseY ) ) {
caretPos = offset + x; w.caretPos = offset + x;
CalculateCaretData(); return; w.CalculateCaretData(); return;
} }
} }
offset += line.Length; offset += line.Length;
} }
caretPos = -1; w.caretPos = -1;
CalculateCaretData(); w.CalculateCaretData();
} }
} }
} }

View File

@ -201,5 +201,15 @@ namespace ClassicalSharp {
result = temp; result = temp;
return true; return true;
} }
public static bool IsValidInputChar( char c, Game game ) {
if( c >= ' ' && c <= '~' ) return true; // ascii
bool isCP437 = Utils.ControlCharReplacements.IndexOf( c ) >= 0 ||
Utils.ExtendedCharReplacements.IndexOf( c ) >= 0;
bool supportsCP437 = game.Server.SupportsFullCP437;
return supportsCP437 && isCP437;
}
} }
} }