Newer
Older
DungeonShooting / DungeonShooting_Godot / editor / src / CodeHintPanel.cs
@小李xl 小李xl on 26 Sep 2022 6 KB 代码提示组件开发
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Godot;

namespace DScript.GodotEditor
{
	/// <summary>
	/// 代码补全提示
	/// </summary>
	public class CodeHintPanel : Popup
	{
		private class CodeHintItemData
		{
			public CodeHintItem CodeHintItem;
			public bool VisibleFlag;
		}
		
		//补全选项
		[Export] private PackedScene CodeHintItem;

		/// <summary>
		/// 获取实例
		/// </summary>
		public static CodeHintPanel Instance { get; private set; }

		/// <summary>
		/// 当前选提示组件活动的项
		/// </summary>
		public int ActiveIndex
		{
			get => _activeIndex;
			set => SetActiveIndex(value);
		}
		private int _activeIndex = -1;

		//滑动容器
		private ScrollContainer _scrollContainer;
		//提示项父容器
		private VBoxContainer _itemContainer;

		//包含提示数据
		private List<CodeHintData> _hintDataList = new List<CodeHintData>();
		//当前已启用项的列表
		private List<CodeHintItemData> _activeItemList = new List<CodeHintItemData>();
		
		//长按计时器
		private float _clickTimer = 0;
		private bool _continuousFlag = false;

		//当前的文本编辑器对象
		private CodeTextEdit _textEdit;
		private int startLine;
		private int startcColumn;
		
		public CodeHintPanel()
		{
			Instance = this;
		}

		public override void _Ready()
		{
			_scrollContainer = GetNode<ScrollContainer>("ScrollContainer");
			_itemContainer = _scrollContainer.GetNode<VBoxContainer>("VBoxContainer");

			for (int i = 0; i < CodeTextEdit.KeyCodes.Length; i++)
			{
				_hintDataList.Add(new CodeHintData() { Text = CodeTextEdit.KeyCodes[i] });
				// var item = CreateItem();
				// item.CodeHintItem.CodeText = CodeTextEdit.KeyCodes[i];
			}
		}

		public override void _Process(float delta)
		{
			if (!Visible) return;
			if (_textEdit == null)
			{
				Visible = false;
				_textEdit = null;
				return;
			}
			
			//长按判定
			var down = Input.IsActionPressed("ui_down");
			var up = Input.IsActionPressed("ui_up");
			//是否触发选项移动
			bool clickFlag = false;
			if (down || up)
			{
				_clickTimer += delta;
				if ((!_continuousFlag && _clickTimer > 0.5f) || (_continuousFlag && _clickTimer > 0.06f))
				{
					_continuousFlag = true;
					_clickTimer %= 0.06f;
					clickFlag = true;
				}
			}
			else
			{
				_clickTimer = 0;
				_continuousFlag = false;
			}
			
			//选项移动判定
			if ((Input.IsActionJustPressed("ui_down") || (clickFlag && down)) && _activeItemList.Count > 1) //按下下键
			{
				var index = ActiveIndex;
				index += 1;
				if (index >= _activeItemList.Count)
				{
					index = 0;
				}

				ActiveIndex = index;
			}
			else if ((Input.IsActionJustPressed("ui_up") || (clickFlag && up)) && _activeItemList.Count > 1) //按下上键
			{
				var index = ActiveIndex;
				index -= 1;
				if (index < 0)
				{
					index = _activeItemList.Count - 1;
				}

				ActiveIndex = index;
			}
		}

		public override void _Input(InputEvent @event)
		{
			if (!Visible) return;
			if (@event is InputEventKey eventKey)
			{
				if (eventKey.IsPressed())
				{
					//按下左,右,空格时隐藏提示框
					if (eventKey.Scancode == (int)KeyList.Left || eventKey.Scancode == (int)KeyList.Right ||
					    eventKey.Scancode == (int)KeyList.Space)
					{
						HidePanel();
					}
					//按下 enter 或者 tab 确认输入
					else if (eventKey.Scancode == (int)KeyList.Enter || eventKey.Scancode == (int)KeyList.Tab)
					{
						ConfirmInput(ActiveIndex);
					}
				}
			}
		}

		/// <summary>
		/// 确认输入选中的项, 补全代码
		/// </summary>
		internal void ConfirmInput(int index)
		{
			if (index >= 0 && _activeItemList.Count > 0 && index < _activeItemList.Count && _textEdit != null)
			{
				var line = _textEdit.CursorGetLine();
				var column = _textEdit.CursorGetColumn();
				var lineStr = _textEdit.GetLine(line);

				var beforeStr = lineStr.Substring(0, column);

				var result = Regex.Match(beforeStr, "[\\w]+$");
				if (result.Success)
				{
					var item = _activeItemList[index];
					var text = item.CodeHintItem.CodeText;
					lineStr = beforeStr.Substring(0,
						result.Index) + text + lineStr.Substring(column);
					_textEdit.SetLine(line, lineStr);
					_textEdit.CursorSetColumn(result.Index + text.Length);
					_textEdit.TriggerTextChanged();
				}
				else
				{
					var item = _activeItemList[index];
					_textEdit.InsertTextAtCursor(item.CodeHintItem.CodeText);
				}
			}

			Hide();
			CodeHintManager.EnterInput = true;
		}

		/// <summary>
		/// 显示提示面板
		/// </summary>
		public void ShowPanel(CodeTextEdit textEdit, Vector2 pos)
		{
			_textEdit = textEdit;
			RectPosition = pos;
			if (!Visible)
			{
				GD.Print("call ShowPanel()");
				Popup_();
				_textEdit.GrabFocus();
				ActiveIndex = 0;
			}
		}

		/// <summary>
		/// 隐藏面板
		/// </summary>
		public void HidePanel()
		{
			GD.Print("call HidePanel()");
			Hide();
			_textEdit = null;
		}
		
		/// <summary>
		/// 设置活动项
		/// </summary>
		private void SetActiveIndex(int index)
		{
			//禁用之前的
			if (_activeIndex >= 0 && _activeIndex < _activeItemList.Count)
			{
				var item = _activeItemList[_activeIndex];
				item.CodeHintItem.SetActive(false);
			}

			_activeIndex = index;

			//启用现在的
			if (index >= 0 && index < _activeItemList.Count)
			{
				var item = _activeItemList[index];
				item.CodeHintItem.SetActive(true);

				//矫正滑动组件y轴值, 使其选中项不会跑到视野外
				var vertical = _scrollContainer.ScrollVertical;
				var scrollSize = _scrollContainer.GetVScrollbar().RectSize;
				var itemPos = item.CodeHintItem.RectPosition;
				var itemSize = item.CodeHintItem.RectSize;
				itemPos.y -= vertical;
				if (itemPos.y < 0)
				{
					_scrollContainer.ScrollVertical = (int)(index * itemSize.y);
				}
				else if (itemPos.y + itemSize.y > scrollSize.y)
				{
					_scrollContainer.ScrollVertical = (int)((index + 1) * itemSize.y - scrollSize.y);
				}
			}
		}

		/// <summary>
		/// 创建提示项
		/// </summary>
		private CodeHintItemData CreateItem()
		{
			var codeHintItem = CodeHintItem.Instance<CodeHintItem>();
			_itemContainer.AddChild(codeHintItem);
			codeHintItem.Index = _activeItemList.Count;
			var item = new CodeHintItemData();
			item.CodeHintItem = codeHintItem;
			_activeItemList.Add(item);
			return item;
		}
	}
}