mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 14:00:21 +00:00
Refactor/rewrite form translation
1. faster control retrieval (fetch all once rather than search for each) 1. smaller methods & added comments (ez understanding) 3. easier to dump control list (ez translation file updates, future)
This commit is contained in:
parent
44c0b53852
commit
c28575aad7
1 changed files with 128 additions and 73 deletions
|
@ -11,106 +11,161 @@ namespace PKHeX.WinForms
|
||||||
public static class WinFormsUtil
|
public static class WinFormsUtil
|
||||||
{
|
{
|
||||||
#region Form Translation
|
#region Form Translation
|
||||||
|
private static readonly string[] Splitter = {" = "};
|
||||||
|
private const char Comment = '-';
|
||||||
|
private const char FormStart = '!';
|
||||||
internal static void TranslateInterface(Control form, string lang)
|
internal static void TranslateInterface(Control form, string lang)
|
||||||
{
|
{
|
||||||
// Check to see if a the translation file exists in the same folder as the executable
|
if (!TryGetTranslationFile(lang, out string[] rawlist))
|
||||||
string externalLangPath = $"lang_{lang}.txt";
|
return; // no translation data retrieved
|
||||||
string[] rawlist;
|
|
||||||
if (File.Exists(externalLangPath))
|
|
||||||
rawlist = File.ReadAllLines(externalLangPath);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var file = $"lang_{lang}";
|
|
||||||
rawlist = Util.GetStringList(file);
|
|
||||||
if (rawlist.Length == 0)
|
|
||||||
{
|
|
||||||
// Translation file does not exist as a resource; abort this function and don't translate UI.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<string> stringdata = new List<string>();
|
// Find Starting Point
|
||||||
int start = -1;
|
int start = GetTranslationStart(rawlist, form.Name);
|
||||||
for (int i = 0; i < rawlist.Length; i++)
|
if (start < 0) // no form info found
|
||||||
{
|
|
||||||
// Find our starting point
|
|
||||||
if (!rawlist[i].Contains($"! {form.Name}")) continue;
|
|
||||||
start = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (start < 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Rename Window Title
|
// Rename Window Title
|
||||||
string[] WindowName = rawlist[start].Split(new[] { " = " }, StringSplitOptions.None);
|
string[] WindowName = rawlist[start].Split(Splitter, StringSplitOptions.None);
|
||||||
if (WindowName.Length > 1) form.Text = WindowName[1];
|
if (WindowName.Length > 1) // window title is specified
|
||||||
|
form.Text = WindowName[1];
|
||||||
|
|
||||||
// Fetch controls to rename
|
// Fetch controls to rename
|
||||||
for (int i = start + 1; i < rawlist.Length; i++)
|
var stringdata = GetTranslationList(rawlist, start);
|
||||||
|
if (stringdata.Count == 0) // no translation data available
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Execute Translation
|
||||||
|
form.SuspendLayout();
|
||||||
|
TranslateForm(form, stringdata);
|
||||||
|
form.ResumeLayout();
|
||||||
|
}
|
||||||
|
private static bool TryGetTranslationFile(string lang, out string[] rawlist)
|
||||||
|
{
|
||||||
|
var file = $"lang_{lang}";
|
||||||
|
// Check to see if a the translation file exists in the same folder as the executable
|
||||||
|
string externalLangPath = $"{file}.txt";
|
||||||
|
if (File.Exists(externalLangPath))
|
||||||
{
|
{
|
||||||
if (rawlist[i].Length == 0) continue; // Skip Over Empty Lines, errhandled
|
try
|
||||||
if (rawlist[i][0] == '-') continue; // Keep translating if line is a comment line
|
{
|
||||||
if (rawlist[i][0] == '!') // Stop if we have reached the end of translation
|
rawlist = File.ReadAllLines(externalLangPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch { /* In use? Just return the internal resource. */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
rawlist = Util.GetStringList(file);
|
||||||
|
// If there's no strings (or null), the translation file does not exist.
|
||||||
|
// No file => abort this function and don't translate UI.
|
||||||
|
return rawlist?.Length > 0;
|
||||||
|
}
|
||||||
|
private static int GetTranslationStart(IReadOnlyList<string> rawlist, string name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < rawlist.Count; i++)
|
||||||
|
if (rawlist[i].StartsWith($"{FormStart} {name}"))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
private static List<string> GetTranslationList(IReadOnlyList<string> rawlist, int start)
|
||||||
|
{
|
||||||
|
List<string> stringdata = new List<string>();
|
||||||
|
for (int i = start + 1; i < rawlist.Count; i++)
|
||||||
|
{
|
||||||
|
var line = rawlist[i];
|
||||||
|
if (line.Length == 0) continue; // Skip Over Empty Lines
|
||||||
|
if (line[0] == Comment) continue; // Keep translating if line is a comment line
|
||||||
|
if (line[0] == FormStart) // Stop if we have reached the end of translation
|
||||||
break;
|
break;
|
||||||
stringdata.Add(rawlist[i]); // Add the entry to process later.
|
stringdata.Add(rawlist[i]); // Add the entry to process later.
|
||||||
}
|
}
|
||||||
|
return stringdata;
|
||||||
|
}
|
||||||
|
|
||||||
if (stringdata.Count == 0)
|
private static void TranslateForm(Control form, IEnumerable<string> stringdata)
|
||||||
return;
|
{
|
||||||
|
// Only fetch the list of controls once; store in dictionary for faster translation
|
||||||
// Find control then change display Text.
|
var controls = GetControlDictionary(form);
|
||||||
form.SuspendLayout();
|
|
||||||
foreach (string str in stringdata)
|
foreach (string str in stringdata)
|
||||||
{
|
{
|
||||||
string[] SplitString = str.Split(new[] { " = " }, StringSplitOptions.None);
|
string[] SplitString = str.Split(Splitter, StringSplitOptions.None);
|
||||||
if (SplitString.Length < 2)
|
if (SplitString.Length != 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
object c = FindControl(SplitString[0], form.Controls); // Find control within Form's controls
|
var controlName = SplitString[0];
|
||||||
if (c == null) // Not found
|
if (!controls.TryGetValue(controlName, out object c))
|
||||||
continue;
|
continue; // control not found
|
||||||
|
|
||||||
string text = SplitString[1]; // Text to set Control.Text to...
|
string text = SplitString[1];
|
||||||
|
if (c is Control r)
|
||||||
if (c is Control)
|
r.Text = text;
|
||||||
(c as Control).Text = text;
|
else if (c is ToolStripItem t)
|
||||||
else if (c is ToolStripItem)
|
t.Text = text;
|
||||||
(c as ToolStripItem).Text = text;
|
|
||||||
}
|
}
|
||||||
form.ResumeLayout();
|
|
||||||
}
|
}
|
||||||
private static object FindControl(string name, Control.ControlCollection c)
|
private static Dictionary<string, object> GetControlDictionary(Control form)
|
||||||
{
|
{
|
||||||
Control control = c.Find(name, true).FirstOrDefault();
|
return GetTranslatableControls(form)
|
||||||
if (control != null)
|
.GroupBy(p => p.Key, StringComparer.OrdinalIgnoreCase)
|
||||||
return control;
|
.ToDictionary(g => g.Key, g => g.First().Value, StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (MenuStrip menu in c.OfType<MenuStrip>())
|
|
||||||
{
|
|
||||||
var item = menu.Items.Find(name, true).FirstOrDefault();
|
|
||||||
if (item != null)
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
foreach (ContextMenuStrip strip in FindContextMenuStrips(c.OfType<Control>()))
|
|
||||||
{
|
|
||||||
var item = strip.Items.Find(name, true).FirstOrDefault();
|
|
||||||
if (item != null)
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
private static List<ContextMenuStrip> FindContextMenuStrips(IEnumerable<Control> c)
|
private static IEnumerable<KeyValuePair<string, object>> GetTranslatableControls(Control f)
|
||||||
{
|
{
|
||||||
List<ContextMenuStrip> cs = new List<ContextMenuStrip>();
|
foreach (var z in f.GetChildrenOfType<Control>())
|
||||||
foreach (Control control in c)
|
|
||||||
{
|
{
|
||||||
if (control.ContextMenuStrip != null)
|
switch (z)
|
||||||
cs.Add(control.ContextMenuStrip);
|
{
|
||||||
|
case ToolStrip menu:
|
||||||
|
foreach (var pair in GetToolStripMenuItems(menu))
|
||||||
|
yield return pair;
|
||||||
|
|
||||||
else if (control.Controls.Count > 0)
|
break;
|
||||||
cs.AddRange(FindContextMenuStrips(control.Controls.OfType<Control>()));
|
default:
|
||||||
|
if (string.IsNullOrWhiteSpace(z.Name))
|
||||||
|
break;
|
||||||
|
|
||||||
|
yield return new KeyValuePair<string, object>(z.Name, z);
|
||||||
|
|
||||||
|
if (z.ContextMenuStrip != null) // control has attached menustrip
|
||||||
|
foreach (var pair in GetToolStripMenuItems(z.ContextMenuStrip))
|
||||||
|
yield return pair;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cs;
|
|
||||||
}
|
}
|
||||||
|
private static IEnumerable<T> GetChildrenOfType<T>(this Control control) where T : class
|
||||||
|
{
|
||||||
|
foreach (Control child in control.Controls)
|
||||||
|
{
|
||||||
|
T childOfT = child as T;
|
||||||
|
if (childOfT != null)
|
||||||
|
yield return childOfT;
|
||||||
|
|
||||||
|
if (!child.HasChildren) continue;
|
||||||
|
foreach (T descendant in GetChildrenOfType<T>(child))
|
||||||
|
yield return descendant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static IEnumerable<KeyValuePair<string, object>> GetToolStripMenuItems(ToolStrip menu)
|
||||||
|
{
|
||||||
|
foreach (var i in menu.Items.OfType<ToolStripMenuItem>())
|
||||||
|
{
|
||||||
|
yield return new KeyValuePair<string, object>(i.Name, i);
|
||||||
|
foreach (var sub in GetToolsStripDropDownItems(i))
|
||||||
|
yield return new KeyValuePair<string, object>(sub.Name, sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static IEnumerable<ToolStripMenuItem> GetToolsStripDropDownItems(ToolStripDropDownItem item)
|
||||||
|
{
|
||||||
|
foreach (var dropDownItem in item.DropDownItems.OfType<ToolStripMenuItem>())
|
||||||
|
{
|
||||||
|
if (dropDownItem.HasDropDownItems)
|
||||||
|
foreach (ToolStripMenuItem subItem in GetToolsStripDropDownItems(dropDownItem))
|
||||||
|
yield return subItem;
|
||||||
|
|
||||||
|
yield return dropDownItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static void CenterToForm(this Control child, Control parent)
|
internal static void CenterToForm(this Control child, Control parent)
|
||||||
{
|
{
|
||||||
int x = parent.Location.X + (parent.Width - child.Width) / 2;
|
int x = parent.Location.X + (parent.Width - child.Width) / 2;
|
||||||
|
|
Loading…
Reference in a new issue