#region Copyright
// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (C) 2015 Ian Horswill
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in the
// Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// --------------------------------------------------------------------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace Prolog
{
public class DebugOverlay : MonoBehaviour
{
public GUIStyle Style;
readonly List displayItems = new List();
private readonly StringBuilder textBuilder = new StringBuilder();
private static Texture2D greyOutTexture;
internal void Start()
{
if (greyOutTexture == null)
{
greyOutTexture = new Texture2D(1, 1);
// For some reason, changing the alpha value here has no effect on the amount of greying out
greyOutTexture.SetPixel(0, 0, new Color(0, 0, 0, 0.5f));
}
}
private Vector2 scrollPosition;
internal void OnGUI()
{
switch (Event.current.type)
{
case EventType.Repaint:
case EventType.Layout:
if (displayItems.Count > 0)
{
GUI.depth = -1;
var screenRect = new Rect(0, 0, Screen.width, Screen.height);
// For some reason, changing the alpha on greyOutTexture has no effect on the greying out
// but drawing the box twice does :(
GUI.Box(screenRect, greyOutTexture);
GUI.Box(screenRect, greyOutTexture);
scrollPosition = GUILayout.BeginScrollView(scrollPosition);
foreach (var item in displayItems)
{
if (item is string)
GUILayout.Label((string) item, Style, GUILayout.MaxWidth(Screen.width));
if (item is Table)
((Table) item).Render();
}
GUILayout.EndScrollView();
}
break;
case EventType.KeyDown:
if (Event.current.keyCode == KeyCode.Escape)
Hide();
break;
}
}
public void Hide()
{
displayItems.Clear();
}
public void UpdateText(object payload)
{
displayItems.Clear();
textBuilder.Length = 0;
Render(payload);
AddDisplayItem();
}
private void AddDisplayItem()
{
displayItems.Add(textBuilder.ToString());
textBuilder.Length = 0;
}
private void MaybeAddDisplayItem()
{
if (textBuilder.Length>0)
AddDisplayItem();
}
private void Render(object renderingOperation)
{
renderingOperation = Term.Deref(renderingOperation);
if (renderingOperation == null)
return;
var op = renderingOperation as Structure;
if (op != null)
{
switch (op.Functor.Name)
{
case "cons":
Render(op.Argument(0));
Render(op.Argument(1));
break;
case "line":
foreach (var arg in op.Arguments)
Render(arg);
textBuilder.AppendLine();
break;
case "color":
textBuilder.AppendFormat("", op.Argument(0));
for (int i = 1; i < op.Arity; i++)
Render(op.Argument(i));
textBuilder.Append(" ");
break;
case "size":
textBuilder.AppendFormat("", op.Argument(0));
for (int i = 1; i < op.Arity; i++)
Render(op.Argument(i));
textBuilder.Append(" ");
break;
case "bold":
textBuilder.AppendFormat("");
for (int i = 0; i < op.Arity; i++)
Render(op.Argument(i));
textBuilder.Append(" ");
break;
case "italic":
textBuilder.AppendFormat("");
for (int i = 0; i < op.Arity; i++)
Render(op.Argument(i));
textBuilder.Append(" ");
break;
case "term":
textBuilder.Append(ISOPrologWriter.WriteToString(op.Argument(0)));
break;
case "table":
MakeTable(op.Argument(0));
break;
default:
textBuilder.Append(ISOPrologWriter.WriteToString(op));
break;
}
}
else
{
var str = renderingOperation as string;
textBuilder.Append(str ?? ISOPrologWriter.WriteToString(renderingOperation));
}
}
private void MakeTable(object rows)
{
MaybeAddDisplayItem();
displayItems.Add(new Table(rows, Style));
}
}
class Table
{
private readonly List widths = new List();
private readonly List> rows = new List>();
private GUIStyle style;
const int Padding = 20;
public Table(object rows, GUIStyle style)
{
this.style = style;
//Debug.Log("Making table");
foreach (var row in Prolog.PrologListItems(rows))
this.rows.Add(MakeRow(row));
//Debug.Log("Made table");
}
private List MakeRow(object row)
{
//Debug.Log("Making row "+ISOPrologWriter.WriteToString(row));
var rowData = new List();
int column = 0;
foreach (var columnData in Prolog.PrologListItems(row))
{
//Debug.Log("Making column " + ISOPrologWriter.WriteToString(columnData));
var data = MakeColumn(columnData);
rowData.Add(data);
ReserveColumnWidth(column, data);
column++;
}
return rowData;
}
private readonly StringBuilder textBuilder = new StringBuilder();
private string MakeColumn(object columnData)
{
textBuilder.Length = 0;
AddItem(columnData);
return textBuilder.ToString();
}
// This ought to get refactored so that it's shared with the render code in the main class.
private void AddItem(object data)
{
var op = data as Structure;
if (op != null)
{
switch (op.Functor.Name)
{
case "cons":
AddItem(op.Argument(0));
AddItem(op.Argument(1));
break;
case "line":
foreach (var arg in op.Arguments)
AddItem(arg);
textBuilder.AppendLine();
break;
case "color":
textBuilder.AppendFormat("", op.Argument(0));
for (int i = 1; i < op.Arity; i++)
AddItem(op.Argument(i));
textBuilder.Append(" ");
break;
case "size":
textBuilder.AppendFormat("", op.Argument(0));
for (int i = 1; i < op.Arity; i++)
AddItem(op.Argument(i));
textBuilder.Append(" ");
break;
case "bold":
textBuilder.AppendFormat("");
for (int i = 0; i < op.Arity; i++)
AddItem(op.Argument(i));
textBuilder.Append(" ");
break;
case "italic":
textBuilder.AppendFormat("");
for (int i = 0; i < op.Arity; i++)
AddItem(op.Argument(i));
textBuilder.Append(" ");
break;
case "term":
textBuilder.Append(ISOPrologWriter.WriteToString(op.Argument(0)));
break;
default:
textBuilder.Append(ISOPrologWriter.WriteToString(op));
break;
}
}
else
{
var str = data as string;
textBuilder.Append(str ?? ISOPrologWriter.WriteToString(data));
}
}
private void ReserveColumnWidth(int column, string data)
{
var width = style.CalcSize(new GUIContent(data)).x;
if (column == widths.Count)
widths.Add(width);
else
widths[column] = Math.Max(width, widths[column]);
}
public void Render()
{
foreach (var row in rows)
{
GUILayout.BeginHorizontal();
for (int i = 0; i < row.Count; i++)
GUILayout.Label(row[i], style, GUILayout.Width(widths[i]+Padding));
GUILayout.EndHorizontal();
}
}
}
}