mirror of
				https://github.com/ClassiCube/ClassiCube.git
				synced 2025-11-03 19:16:45 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			239 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
 | 
						|
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; // need 2 chars for colour in multilined chat, when server doesn't support partial messages
 | 
						|
			}
 | 
						|
		}
 | 
						|
		
 | 
						|
		public override void Init() {
 | 
						|
			base.Init();
 | 
						|
			bool supports = game.Server.SupportsPartialMessages;
 | 
						|
			
 | 
						|
			if (Text.Length > MaxCharsPerLine && !shownWarning && !supports) {
 | 
						|
				game.Chat.Add("&eNote: On this server, each line will be sent separately.", 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
 | 
						|
	}
 | 
						|
} |