ClassiCube/ClassicalSharp/2D/Widgets/Chat/ChatInputWidget.cs
2016-11-27 14:47:09 +11:00

239 lines
6.7 KiB
C#

// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using System.Collections.Generic;
using System.Drawing;
using ClassicalSharp.Entities;
using OpenTK.Input;
#if ANDROID
using Android.Graphics;
#endif
namespace ClassicalSharp.Gui.Widgets {
public sealed class ChatInputWidget : InputWidget {
public ChatInputWidget(Game game, Font font) : base(game, font) {
typingLogPos = game.Chat.InputLog.Count; // Index of newest entry + 1.
ShowCaret = true;
}
static FastColour backColour = new FastColour(0, 0, 0, 127);
int typingLogPos;
string originalText;
bool shownWarning;
public override int MaxLines { get { return game.ClassicMode ? 1 : 3; } }
public override string Prefix { get { return "> "; } }
public override int Padding { get { return 5; } }
public override int MaxCharsPerLine {
get {
bool allChars = game.ClassicMode || game.Server.SupportsPartialMessages;
return allChars ? 64 : 62;
}
}
public override void Init() {
base.Init();
bool supports = game.Server.SupportsPartialMessages;
if (Text.Length > MaxCharsPerLine && !shownWarning && !supports) {
game.Chat.Add("&eNote: Each line will be sent as a separate packet.", MessageType.ClientStatus6);
shownWarning = true;
} else if (Text.Length <= MaxCharsPerLine && shownWarning) {
game.Chat.Add(null, MessageType.ClientStatus6);
shownWarning = false;
}
}
public override void Render(double delta) {
gfx.Texturing = false;
int y = Y, x = X;
for (int i = 0; i < lineSizes.Length; i++) {
if (i > 0 && lineSizes[i].Height == 0) break;
bool caretAtEnd = (caretRow == i) && (caretCol == MaxCharsPerLine || caret == -1);
int drawWidth = lineSizes[i].Width + (caretAtEnd ? caretTex.Width : 0);
// Cover whole window width to match original classic behaviour
if (game.PureClassic)
drawWidth = Math.Max(drawWidth, game.Width - X * 4);
gfx.Draw2DQuad(x, y, drawWidth + Padding * 2, prefixHeight, backColour);
y += lineSizes[i].Height;
}
gfx.Texturing = true;
inputTex.Render(gfx);
RenderCaret(delta);
}
public override void EnterInput() {
SendChat();
originalText = null;
typingLogPos = game.Chat.InputLog.Count; // Index of newest entry + 1.
game.Chat.Add(null, MessageType.ClientStatus4);
game.Chat.Add(null, MessageType.ClientStatus5);
game.Chat.Add(null, MessageType.ClientStatus6);
base.EnterInput();
}
void SendChat() {
if (Text.Empty) return;
// Don't want trailing spaces in output message
string allText = new String(Text.value, 0, Text.TextLength);
game.Chat.InputLog.Add(allText);
if (game.Server.SupportsPartialMessages) {
SendWithPartial(allText);
} else {
SendNormal();
}
}
void SendWithPartial(string allText) {
// don't automatically word wrap the message.
while (allText.Length > Utils.StringLength) {
game.Chat.Send(allText.Substring(0, Utils.StringLength), true);
allText = allText.Substring(Utils.StringLength);
}
game.Chat.Send(allText, false);
}
void SendNormal() {
int packetsCount = 0;
for (int i = 0; i < lines.Length; i++) {
if (lines[i] == null) break;
packetsCount++;
}
// split up into both partial and final packet.
for (int i = 0; i < packetsCount - 1; i++)
SendNormalText(i, true);
SendNormalText(packetsCount - 1, false);
}
void SendNormalText(int i, bool partial) {
string text = lines[i];
char lastCol = GetLastColour(0, i);
if (!IDrawer2D.IsWhiteColour(lastCol))
text = "&" + lastCol + text;
game.Chat.Send(text, partial);
}
#region Input handling
public override bool HandlesKeyDown(Key key) {
if (game.HideGui) return key < Key.F1 || key > Key.F35;
bool controlDown = ControlDown();
if (key == Key.Tab) { TabKey(); return true; }
if (key == Key.Up) { UpKey(controlDown); return true; }
if (key == Key.Down) { DownKey(controlDown); return true; }
return base.HandlesKeyDown(key);
}
void UpKey(bool controlDown) {
if (controlDown) {
int pos = caret == -1 ? Text.Length : caret;
if (pos < MaxCharsPerLine) return;
caret = pos - MaxCharsPerLine;
UpdateCaret();
return;
}
if (typingLogPos == game.Chat.InputLog.Count)
originalText = Text.ToString();
if (game.Chat.InputLog.Count > 0) {
typingLogPos--;
if (typingLogPos < 0) typingLogPos = 0;
Text.Clear();
Text.Append(0, game.Chat.InputLog[typingLogPos]);
caret = -1;
Recreate();
}
}
void DownKey(bool controlDown) {
if (controlDown) {
if (caret == -1 || caret >= (lines.Length - 1) * MaxCharsPerLine) return;
caret += MaxCharsPerLine;
UpdateCaret();
return;
}
if (game.Chat.InputLog.Count > 0) {
typingLogPos++;
Text.Clear();
if (typingLogPos >= game.Chat.InputLog.Count) {
typingLogPos = game.Chat.InputLog.Count;
if (originalText != null)
Text.Append(0, originalText);
} else {
Text.Append(0, game.Chat.InputLog[typingLogPos]);
}
caret = -1;
Recreate();
}
}
void TabKey() {
int pos = caret == -1 ? Text.Length - 1 : caret;
int start = pos;
char[] value = Text.value;
while (start >= 0 && IsNameChar(value[start]))
start--;
start++;
if (pos < 0 || start > pos) return;
string part = new String(value, start, pos + 1 - start);
List<string> matches = new List<string>();
game.Chat.Add(null, MessageType.ClientStatus5);
TabListEntry[] entries = game.TabList.Entries;
for (int i = 0; i < EntityList.MaxCount; i++) {
if (entries[i] == null) continue;
string rawName = entries[i].PlayerName;
string name = Utils.StripColours(rawName);
if (name.StartsWith(part, StringComparison.OrdinalIgnoreCase))
matches.Add(name);
}
if (matches.Count == 1) {
if (caret == -1) pos++;
int len = pos - start;
for (int i = 0; i < len; i++)
Text.DeleteAt(start);
if (caret != -1) caret -= len;
Append(matches[0]);
} else if (matches.Count > 1) {
StringBuffer sb = new StringBuffer(Utils.StringLength);
int index = 0;
sb.Append(ref index, "&e");
sb.AppendNum(ref index, matches.Count);
sb.Append(ref index, " matching names: ");
for (int i = 0; i < matches.Count; i++) {
string match = matches[i];
if ((sb.Length + match.Length + 1) > sb.Capacity) break;
sb.Append(ref index, match);
sb.Append(ref index, ' ');
}
game.Chat.Add(sb.ToString(), MessageType.ClientStatus5);
}
}
bool IsNameChar(char c) {
return c == '_' || c == '.' || (c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
#endregion
}
}