diff --git a/GameData/ConformalDecals/Plugins/ConformalDecals.dll b/GameData/ConformalDecals/Plugins/ConformalDecals.dll index 76afae2..f6320b6 100644 Binary files a/GameData/ConformalDecals/Plugins/ConformalDecals.dll and b/GameData/ConformalDecals/Plugins/ConformalDecals.dll differ diff --git a/GameData/ConformalDecals/Resources/ConformalDecals.cfg b/GameData/ConformalDecals/Resources/ConformalDecals.cfg index bed0e9c..4a8e3e6 100644 --- a/GameData/ConformalDecals/Resources/ConformalDecals.cfg +++ b/GameData/ConformalDecals/Resources/ConformalDecals.cfg @@ -20,4 +20,39 @@ CONFORMALDECALS { shader = KSP/Specular (Transparent) shader = Solid Color (Alpha) } + + FONT { + name = LiberationSans SDF + title = Liberation Sans + } + + FONT { + name = Calibri SDF + title = Calibri + } + + FONT { + name = NotoSans-Regular SDF + title = Noto Sans + } + + FONT { + name = Dited SDF + title = Dited + } + + FONT { + name = Waukegan LDO Extended SDF + title = Waukegan Extended + } + + FONT { + name = Nasalization SDF + title = Nasalization + } + + FONT { + name = Helvetica SDF + title = Helvetica + } } \ No newline at end of file diff --git a/GameData/ConformalDecals/Resources/conformaldecals.kspfont b/GameData/ConformalDecals/Resources/conformaldecals.kspfont index 94dc394..efe7dea 100644 --- a/GameData/ConformalDecals/Resources/conformaldecals.kspfont +++ b/GameData/ConformalDecals/Resources/conformaldecals.kspfont @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c543a58c9bc1ea96e26ce85625ca1fd765f5f1067f0eeca8fbdf68a2fe7e66ab -size 139184 +oid sha256:29f1da73b9f5fbd996162fb61f99378b5f05fbc35d93d04907c6025963f8a4c4 +size 347881 diff --git a/GameData/ConformalDecals/Resources/ui.conformaldecals b/GameData/ConformalDecals/Resources/ui.conformaldecals index c8c8f29..c5bf6db 100644 Binary files a/GameData/ConformalDecals/Resources/ui.conformaldecals and b/GameData/ConformalDecals/Resources/ui.conformaldecals differ diff --git a/Source/ConformalDecals/ConformalDecals.csproj b/Source/ConformalDecals/ConformalDecals.csproj index 2c7ba10..29fc765 100644 --- a/Source/ConformalDecals/ConformalDecals.csproj +++ b/Source/ConformalDecals/ConformalDecals.csproj @@ -30,7 +30,7 @@ prompt 4 bin\Release\ConformalDecals.xml - CS1591 + CS1591,CS0649 @@ -83,10 +83,13 @@ + - + + + diff --git a/Source/ConformalDecals/DecalConfig.cs b/Source/ConformalDecals/DecalConfig.cs index 40d2789..d019383 100644 --- a/Source/ConformalDecals/DecalConfig.cs +++ b/Source/ConformalDecals/DecalConfig.cs @@ -1,13 +1,17 @@ using System.Collections.Generic; +using ConformalDecals.Text; using ConformalDecals.Util; +using TMPro; +using UniLinq; using UnityEngine; namespace ConformalDecals { public static class DecalConfig { - private static Texture2D _blankNormal; - private static List _shaderBlacklist; - private static int _decalLayer = 31; - private static bool _selectableInFlight; + private static Texture2D _blankNormal; + private static List _shaderBlacklist; + private static Dictionary _fontList; + private static int _decalLayer = 31; + private static bool _selectableInFlight; private struct LegacyShaderEntry { public string name; @@ -37,12 +41,17 @@ namespace ConformalDecals { }, }; + public static Texture2D BlankNormal => _blankNormal; public static int DecalLayer => _decalLayer; public static bool SelectableInFlight => _selectableInFlight; + public static IEnumerable Fonts => _fontList.Values; + + public static DecalFont FallbackFont { get; private set; } + public static bool IsBlacklisted(Shader shader) { return IsBlacklisted(shader.name); } @@ -63,14 +72,40 @@ namespace ConformalDecals { return false; } + public static DecalFont GetFont(string name) { + if (_fontList.TryGetValue(name, out var font)) { + return font; + } + else { + throw new KeyNotFoundException($"Font {name} not found"); + } + } + private static void ParseConfig(ConfigNode node) { + + ParseUtil.ParseIntIndirect(ref _decalLayer, node, "decalLayer"); + ParseUtil.ParseBoolIndirect(ref _selectableInFlight, node, "selectableInFlight"); + foreach (var blacklist in node.GetNodes("SHADERBLACKLIST")) { foreach (var shaderName in blacklist.GetValuesList("shader")) { _shaderBlacklist.Add(shaderName); } - - ParseUtil.ParseIntIndirect(ref _decalLayer, node, "decalLayer"); - ParseUtil.ParseBoolIndirect(ref _selectableInFlight, node, "selectableInFlight"); + } + + var allFonts = Resources.FindObjectsOfTypeAll(); + + foreach (var fontNode in node.GetNodes("FONT")) { + var name = ParseUtil.ParseString(fontNode, "name"); + var title = ParseUtil.ParseString(fontNode, "title", true, name); + var style = ParseUtil.ParseInt(fontNode, "style", true); + + var font = allFonts.First(o => o.name == name); + if (font == null) { + Debug.LogWarning($"[ConformalDecals] Could not found named {name}"); + } + + Debug.Log($"Adding font named {name}"); + _fontList.Add(name, new DecalFont(title, font, (FontStyles) style)); } } @@ -96,6 +131,7 @@ namespace ConformalDecals { // ReSharper disable once UnusedMember.Global public static void ModuleManagerPostLoad() { _shaderBlacklist = new List(); + _fontList = new Dictionary(); var configs = GameDatabase.Instance.GetConfigs("CONFORMALDECALS"); diff --git a/Source/ConformalDecals/ModuleConformalText.cs b/Source/ConformalDecals/ModuleConformalText.cs index 6957e51..93c6e19 100644 --- a/Source/ConformalDecals/ModuleConformalText.cs +++ b/Source/ConformalDecals/ModuleConformalText.cs @@ -1,33 +1,52 @@ +using System; using ConformalDecals.Text; using ConformalDecals.UI; using ConformalDecals.Util; using TMPro; using UnityEngine; -using UnityEngine.UI; namespace ConformalDecals { - public class ModuleConformalText: ModuleConformalDecal { + public class ModuleConformalText : ModuleConformalDecal { [KSPField(isPersistant = true)] public string text = "Hello World!"; + [KSPField(isPersistant = true)] public string font = "Calibri SDF"; + [KSPField(isPersistant = true)] public int style; + [KSPField(isPersistant = true)] public bool vertical; + [KSPField(isPersistant = true)] public Color color = Color.black; + [KSPField(isPersistant = true)] public Color outlineColor = Color.white; + [KSPField(isPersistant = true)] public float outlineWidth; - private GameObject _textEntryGui; + private DecalText _text; + + private TextEntryController _textEntryController; public override void OnLoad(ConfigNode node) { base.OnLoad(node); - } public override void OnStart(StartState state) { base.OnStart(state); + var decalFont = DecalConfig.GetFont(font); + + _text = new DecalText { + text = text, + font = decalFont, + style = (FontStyles) style, + vertical = vertical, + color = color, + outlineColor = outlineColor, + outlineWidth = outlineWidth + }; + } + + public void OnTextUpdate(DecalText newText) { + _text = newText; } [KSPEvent(guiActive = false, guiActiveEditor = true, guiName = "#LOC_ConformalDecals_gui-select-flag")] - public void SetText() - { - if (_textEntryGui == null) { - _textEntryGui = Instantiate(UILoader.textEntryPrefab, MainCanvasUtil.MainCanvas.transform, true); - //_textEntryGui.AddComponent(); - //MenuNavigation.SpawnMenuNavigation(_textEntryGui, Navigation.Mode.Automatic, true); + public void SetText() { + if (_textEntryController == null) { + _textEntryController = TextEntryController.Create(_text, OnTextUpdate); } } } diff --git a/Source/ConformalDecals/Text/DecalFont.cs b/Source/ConformalDecals/Text/DecalFont.cs new file mode 100644 index 0000000..936a44b --- /dev/null +++ b/Source/ConformalDecals/Text/DecalFont.cs @@ -0,0 +1,27 @@ +using System; +using TMPro; + +namespace ConformalDecals.Text { + public class DecalFont { + public readonly string title; + public readonly TMP_FontAsset fontAsset; + public readonly FontStyles fontStyle; + + public DecalFont(string title, TMP_FontAsset fontAsset, FontStyles fontStyle) { + if (fontAsset == null) throw new ArgumentNullException(nameof(fontAsset)); + + this.title = title; + this.fontAsset = fontAsset; + this.fontStyle = fontStyle; + } + + public void SetupSample(TMP_Text tmp) { + if (tmp == null) throw new ArgumentNullException(nameof(tmp)); + if (fontAsset == null) throw new InvalidOperationException("DecalFont has not been initialized and Font is null."); + + tmp.text = title; + tmp.font = fontAsset; + tmp.fontStyle = fontStyle; + } + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/Text/DecalText.cs b/Source/ConformalDecals/Text/DecalText.cs new file mode 100644 index 0000000..aa59d21 --- /dev/null +++ b/Source/ConformalDecals/Text/DecalText.cs @@ -0,0 +1,16 @@ +using System; +using TMPro; +using UnityEngine; + +namespace ConformalDecals.Text { + public struct DecalText { + public string text; + public DecalFont font; + public FontStyles style; + public bool vertical; + + public Color color; + public Color outlineColor; + public float outlineWidth; + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/Text/FormattedText.cs b/Source/ConformalDecals/Text/FormattedText.cs deleted file mode 100644 index e69ab2c..0000000 --- a/Source/ConformalDecals/Text/FormattedText.cs +++ /dev/null @@ -1,15 +0,0 @@ -using TMPro; -using UnityEngine; - -namespace ConformalDecals.Text { - public struct FormattedText { - public string text; - public TMP_FontAsset font; - public FontStyles style; - public bool vertical; - - public Color32 color; - public Color32 outlineColor; - public float outlineWidth; - } -} \ No newline at end of file diff --git a/Source/ConformalDecals/UI/FontMenuController.cs b/Source/ConformalDecals/UI/FontMenuController.cs new file mode 100644 index 0000000..5b28139 --- /dev/null +++ b/Source/ConformalDecals/UI/FontMenuController.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using ConformalDecals.Text; +using UniLinq; +using UnityEngine; +using UnityEngine.UI; + +namespace ConformalDecals.UI { + public class FontMenuController : MonoBehaviour { + [SerializeField] private GameObject _menuItem; + [SerializeField] private GameObject _menuList; + + public DecalFont currentFont; + + public delegate void FontUpdateReceiver(DecalFont font); + + public FontUpdateReceiver fontUpdateCallback; + + public static FontMenuController Create(IEnumerable fonts, DecalFont currentFont, FontUpdateReceiver fontUpdateCallback) { + var menu = Instantiate(UILoader.FontMenuPrefab, MainCanvasUtil.MainCanvas.transform, true); + menu.AddComponent(); + MenuNavigation.SpawnMenuNavigation(menu, Navigation.Mode.Automatic, true); + + var controller = menu.GetComponent(); + controller.fontUpdateCallback = fontUpdateCallback; + controller.currentFont = currentFont; + controller.Populate(fonts); + return controller; + } + + public void OnClose() { + Destroy(gameObject); + } + + public void OnFontSelected(DecalFont font) { + currentFont = font ?? throw new ArgumentNullException(nameof(font)); + fontUpdateCallback(currentFont); + } + + public void Populate(IEnumerable fonts) { + if (fonts == null) throw new ArgumentNullException(nameof(fonts)); + + Toggle active = null; + + foreach (var font in fonts.OrderBy(x => x.title)) { + Debug.Log(font.title); + var listItem = GameObject.Instantiate(_menuItem, _menuList.transform); + listItem.name = font.title; + listItem.SetActive(true); + + var fontItem = listItem.AddComponent(); + fontItem.Font = font; + fontItem.fontSelectionCallback = OnFontSelected; + + if (font == currentFont) active = fontItem.toggle; + } + + if (active != null) active.isOn = true; + } + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/UI/FontMenuItem.cs b/Source/ConformalDecals/UI/FontMenuItem.cs new file mode 100644 index 0000000..8d9bc74 --- /dev/null +++ b/Source/ConformalDecals/UI/FontMenuItem.cs @@ -0,0 +1,36 @@ +using System; +using ConformalDecals.Text; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace ConformalDecals.UI { + public class FontMenuItem : MonoBehaviour { + public DecalFont Font { + get => _font; + set { + _font = value; + _font.SetupSample(_label); + } + } + + public delegate void FontSelectionReceiver(DecalFont font); + + public FontSelectionReceiver fontSelectionCallback; + public Toggle toggle; + + private DecalFont _font; + private TMP_Text _label; + + private void Awake() { + _label = gameObject.GetComponentInChildren(); + toggle = gameObject.GetComponent(); + toggle.isOn = false; + toggle.onValueChanged.AddListener(delegate { OnToggle(toggle); }); + } + + public void OnToggle(Toggle change) { + if (change.isOn) fontSelectionCallback?.Invoke(_font); + } + } +} \ No newline at end of file diff --git a/Source/ConformalDecals/UI/TextEntryController.cs b/Source/ConformalDecals/UI/TextEntryController.cs index 115e4c8..fbc5436 100644 --- a/Source/ConformalDecals/UI/TextEntryController.cs +++ b/Source/ConformalDecals/UI/TextEntryController.cs @@ -1,4 +1,3 @@ -using System; using ConformalDecals.Text; using TMPro; using UnityEngine; @@ -6,12 +5,15 @@ using UnityEngine.UI; namespace ConformalDecals.UI { public class TextEntryController : MonoBehaviour { - private FormattedText _text; + public delegate void TextUpdateReceiver(DecalText text); + + public TextUpdateReceiver textUpdateCallback; + + public DecalText decalText; + private FontMenuController _fontMenu; [SerializeField] private Selectable _textBox; - [SerializeField] private Toggle _fontColorButton; - [SerializeField] private Toggle _fontButton; - [SerializeField] private Toggle _outlineColorButton; + [SerializeField] private Button _fontButton; [SerializeField] private Slider _outlineWidthSlider; [SerializeField] private Toggle _boldButton; @@ -20,89 +22,123 @@ namespace ConformalDecals.UI { [SerializeField] private Toggle _smallCapsButton; [SerializeField] private Toggle _verticalButton; - public delegate void TextUpdateReceiver(FormattedText text); + public static TextEntryController Create(DecalText text, TextUpdateReceiver textUpdateCallback) { + var window = Instantiate(UILoader.TextEntryPrefab, MainCanvasUtil.MainCanvas.transform, true); + window.AddComponent(); + MenuNavigation.SpawnMenuNavigation(window, Navigation.Mode.Automatic, true); - public delegate void TextCancelReceiver(); + var controller = window.GetComponent(); + controller.decalText = text; + controller.textUpdateCallback = textUpdateCallback; + text.font.SetupSample(controller._fontButton.GetComponentInChildren()); - public TextUpdateReceiver textUpdateCallback; - public TextCancelReceiver textCancelCallback; + return controller; + } private void Start() { - (_textBox as TMP_InputField).text = _text.text; + ((TMP_InputField) _textBox).text = decalText.text; + + _outlineWidthSlider.value = decalText.outlineWidth; + _boldButton.isOn = (decalText.style & FontStyles.Bold) != 0; + _italicButton.isOn = (decalText.style & FontStyles.Italic) != 0; + _underlineButton.isOn = (decalText.style & FontStyles.Underline) != 0; + _smallCapsButton.isOn = (decalText.style & FontStyles.SmallCaps) != 0; + _verticalButton.isOn = decalText.vertical; - _boldButton.isOn = (_text.style | FontStyles.Bold) != 0; - _italicButton.isOn = (_text.style | FontStyles.Italic) != 0; - _underlineButton.isOn = (_text.style | FontStyles.Underline) != 0; - _smallCapsButton.isOn = (_text.style | FontStyles.SmallCaps) != 0; - _verticalButton.isOn = _text.vertical; } - public void Close() { + public void OnClose() { + if (_fontMenu != null) _fontMenu.OnClose(); Destroy(gameObject); } - public void OnCancel() { - textCancelCallback(); - Close(); + public void OnAnyUpdate() { + textUpdateCallback(decalText); } - public void OnApply() { - textUpdateCallback(_text); - Close(); + public void OnTextUpdate(string newText) { + this.decalText.text = newText; + + OnAnyUpdate(); } - public void OnTextUpdate(string text) { - _text.text = text; - textUpdateCallback(_text); + public void OnFontMenu() { + if (_fontMenu == null) _fontMenu = FontMenuController.Create(DecalConfig.Fonts, decalText.font, OnFontUpdate); + } + + public void OnFontUpdate(DecalFont font) { + decalText.font = font; + font.SetupSample(_fontButton.GetComponentInChildren()); + + var textBox = ((TMP_InputField) _textBox); + textBox.textComponent.fontStyle = decalText.style | decalText.font.fontStyle; + textBox.fontAsset = decalText.font.fontAsset; + OnAnyUpdate(); } - public void OnFontMenu(bool state) { } - public void OnColorMenu(bool state) { } + public void OnColorMenu() { } - public void OnOutlineColorMenu(bool state) { } + public void OnColorUpdate(Color color) { + decalText.color = color; + OnAnyUpdate(); + } - public void OnOutlineUpdate(float value) { - _text.outlineWidth = value; - textUpdateCallback(_text); + public void OnOutlineColorMenu() { } + public void OnOutlineColorUpdate(Color color) { + decalText.outlineColor = color; + OnAnyUpdate(); + } + + public void OnOutlineUpdate(float value) { + decalText.outlineWidth = value; + OnAnyUpdate(); } public void OnBoldUpdate(bool state) { - if (state) _text.style |= FontStyles.Bold; - else _text.style &= ~FontStyles.Bold; + if (state) decalText.style |= FontStyles.Bold; + else decalText.style &= ~FontStyles.Bold; - textUpdateCallback(_text); + ((TMP_InputField) _textBox).textComponent.fontStyle = decalText.style | decalText.font.fontStyle; + + OnAnyUpdate(); } public void OnItalicUpdate(bool state) { - if (state) _text.style |= FontStyles.Italic; - else _text.style &= ~FontStyles.Italic; + if (state) decalText.style |= FontStyles.Italic; + else decalText.style &= ~FontStyles.Italic; + + ((TMP_InputField) _textBox).textComponent.fontStyle = decalText.style | decalText.font.fontStyle; - textUpdateCallback(_text); + OnAnyUpdate(); } public void OnUnderlineUpdate(bool state) { - if (state) _text.style |= FontStyles.Underline; - else _text.style &= ~FontStyles.Underline; + if (state) decalText.style |= FontStyles.Underline; + else decalText.style &= ~FontStyles.Underline; - textUpdateCallback(_text); + ((TMP_InputField) _textBox).textComponent.fontStyle = decalText.style | decalText.font.fontStyle; + + OnAnyUpdate(); } public void OnSmallCapsUpdate(bool state) { - if (state) _text.style |= FontStyles.SmallCaps; - else _text.style &= ~FontStyles.SmallCaps; + if (state) decalText.style |= FontStyles.SmallCaps; + else decalText.style &= ~FontStyles.SmallCaps; + + ((TMP_InputField) _textBox).textComponent.fontStyle = decalText.style | decalText.font.fontStyle; - textUpdateCallback(_text); + OnAnyUpdate(); } public void OnVerticalUpdate(bool state) { - _text.vertical = state; - textUpdateCallback(_text); + decalText.vertical = state; + OnAnyUpdate(); } } } \ No newline at end of file diff --git a/Source/ConformalDecals/UI/UILoader.cs b/Source/ConformalDecals/UI/UILoader.cs index aa6fb8c..8c7f876 100644 --- a/Source/ConformalDecals/UI/UILoader.cs +++ b/Source/ConformalDecals/UI/UILoader.cs @@ -8,30 +8,22 @@ namespace ConformalDecals.UI { public class UILoader : MonoBehaviour { private static readonly string Path = KSPUtil.ApplicationRootPath + "GameData/ConformalDecals/Resources/"; - public static GameObject textEntryPrefab; + private static GameObject _textEntryPrefab; + private static GameObject _fontMenuPrefab; + + public static GameObject FontMenuPrefab => _fontMenuPrefab; + public static GameObject TextEntryPrefab => _textEntryPrefab; private void Awake() { var prefabs = AssetBundle.LoadFromFile(Path + "ui.conformaldecals"); - textEntryPrefab = prefabs.LoadAsset("TextEntryPanel") as GameObject; - - ProcessWindow(textEntryPrefab); - - Debug.Log("[ConformalDecals] UI prefabs loaded and modified"); - Debug.Log($"[ConformalDecals] {MainCanvasUtil.MainCanvas.renderMode}"); - Debug.Log($"[ConformalDecals] {MainCanvasUtil.MainCanvas.sortingOrder}"); - Debug.Log($"[ConformalDecals] {MainCanvasUtil.MainCanvas.sortingLayerID}"); - Debug.Log($"[ConformalDecals] {MainCanvasUtil.MainCanvas.sortingLayerName}"); - foreach (var layer in SortingLayer.layers) { - Debug.Log(layer.name); - Debug.Log(layer.id); - Debug.Log(layer.value); - } - + _textEntryPrefab = prefabs.LoadAsset("TextEntryPanel") as GameObject; + _fontMenuPrefab = prefabs.LoadAsset("FontMenuPanel") as GameObject; - var window = Instantiate(UILoader.textEntryPrefab, MainCanvasUtil.MainCanvas.transform, true); + ProcessWindow(_textEntryPrefab); + ProcessWindow(_fontMenuPrefab); } - + private static void ProcessWindow(GameObject window) { var skin = UISkinManager.defaultSkin; var font = UISkinManager.TMPFont;