using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; public static partial class gui { public static class advanced { public class CodeArea { public CodeArea(EditorWindow window) { this.window = window; } private bool needUpdateOn = true; public bool shift; //Данные позиционирования //x,y,pos - реальная позиция //cursorPosition - позиция показываемая под курсором //anchorPosition - позиция второй стороны выделения public int x, y; public Vector2 pos { get { return new Vector2(x, y); }} public Vector2 cursorPosition; public Vector2 anchorPosition; public void PosFromCursor() { x = (int)cursorPosition.x; y = (int)cursorPosition.y; } public void PosToCursor() { cursorPosition = new Vector2(x, y); } public void PosFromAnchor() { x = (int)anchorPosition.x; y = (int)anchorPosition.y; } public void PosToAnchor() { anchorPosition = new Vector2(x, y); } public Vector2 currentPosition { get { return cursorPosition; } set { if (!shift) anchorPosition = value; cursorPosition = value; } } public int currentPositionX { get { return (int)cursorPosition.x; } set { if (!shift) anchorPosition.x = value; cursorPosition.x = value; } } public int currentPositionY { get { return (int)cursorPosition.y; } set { if (!shift) anchorPosition.y = value; cursorPosition.y = value; } } public List<string> content = new List<string>(); public List<Marker> markers = new List<Marker>(); public class Marker { public string pattern; public string color; public RegexOptions options; } public void AddMarker(string pattern, string richColor, RegexOptions options) { markers.Add(new Marker { pattern = pattern, color = richColor, options = options}); } public char GetChar(Vector2 position) { return content[(int) position.y][(int) position.x]; } public Vector2? GetLeftIndexOrDefault(int offsetLeft = 1) { var x = currentPositionX; var y = currentPositionY; x -= offsetLeft; while (x < 0) { y -= 1; var line = GetLine((int)y); if (line == null) return null; x += line.Length + 1; } return new Vector2(x, y); } public Vector2? GetRightIndexOrDefault(int offsetRight = 1) { var x = currentPositionX; var y = currentPositionY; x += offsetRight; while (true) { var line = GetLine((int)y); if (x <= line.Length) break; y += 1; var line2 = GetLine((int)y); if (line2 == null) return null; x -= line.Length + 1; } return new Vector2(x, y); } public Vector2? GetUpIndexOrDefault(bool lengthCheck = false) { var x = currentPositionX; var y = currentPositionY; var lineCurr = GetLine((int)y); var lineUp = GetLine((int)y - 1); if (lineUp == null) return null; if (x > lineUp.Length || (lengthCheck && Math.Abs(x - lineCurr.Length) < Mathf.Epsilon)) x = lineUp.Length; y -= 1; return new Vector2(x, y); } public Vector2? GetDownIndexOrDefault(bool lengthCheck = false) { var x = currentPositionX; var y = currentPositionY; var lineCurr = GetLine((int) y); var lineDown = GetLine((int) y + 1); if (lineDown == null) return null; if (x > lineDown.Length || (lengthCheck && Math.Abs(x - lineCurr.Length) < Mathf.Epsilon)) x = lineDown.Length; y += 1; return new Vector2(x, y); } public void Home() { var startX = (int)currentPosition.x; currentPositionX = 0; while (rightChar == ' ' || rightChar == '\t') currentPositionX++; if (startX <= currentPositionX && startX != 0) currentPositionX = 0; } public char? leftChar { get { var pos = GetLeftIndexOrDefault(); if (pos != null) { if (Math.Abs(currentPosition.x) > Mathf.Epsilon) return GetChar(pos.Value); return '\n'; } return null; } } public char? rightChar { get { if (Math.Abs(currentPosition.x - GetLine((int)currentPosition.y).Length) > Mathf.Epsilon) return GetChar(currentPosition); if (Math.Abs(currentPosition.y - (content.Count - 1)) > Mathf.Epsilon) return null; return '\n'; } } public string GetLine(int lineIndex) { return lineIndex >= 0 && lineIndex < content.Count ? content[lineIndex] : null; } public void SetText(string text) { var lines = text.Split(new [] { Environment.NewLine }, StringSplitOptions.None); content = lines.ToList(); } public class StringWithMask { public StringWithMask(string value) { this.value = value; openTags = new string[value.Length+1]; for (int i = 0; i < openTags.Length; i++) openTags[i] = ""; closeTags = new string[value.Length+1]; for (int i = 0; i < closeTags.Length; i++) closeTags[i] = ""; } public string value; public string[] openTags, closeTags; public void AddTag(int index, int length, string open, string close) { openTags[index] = open + openTags[index]; closeTags[index + length] += close; } public string GetValueWithMask() { var str = value; for (int i = openTags.Length - 1; i >= 0; i--) { if (closeTags[i].Length > 0) str = str.Insert(i, closeTags[i]); if (openTags[i].Length > 0) str = str.Insert(i, openTags[i]); } return str; } } public List<string> GetStyledMask() { var lines = content.ToList().Select(x => new StringWithMask(x)).ToArray(); for (int i = 0; i < markers.Count; i++) { var marker = markers[i]; var regular = new Regex(marker.pattern, marker.options); for (int j = 0; j < lines.Length; j++) { var matches = regular.Matches(lines[j].value).Cast<Match>().OrderByDescending(x => x.Index).ToList(); for (int k = 0; k < matches.Count; k++) { var index = matches[k].Index; var length = matches[k].Length; lines[j].AddTag(index, length, "<color=" + markers[i].color + ">", "</color>"); } } } return lines.Select(x => x.GetValueWithMask()).ToList(); } public int wordWidth = 7; public int lineHeight = 13; public Vector2 scroll; public void Draw() { if (needUpdateOn) { EditorApplication.update += Update; needUpdateOn = false; } if (content.Count == 0) content.Add(""); using (gui.group_scroll(ref scroll)) { var id = GUIUtility.GetControlID(FocusType.Native, new Rect(0, 0, rect.width, rect.height)); var mask = GetStyledMask(); var width = content.Max(x => x.Length); var height = content.Count; using (group_vertical("HelpBox", css.expand_height(true))) { gui.box(GUIStyle.none, css.width((width+4)*wordWidth), css.height(height*lineHeight)); if (guievent.repaint) { if (rect == new Rect()) Event.current.Use(); rect = GUILayoutUtility.GetLastRect(); } GUI.BeginGroup(rect); var selectPack = css.pack(css.style("CN EntryBackOdd"), css.margin(0), css.padding(0), css.border(0)); if (cursorPosition != anchorPosition) { Vector2 start, end; if (cursorPosition.y > anchorPosition.y || (cursorPosition.y == anchorPosition.y && cursorPosition.x > anchorPosition.x)) { start = anchorPosition; end = cursorPosition; } else { start = cursorPosition; end = anchorPosition; } //multiline if (start.y != end.y) { if (start.x != content[(int)start.y].Length) gui.box(new Rect(start.x*wordWidth, start.y*lineHeight, (content[(int) start.y].Length - start.x)*wordWidth, lineHeight), selectPack); if (end.y - start.y > 1) { for (int i = (int)start.y + 1; i < end.y; i++) { gui.box(new Rect(0, i * lineHeight, (content[i].Length) * wordWidth, lineHeight), selectPack); } } if (end.x != 0) gui.box(new Rect(0, end.y * lineHeight, (end.x) * wordWidth, lineHeight), selectPack); } else { gui.box(new Rect(start.x * wordWidth, start.y * lineHeight, (end.x - start.x) * wordWidth, lineHeight), selectPack); } } for (int i = 0; i < mask.Count; i++) { var r = new rect(0, i * lineHeight, (content[i].Length + 4) * wordWidth, lineHeight); label(r, mask[i], css.rich(true), css.font_family(ParserCssWindow.font), css.padding(0), css.font_size(13), css.margin(0)); } globalIndex = GetPositionByIndex(cursorPosition); if (cursorShow) gui.box(new Rect(currentPosition.x * wordWidth, currentPosition.y * lineHeight, 1, lineHeight), css.border(1)); if (new Rect(0, 0, rect.width, rect.height).Contains(Event.current.mousePosition)) { if (Event.current.type == EventType.mouseDown && Event.current.clickCount == 2) { var mp = Event.current.mousePosition; var x = (mp.x + wordWidth / 2f) / wordWidth; var y = mp.y / lineHeight; if (y >= content.Count) y = content.Count - 1; if (x > GetLine((int)y).Length) x = GetLine((int)y).Length; currentPosition = new Vector2((int)x, (int)y); var c = rightChar; if (c == null || c.Value == ' ') c = leftChar; if (c != null) { var startIndex = currentPositionX; var endIndex = currentPositionX; int group = 0; if (charsGroup.Contains(c.Value)) group = 1; else if (c.Value == ' ') group = 2; if (leftChar != null) { int leftGroup = group; while (leftGroup == group && startIndex > 0) { var leftC = GetChar(new Vector2(startIndex, currentPositionY)); if (charsGroup.Contains(leftC)) leftGroup = 1; else if (leftC == ' ') leftGroup = 2; else leftGroup = 0; startIndex--; } } if (rightChar != null) { int rightGroup = group; while (rightGroup == group && endIndex != GetLine(currentPositionY).Length) { var leftC = GetChar(new Vector2(endIndex, currentPositionY)); if (charsGroup.Contains(leftC)) rightGroup = 1; else if (leftC == ' ') rightGroup = 2; else rightGroup = 0; endIndex++; } } cursorPosition = new Vector2(startIndex+2, currentPositionY); anchorPosition = new Vector2(endIndex-1, currentPositionY); } index = 0; cursorShow = true; GUIUtility.keyboardControl = id; Event.current.Use(); } else if (Event.current.type == EventType.mouseDown) { var mp = Event.current.mousePosition; var x = (mp.x + wordWidth / 2f) / wordWidth; var y = mp.y / lineHeight; if (y >= content.Count) y = content.Count - 1; if (x > GetLine((int)y).Length) x = GetLine((int)y).Length; currentPosition = new Vector2((int)x, (int)y); index = 0; cursorShow = true; GUIUtility.keyboardControl = id; GUIUtility.hotControl = id; Debug.Log(GUIUtility.hotControl); Event.current.Use(); } if (GUIUtility.hotControl == id && (Event.current.type == EventType.mouseUp || Event.current.type == EventType.mouseDrag)) { var mp = Event.current.mousePosition; var x = (mp.x + wordWidth / 2f) / wordWidth; var y = mp.y / lineHeight; if (y >= content.Count) y = content.Count - 1; if (x > GetLine((int)y).Length) x = GetLine((int)y).Length; cursorPosition = new Vector2((int)x, (int)y); index = 0; cursorShow = true; if (Event.current.type == EventType.mouseUp) GUIUtility.hotControl = 0; Event.current.Use(); } } GUI.EndGroup(); } } UseControl(Event.current); } public Vector2 globalIndex; private Vector2 GetPositionByIndex(Vector2 index) { var x = index.x; var y = index.y; var px = wordWidth*x - wordWidth/2f; var py = y*lineHeight; return GUIUtility.GUIToScreenPoint(new Vector2(px, py)); } public void WriteText(string text) { content[(int)currentPosition.y] = content[(int)currentPosition.y].Insert((int)currentPosition.x, text); currentPositionX += text.Length; } public void RemoveRightChar() { var pos = GetRightIndexOrDefault(); if (pos != null) { if (Math.Abs(pos.Value.y - currentPosition.y) < Mathf.Epsilon) { content[(int)currentPosition.y] = content[(int)currentPosition.y].Remove((int)currentPosition.x, 1); } else { content[(int)currentPosition.y] += content[(int)pos.Value.y]; content.RemoveAt((int)pos.Value.y); while (rightChar != null && rightChar == ' ') RemoveRightChar(); } } } public void RemoveLeftChar() { var pos = GetLeftIndexOrDefault(); if (pos != null) { if (Math.Abs(currentPosition.x) > Mathf.Epsilon) { content[(int)currentPosition.y] = content[(int)currentPosition.y].Remove((int)pos.Value.x, 1); } else { content[(int)pos.Value.y] += content[(int)currentPosition.y]; content.RemoveAt((int)currentPosition.y); } currentPosition = pos.Value; } } public void Enter() { var lineNew = content[(int)currentPosition.y].Substring((int)currentPosition.x); if (lineNew.Length != 0) content[(int)currentPosition.y] = content[(int)currentPosition.y].Remove((int)currentPosition.x); content.Insert((int)currentPosition.y + 1, lineNew); var p = GetRightIndexOrDefault(); if (p != null) { currentPosition = p.Value; var quote = 0; for (int i = 0; i < currentPositionY; i++) { quote += content[i].Count(x => x == '{'); quote -= content[i].Count(x => x == '}'); } if (content[currentPositionY].IndexOf('{') < currentPositionX) quote += 1; for (int i = 0; i < quote; i++) WriteText(" "); } } public void CtrlLeft() { if (leftChar != null) { int group = 0; if (decimalGroup.Contains(leftChar.Value)) group = 1; else if (charsGroup.Contains(leftChar.Value)) group = 2; else if (leftChar.Value == ' ') group = 3; int nextGroup = group; while (nextGroup == group) { var p = GetLeftIndexOrDefault(); if (p == null) break; currentPosition = p.Value; if (leftChar == null) break; if (decimalGroup.Contains(leftChar.Value)) nextGroup = 1; else if (charsGroup.Contains(leftChar.Value)) nextGroup = 2; else if (leftChar.Value == ' ') group = 3; else nextGroup = 0; } } } public void CtrlRight() { if (rightChar != null) { int group = 0; if (decimalGroup.Contains(rightChar.Value)) group = 1; else if (charsGroup.Contains(rightChar.Value)) group = 2; else if (rightChar.Value == ' ') group = 3; int nextGroup = group; while (nextGroup == group) { var p = GetRightIndexOrDefault(); if (p == null) break; currentPosition = p.Value; if (rightChar == null) break; if (decimalGroup.Contains(rightChar.Value)) nextGroup = 1; else if (charsGroup.Contains(rightChar.Value)) nextGroup = 2; else if (rightChar.Value == ' ') group = 3; else nextGroup = 0; } } } public void UseControl(Event e) { shift = false; if (e.type == EventType.KeyDown) { bool use = true; shift = e.shift; if (e.keyCode == KeyCode.LeftArrow) { if (e.control) { CtrlLeft(); } else { var p = GetLeftIndexOrDefault(); if (p != null) currentPosition = p.Value; } } else if (e.keyCode == KeyCode.RightArrow) { shift = e.shift; if (e.control) { CtrlRight(); } else { var p = GetRightIndexOrDefault(); if (p != null) currentPosition = p.Value; } } else if (e.keyCode == KeyCode.UpArrow) { shift = e.shift; var p = GetUpIndexOrDefault(); if (p != null) currentPosition = p.Value; } else if (e.keyCode == KeyCode.DownArrow) { shift = e.shift; var p = GetDownIndexOrDefault(); if (p != null) currentPosition = p.Value; } else if (e.keyCode == KeyCode.End) { shift = e.shift; currentPositionX = GetLine((int)currentPosition.y).Length; } else if (e.keyCode == KeyCode.Home) { shift = e.shift; Home(); } else if (e.keyCode == KeyCode.Tab) { WriteText(" "); } else if (e.keyCode == KeyCode.Backspace) { if (!ClearSelect()) RemoveLeftChar(); } else if (e.keyCode == KeyCode.Delete) { if (!ClearSelect()) RemoveRightChar(); } else if (e.keyCode == KeyCode.Return) { ClearSelect(); Enter(); } else if (e.keyCode == KeyCode.Space) { if (e.control) { var word = GetWordLeft(); var commands = ProfilerCss.items.Where(x => x.name != word && x.name.StartsWith(word)) .Select(x => x.name) .ToArray(); if (commands.Length != 0) { AutocompleteMenu.Open(window, globalIndex + new Vector2(-32, lineHeight), UseControl, WriteText, commands).indexWord = word.Length; } } else { shift = false; WriteText(" "); } } else { var simbol = GetSimbolByEvent(e); if (simbol != null) { if (simbol != '\t' && simbol != (char) 10 && simbol != ' ') { shift = false; if (simbol.Value == '{' && leftChar != null) Enter(); WriteText(simbol.Value.ToString()); var word = GetWordLeft(); if (word.Length != 0) { var commands = ProfilerCss.items.Where(x => x.name != word && x.name.StartsWith(word)) .Select(x => x.name) .ToArray(); if (commands.Length != 0) { AutocompleteMenu.Open(window, globalIndex + new Vector2(-32, lineHeight), UseControl, WriteText, commands).indexWord = word.Length; } } } } else { use = false; } } if (use) { index = 0; cursorShow = true; e.Use(); } } } private string _validChars; public string validChars { get { if (_validChars == null) { _validChars = ""; foreach (var item in ProfilerCss.items) { for (int i = 0; i < item.name.Length; i++) { var c = item.name[i]; if (!_validChars.Contains(c)) _validChars += c; } } } return _validChars; } } public string GetWordLeft() { var x = (int)cursorPosition.x - 1; var y = (int)cursorPosition.y; var result = ""; while (x >= 0) { var c = GetChar(new Vector2(x, y)); if (validChars.Contains(c)) { result = c + result; Debug.Log(result); x--; } else break; } return result; } private bool ClearSelect() { if (cursorPosition != anchorPosition) { Vector2 start, end; if (cursorPosition.y > anchorPosition.y || (cursorPosition.y == anchorPosition.y && cursorPosition.x > anchorPosition.x)) { start = anchorPosition; end = cursorPosition; } else { start = cursorPosition; end = anchorPosition; } if (start.y != end.y) { var removeRangeStart = (int) (start.y + 1); var removeRangeEnd = (int) (end.y - 1); if (start.x != content[(int) start.y].Length) { content[(int) start.y] = content[(int) start.y].Remove((int) start.x); } if (end.x != 0) { content[(int)end.y] = content[(int)end.y].Remove(0, (int)end.x); } content[(int) start.y] += content[(int) end.y]; removeRangeEnd += 1; if (end.y - start.y >= 1) { for (int i = removeRangeEnd; i >= removeRangeStart; i--) content.RemoveAt(i); } } else { content[(int) start.y] = content[(int) start.y].Remove((int) start.x, (int) (end.x - start.x)); } cursorPosition = start; anchorPosition = cursorPosition; return true; } return false; } private static string charsGroup = "~`!@#$%^&*()_+-=|\\?/.,<>:;\"'[]{}№"; private static string decimalGroup = "0123456789"; public Rect rect; private int index = 0; private void Update() { index++; if (index >= 400) { index = 0; ByTimer(); } } public EditorWindow window; private bool cursorShow; private void ByTimer() { cursorShow = !cursorShow; if (window != null) window.Repaint(); } private char? GetSimbolByEvent(Event e) { if (e.character != default(char)) return e.character; return null; } } } }
При снятии с публикации можно указать причину в комментарий к ресурсу.
Комментарии проекта cssGUI
cssGUI
cssGUI
По сути данная наработка является обёрткой поверх стандартного UnityGUI.
Ред. AsagiriGen