mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-18 03:55:19 -04:00
Initial URL rewrite (broken currently)
This commit is contained in:
parent
c68208907c
commit
e763e174a5
@ -22,7 +22,6 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
public Texture[] Textures;
|
public Texture[] Textures;
|
||||||
public bool[] PlaceholderHeight;
|
public bool[] PlaceholderHeight;
|
||||||
internal string[] lines;
|
internal string[] lines;
|
||||||
LinkData[] linkData;
|
|
||||||
int ElementsCount, defaultHeight;
|
int ElementsCount, defaultHeight;
|
||||||
readonly Font font, underlineFont;
|
readonly Font font, underlineFont;
|
||||||
|
|
||||||
@ -30,7 +29,6 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
Textures = new Texture[ElementsCount];
|
Textures = new Texture[ElementsCount];
|
||||||
PlaceholderHeight = new bool[ElementsCount];
|
PlaceholderHeight = new bool[ElementsCount];
|
||||||
lines = new string[ElementsCount];
|
lines = new string[ElementsCount];
|
||||||
linkData = new LinkData[ElementsCount];
|
|
||||||
|
|
||||||
int height = game.Drawer2D.FontHeight(font, true);
|
int height = game.Drawer2D.FontHeight(font, true);
|
||||||
game.Drawer2D.ReducePadding(ref height, Utils.Floor(font.Size), 3);
|
game.Drawer2D.ReducePadding(ref height, Utils.Floor(font.Size), 3);
|
||||||
@ -60,10 +58,8 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
lines[i] = lines[i + 1];
|
lines[i] = lines[i + 1];
|
||||||
Textures[i].Y1 = y;
|
Textures[i].Y1 = y;
|
||||||
y += Textures[i].Height;
|
y += Textures[i].Height;
|
||||||
linkData[i] = linkData[i + 1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
linkData[Textures.Length - 1] = default(LinkData);
|
|
||||||
Textures[Textures.Length - 1].ID = 0; // Delete() is called by SetText otherwise.
|
Textures[Textures.Length - 1].ID = 0; // Delete() is called by SetText otherwise.
|
||||||
SetText(Textures.Length - 1, text);
|
SetText(Textures.Length - 1, text);
|
||||||
}
|
}
|
||||||
@ -147,46 +143,47 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
string GetUrl(int index, int mouseX) {
|
unsafe string GetUrl(int index, int mouseX) {
|
||||||
Rectangle[] partBounds = linkData[index].bounds;
|
Texture tex = Textures[index]; mouseX -= tex.X1;
|
||||||
if (partBounds == null) return null;
|
DrawTextArgs args = default(DrawTextArgs);
|
||||||
Texture tex = Textures[index];
|
string text = lines[index];
|
||||||
mouseX -= tex.X1;
|
|
||||||
|
|
||||||
for (int i = 1; i < partBounds.Length; i += 2) {
|
char* chars = stackalloc char[lines.Length * 96];
|
||||||
if (mouseX >= partBounds[i].Left && mouseX < partBounds[i].Right)
|
Portion* portions = stackalloc Portion[(96 / 7) * 2];
|
||||||
return linkData[index].urls[i];
|
int portionsCount = Reduce(chars, index, portions);
|
||||||
|
|
||||||
|
for (int i = 0, x = 0; i < portionsCount; i++) {
|
||||||
|
Portion bit = portions[i];
|
||||||
|
args.Text = text.Substring(bit.Beg, bit.Len);
|
||||||
|
args.Font = (bit.ReducedLen & 0x8000) == 0 ? font : underlineFont;
|
||||||
|
|
||||||
|
int width = game.Drawer2D.MeasureSize(ref args).Width;
|
||||||
|
if (args.Font != font && mouseX >= x && mouseX < x + width) {
|
||||||
|
return new string(chars, bit.ReducedBeg, bit.ReducedLen);
|
||||||
|
}
|
||||||
|
x += width;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture MakeTexture(int index, string text) {
|
public void SetText(int index, string text) {
|
||||||
DrawTextArgs args = new DrawTextArgs(text, font, true);
|
game.Graphics.DeleteTexture(ref Textures[index]);
|
||||||
LinkFlags prevFlags = index > 0 ? linkData[index - 1].flags : 0;
|
|
||||||
Texture tex;
|
Texture tex;
|
||||||
|
|
||||||
if (game.ClassicMode || NextToken(text, 0, ref prevFlags) == -1) {
|
if (IDrawer2D.EmptyText(text)) {
|
||||||
|
lines[index] = null;
|
||||||
|
tex = default(Texture);
|
||||||
|
tex.Height = (ushort)(PlaceholderHeight[index] ? defaultHeight : 0);
|
||||||
|
} else {
|
||||||
|
lines[index] = text;
|
||||||
|
DrawTextArgs args = new DrawTextArgs(text, font, true);
|
||||||
|
|
||||||
|
if (game.ClassicMode || !MightHaveUrls()) {
|
||||||
tex = game.Drawer2D.MakeTextTexture(ref args, 0, 0);
|
tex = game.Drawer2D.MakeTextTexture(ref args, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
tex = DrawAdvanced(ref args, index, text);
|
tex = DrawAdvanced(ref args, index, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.Drawer2D.ReducePadding(ref tex, Utils.Floor(args.Font.Size), 3);
|
game.Drawer2D.ReducePadding(ref tex, Utils.Floor(args.Font.Size), 3);
|
||||||
return tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetText(int index, string text) {
|
|
||||||
game.Graphics.DeleteTexture(ref Textures[index]);
|
|
||||||
linkData[index] = default(LinkData);
|
|
||||||
Texture tex;
|
|
||||||
|
|
||||||
if (!IDrawer2D.EmptyText(text)) {
|
|
||||||
tex = MakeTexture(index, text);
|
|
||||||
lines[index] = text;
|
|
||||||
} else {
|
|
||||||
tex = default(Texture);
|
|
||||||
tex.Height = (ushort)(PlaceholderHeight[index] ? defaultHeight : 0);
|
|
||||||
lines[index] = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tex.X1 = CalcPos(HorizontalAnchor, XOffset, tex.Width, game.Width);
|
tex.X1 = CalcPos(HorizontalAnchor, XOffset, tex.Width, game.Width);
|
||||||
@ -195,15 +192,27 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
UpdateDimensions();
|
UpdateDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe Texture DrawAdvanced(ref DrawTextArgs args, int index, string text) {
|
bool MightHaveUrls() {
|
||||||
LinkData data = Split(index, text);
|
for (int i = 0; i < lines.Length; i++) {
|
||||||
Size total = Size.Empty;
|
if (lines[i] == null) continue;
|
||||||
Size* partSizes = stackalloc Size[data.parts.Length];
|
if (lines[i].IndexOf('/') >= 0) return true;
|
||||||
linkData[index] = data;
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe Texture DrawAdvanced(ref DrawTextArgs args, int index, string text) {
|
||||||
|
char* chars = stackalloc char[lines.Length * 96];
|
||||||
|
Portion* portions = stackalloc Portion[(96 / 7) * 2];
|
||||||
|
int portionsCount = Reduce(chars, index, portions);
|
||||||
|
|
||||||
|
Size total = Size.Empty;
|
||||||
|
Size* partSizes = stackalloc Size[portionsCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < portionsCount; i++) {
|
||||||
|
Portion bit = portions[i];
|
||||||
|
args.Text = text.Substring(bit.Beg, bit.Len);
|
||||||
|
args.Font = (bit.ReducedLen & 0x8000) == 0 ? font : underlineFont;
|
||||||
|
|
||||||
for (int i = 0; i < data.parts.Length; i++) {
|
|
||||||
args.Text = data.parts[i];
|
|
||||||
args.Font = (i & 1) == 0 ? font : underlineFont;
|
|
||||||
partSizes[i] = game.Drawer2D.MeasureSize(ref args);
|
partSizes[i] = game.Drawer2D.MeasureSize(ref args);
|
||||||
total.Height = Math.Max(partSizes[i].Height, total.Height);
|
total.Height = Math.Max(partSizes[i].Height, total.Height);
|
||||||
total.Width += partSizes[i].Width;
|
total.Width += partSizes[i].Width;
|
||||||
@ -215,134 +224,134 @@ namespace ClassicalSharp.Gui.Widgets {
|
|||||||
drawer.SetBitmap(bmp);
|
drawer.SetBitmap(bmp);
|
||||||
int x = 0;
|
int x = 0;
|
||||||
|
|
||||||
for (int i = 0; i < data.parts.Length; i++) {
|
for (int i = 0; i < portionsCount; i++) {
|
||||||
args.Text = data.parts[i];
|
Portion bit = portions[i];
|
||||||
args.Font = (i & 1) == 0 ? font : underlineFont;
|
args.Text = text.Substring(bit.Beg, bit.Len);
|
||||||
Size size = partSizes[i];
|
args.Font = (bit.ReducedLen & 0x8000) == 0 ? font : underlineFont;
|
||||||
|
|
||||||
drawer.DrawText(ref args, x, 0);
|
drawer.DrawText(ref args, x, 0);
|
||||||
data.bounds[i].X = x;
|
x += partSizes[i].Width;
|
||||||
data.bounds[i].Width = size.Width;
|
|
||||||
x += size.Width;
|
|
||||||
}
|
}
|
||||||
return drawer.Make2DTexture(bmp, total, 0, 0);
|
return drawer.Make2DTexture(bmp, total, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkData Split(int index, string line) {
|
unsafe static int NextUrl(char* chars, int i, int len) {
|
||||||
int start = 0, lastEnd = 0, count = 0;
|
for (; i < len; i++) {
|
||||||
LinkData data = default(LinkData);
|
if (chars[i] != 'h') continue;
|
||||||
data.parts = new string[GetTokensCount(index, line)];
|
int left = len - i;
|
||||||
data.urls = new string[data.parts.Length];
|
if (left < 7) return -1; // "http://".Length
|
||||||
data.bounds = new Rectangle[data.parts.Length];
|
|
||||||
LinkFlags prevFlags = index > 0 ? linkData[index - 1].flags : 0;
|
|
||||||
|
|
||||||
while ((start = NextToken(line, start, ref prevFlags)) >= 0) {
|
// Starts with "http" ?
|
||||||
int nextEnd = line.IndexOf(' ', start);
|
if (chars[i + 1] != 't' || chars[i + 2] != 't' || chars[i + 3] != 'p') continue;
|
||||||
if (nextEnd == -1) {
|
left -= 4; i += 4;
|
||||||
nextEnd = line.Length;
|
|
||||||
data.flags |= LinkFlags.Continue;
|
// And then with "s://" or "://" ?
|
||||||
|
if (chars[i] == 's') { left--; i++; }
|
||||||
|
if (left >= 3 && chars[i] == ':' && chars[i + 1] == '/' && chars[i + 2] == '/') return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.AddPart(count, GetPart(line, lastEnd, start)); // word bit
|
unsafe static int ReduceLine(char* chars, ushort* mappings,
|
||||||
data.AddPart(count + 1, GetPart(line, start, nextEnd)); // url bit
|
int count, int offset, string line) {
|
||||||
count += 2;
|
bool lineStart = true;
|
||||||
|
for (int i = 0, last = line.Length - 1; i < line.Length;) {
|
||||||
|
char cur = line[i];
|
||||||
|
|
||||||
if ((prevFlags & LinkFlags.Append) != 0) {
|
// Trim colour codes and "> " line continues
|
||||||
string url = linkData[index - 1].LastUrl + data.urls[count - 1];
|
if (cur == '&' && i < last && IDrawer2D.ValidColCode(line[i + 1])) {
|
||||||
data.urls[count - 1] = url;
|
i += 2; continue;
|
||||||
data.parts[count - 2] = "";
|
}
|
||||||
UpdatePreviousUrls(index - 1, url);
|
if (cur == '>' && i < last && lineStart && line[i + 1] == ' ') {
|
||||||
|
lineStart = false; i += 2; continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((prevFlags & LinkFlags.NewLink) != 0)
|
lineStart = false;
|
||||||
data.flags |= LinkFlags.NewLink;
|
chars[count] = cur;
|
||||||
start = nextEnd;
|
mappings[count] = (ushort)(offset + i);
|
||||||
lastEnd = nextEnd;
|
i++; count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastEnd < line.Length)
|
|
||||||
data.AddPart(count, GetPart(line, lastEnd, line.Length)); // word bit
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdatePreviousUrls(int i, string url) {
|
|
||||||
while (i >= 0 && linkData[i].urls != null && (linkData[i].flags & LinkFlags.Continue) != 0) {
|
|
||||||
linkData[i].LastUrl = url;
|
|
||||||
if (linkData[i].urls.Length > 2 || (linkData[i].flags & LinkFlags.NewLink) != 0)
|
|
||||||
break;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string GetPart(string line, int start, int end) {
|
|
||||||
string part = line.Substring(start, end - start);
|
|
||||||
int lastCol = line.LastIndexOf('&', start, start);
|
|
||||||
// We may split up a line into say %e<word><url>
|
|
||||||
// url and word both need to have %e at the start.
|
|
||||||
|
|
||||||
if (lastCol >= 0 && IDrawer2D.ValidColCode(line, lastCol + 1)) {
|
|
||||||
part = "&" + line[lastCol + 1] + part;
|
|
||||||
}
|
|
||||||
return part;
|
|
||||||
}
|
|
||||||
|
|
||||||
int NextToken(string line, int start, ref LinkFlags prevFlags) {
|
|
||||||
bool isWrapped = start == 0 && line.StartsWith("> ");
|
|
||||||
if ((prevFlags & LinkFlags.Continue) != 0 && isWrapped) {
|
|
||||||
prevFlags = 0;
|
|
||||||
if (!Utils.IsUrlPrefix(Utils.StripColours(line), 2))
|
|
||||||
prevFlags |= LinkFlags.Append;
|
|
||||||
else
|
|
||||||
prevFlags |= LinkFlags.NewLink;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
prevFlags = LinkFlags.NewLink;
|
|
||||||
int nextHttp = line.IndexOf("http://", start);
|
|
||||||
int nextHttps = line.IndexOf("https://", start);
|
|
||||||
return nextHttp == -1 ? nextHttps : nextHttp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTokensCount(int index, string line) {
|
|
||||||
int start = 0, lastEnd = 0, count = 0;
|
|
||||||
LinkFlags prevFlags = index > 0 ? linkData[index - 1].flags : 0;
|
|
||||||
|
|
||||||
while ((start = NextToken(line, start, ref prevFlags)) >= 0) {
|
|
||||||
int nextEnd = line.IndexOf(' ', start);
|
|
||||||
if (nextEnd == -1)
|
|
||||||
nextEnd = line.Length;
|
|
||||||
|
|
||||||
start = nextEnd;
|
|
||||||
lastEnd = nextEnd;
|
|
||||||
count += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastEnd < line.Length) count++;
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LinkData {
|
unsafe static void Output(Portion bit, ushort* mappings, int count,
|
||||||
public Rectangle[] bounds;
|
int target, string[] lines, ref Portion* portions) {
|
||||||
public string[] parts, urls;
|
int lineBeg = 0, lineLen = 0, lineEnd = 0, total = 0;
|
||||||
public LinkFlags flags;
|
for (int i = 0; i < lines.Length; i++) {
|
||||||
|
string line = lines[i];
|
||||||
|
if (line == null) continue;
|
||||||
|
|
||||||
public void AddPart(int index, string part) {
|
if (i == target) {
|
||||||
parts[index] = part;
|
lineBeg = total; lineLen = line.Length;
|
||||||
urls[index] = part;
|
lineEnd = lineBeg + lineEnd;
|
||||||
|
}
|
||||||
|
total += line.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LastUrl {
|
bit.Beg = mappings[bit.ReducedBeg];
|
||||||
get { return urls[parts.Length - 1]; }
|
if (bit.Beg >= lineEnd) return;
|
||||||
set { urls[parts.Length - 1] = value; }
|
|
||||||
}
|
// Map back this reduced portion to original lines
|
||||||
|
int end = bit.ReducedBeg + (bit.ReducedLen & 0x7FFF);
|
||||||
|
end = end < count ? mappings[end] : total;
|
||||||
|
bit.Len = end - bit.Beg;
|
||||||
|
|
||||||
|
// Adjust this reduced portion to lie inside line we care about
|
||||||
|
if (bit.Beg >= lineBeg) {
|
||||||
|
} else if (bit.Beg + bit.Len > lineBeg) {
|
||||||
|
// Clamp start of portion to lie in this line
|
||||||
|
int underBy = (bit.Beg + bit.Len) - lineBeg;
|
||||||
|
bit.Beg += underBy; bit.Len -= underBy;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
// Clamp length of portion to lie in this line
|
||||||
enum LinkFlags : byte {
|
int overBy = (bit.Beg + bit.Len) - lineEnd;
|
||||||
Continue = 2, // "part1" "> part2" type urls
|
if (overBy > 0) bit.Len -= overBy;
|
||||||
Append = 4, // used for internally combining "part2" and "part2"
|
|
||||||
NewLink = 8, // used to signify that part2 is a separate url from part1
|
bit.Beg -= lineBeg;
|
||||||
|
if (bit.Len == 0) return;
|
||||||
|
*portions = bit; portions++;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Portion { public int ReducedBeg, ReducedLen, Beg, Len; }
|
||||||
|
unsafe int Reduce(char* chars, int target, Portion* portions) {
|
||||||
|
ushort* mappings = stackalloc ushort[lines.Length * 96];
|
||||||
|
Portion* portionsStart = portions;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int i = 0, offset = 0; i < lines.Length; i++) {
|
||||||
|
string line = lines[i];
|
||||||
|
if (line == null) continue;
|
||||||
|
|
||||||
|
count = ReduceLine(chars, mappings, count, offset, line);
|
||||||
|
offset += line.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now find http:// and https:// urls
|
||||||
|
int urlEnd = 0;
|
||||||
|
for (;;) {
|
||||||
|
int nextUrlStart = NextUrl(chars, urlEnd, count);
|
||||||
|
if (nextUrlStart == -1) nextUrlStart = count;
|
||||||
|
|
||||||
|
// add normal portion between urls
|
||||||
|
Portion bit = default(Portion); bit.ReducedBeg = urlEnd;
|
||||||
|
bit.ReducedLen = nextUrlStart - urlEnd;
|
||||||
|
Output(bit, mappings, count, target, lines, ref portions);
|
||||||
|
|
||||||
|
if (nextUrlStart == count) break;
|
||||||
|
// work out how long this url is
|
||||||
|
urlEnd = nextUrlStart;
|
||||||
|
for (; urlEnd < count && chars[urlEnd] != ' '; urlEnd++) { }
|
||||||
|
|
||||||
|
// add this url portion
|
||||||
|
bit = default(Portion); bit.ReducedBeg = nextUrlStart;
|
||||||
|
bit.ReducedLen = (urlEnd - nextUrlStart) | 0x8000;
|
||||||
|
Output(bit, mappings, count, target, lines, ref portions);
|
||||||
|
}
|
||||||
|
return (int)(portions - portionsStart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user