Skip to content

Commit

Permalink
Character counter, Live send mode, character counter, typing indicato…
Browse files Browse the repository at this point in the history
…r option & text macros
  • Loading branch information
nyakowint committed Dec 3, 2023
1 parent 973b410 commit 5febf8f
Show file tree
Hide file tree
Showing 10 changed files with 950 additions and 116 deletions.
160 changes: 120 additions & 40 deletions KeyboardOSC/ChatMode.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
Expand All @@ -15,13 +16,14 @@ namespace KeyboardOSC;
public static class ChatMode
{
private static bool _isSilentMsg;
private static bool _allowPartialSend;
private static bool _isFirstMsg;
private static string _currentText = "";
private static string _lastMsg = "";
private static DateTime _lastTypingTime;
private static readonly ManualLogSource Logger = Plugin.PluginLogger;
private static TextMeshProUGUI _oscBarText;
private static TextMeshProUGUI _charCounter;
private static List<KeyboardKey> _currentlyDownStickyKeys = new();
private static Timer _eventsTimer = new(1300);

public static void HandleKey(KeyboardKey.VirtualKeyEventData eventData)
{
Expand All @@ -44,6 +46,8 @@ private static void ProcessKey(VirtualKeyCode key, KeyboardKey.VirtualKeyEventDa
{
var isCtrlHeld = _currentlyDownStickyKeys
.Any(k => k.Key[0] is VirtualKeyCode.LCONTROL or VirtualKeyCode.RCONTROL);
var sendTyping = PluginSettings.GetSetting<bool>("TypingIndicator").Value;
var liveSendMode = PluginSettings.GetSetting<bool>("LiveSend").Value;
switch (key)
{
// backspace/delete keys
Expand All @@ -58,31 +62,37 @@ private static void ProcessKey(VirtualKeyCode key, KeyboardKey.VirtualKeyEventDa
#if DEBUG
Logger.LogInfo("bulk deleting chat text: " + _currentText);
#endif
if (sendTyping) SendTyping(_currentText.Length != 0);
if (liveSendMode) _eventsTimer.Start();
return;
}

_currentText = _currentText.Remove(key is VirtualKeyCode.DELETE ? 0 : _currentText.Length - 1, 1);
if (sendTyping) SendTyping(_currentText.Length != 0);
if (liveSendMode) _eventsTimer.Start();
UpdateChatText(_currentText);
return;
}
// silent switch (no sound on send, no typing indicator)
case VirtualKeyCode.TAB:
_isSilentMsg = !_isSilentMsg;
UpdateChatColor();
Logger.LogInfo($"Silent mode: {_isSilentMsg}");
return;
// clear shortcut
case VirtualKeyCode.ESCAPE:
_currentText = "";
UpdateChatText(_currentText);
Logger.LogInfo("INPUT CLEARED");
SendTyping(false);
ClearInput();
Logger.LogInfo("Input cleared");
return;
case VirtualKeyCode.END:
SendMessage("/chatbox/input", string.Empty, true, false);
Tools.SendOsc("/chatbox/input", string.Empty, true, false);
Logger.LogInfo("Chatbox cleared");
return;
case VirtualKeyCode.INSERT:
_currentText = _lastMsg;
UpdateChatText(_currentText);
_isFirstMsg = true;
Logger.LogInfo("Inserted last input");
return;
// copy + paste
case VirtualKeyCode.VK_C:
Expand All @@ -94,54 +104,91 @@ private static void ProcessKey(VirtualKeyCode key, KeyboardKey.VirtualKeyEventDa
_currentText += GUIUtility.systemCopyBuffer;
UpdateChatText(_currentText);
return;
// that silly "send as you're typing" quirk some other osc apps do
// no idea if this will break to the rate limit like my old method did, we'll see
case VirtualKeyCode.F6:
_allowPartialSend = !_allowPartialSend;
break;
// Send message (or clear for continuous)
case VirtualKeyCode.RETURN:
if (liveSendMode)
{
Logger.LogInfo($"Sending message: {_currentText.ReplaceShortcodes()} [^-^]");
_lastMsg = _currentText;
SendMessage(true);
ClearInput();
}
else
{
Logger.LogInfo($"Sending message: {_currentText.ReplaceShortcodes()}");
SendMessage();
ClearInput();
}

return;
}

// Normal character inputs
if (sendTyping) SendTyping(_currentText.Length != 0);
if (liveSendMode)
{
_eventsTimer.Start();
if (_currentText.IsNullOrWhiteSpace())
_isFirstMsg = true;
}

if (key is VirtualKeyCode.RETURN)
_currentText += character;
UpdateChatText(_currentText);
}

private static void TimerElapsed(object sender, ElapsedEventArgs e)
{
if (_isSilentMsg) return;
Logger.LogInfo("Timer elapsed, sending message");
if (_currentText.IsNullOrWhiteSpace())
{
if (_currentText.Length <= 0) return;
Logger.LogInfo("CHAT SENT: " + _currentText);
_lastMsg = _currentText;
SendMessage("/chatbox/input", _currentText, true, !_isSilentMsg);
UpdateChatText("");
_currentText = "";
_isSilentMsg = false;
Plugin.ReleaseStickyKeys.Invoke(Plugin.Instance.inputHandler, null);
Tools.SendOsc("/chatbox/input", string.Empty, true, false);
SendTyping(false);
UpdateChatColor();
return;
}

var sendTyping = PluginSettings.GetSetting<bool>("TypingIndicator").Value;
SendMessage(true);
if (sendTyping) SendTyping(true);
}

SendTyping(true);
_currentText += character;
UpdateChatText(_currentText);
private static void SendMessage(bool liveSend = false)
{
if (liveSend)
{
_eventsTimer.Stop();
Tools.SendOsc("/chatbox/input", _currentText.ReplaceShortcodes(), true, !_isSilentMsg || _isFirstMsg);
SendTyping(false);
_isFirstMsg = false;
}
else
{
Tools.SendOsc("/chatbox/input", _currentText.ReplaceShortcodes(), true, !_isSilentMsg);
SendTyping(false);
_lastMsg = _currentText;
ClearInput();
}
}

private static void SendMessage(string address, string msg, bool now, bool sound)
private static void ClearInput()
{
Tools.SendOsc(address, msg, now, sound);
UpdateChatText(string.Empty);
_currentText = string.Empty;
_isSilentMsg = false;
UpdateChatColor();
Plugin.ReleaseStickyKeys.Invoke(Plugin.Instance.inputHandler, null);
}

private static void SendTyping(bool typing)
{
if (typing && (DateTime.Now - _lastTypingTime).TotalSeconds <= 2 || _isSilentMsg) return;
_lastTypingTime = DateTime.Now;
if (typing && _isSilentMsg) return;
Tools.SendOsc("/chatbox/typing", typing);
if (_allowPartialSend)
{
SendMessage("/chatbox/input", _currentText, true, false);
}
}

public static void Setup(TextMeshProUGUI obText)
public static void Setup(TextMeshProUGUI barText, TextMeshProUGUI charCounter)
{
_oscBarText = obText;
_oscBarText = barText;
_charCounter = charCounter;
_eventsTimer.Elapsed += TimerElapsed;
var stickyKeysField = AccessTools.Field(typeof(KeyboardInputHandler), "CurrentlyDownStickyKeys");
_currentlyDownStickyKeys = (List<KeyboardKey>) stickyKeysField.GetValue(Plugin.Instance.inputHandler);
}
Expand All @@ -150,15 +197,48 @@ private static void UpdateChatColor()
{
_oscBarText.color =
_isSilentMsg ? UIThemeHandler.Instance.T_WarningTone : UIThemeHandler.Instance.T_ConstrastingTone;
_charCounter.color = _currentText.Length switch
{
>= 120 => UIThemeHandler.Instance.T_ErrTone,
>= 85 => UIThemeHandler.Instance.T_WarningTone,
_ => UIThemeHandler.Instance.T_ConstrastingTone
};
}

private static void UpdateChatText(string text)
{
if (text.Length > 250)
if (text.Length > 144)
{
text = text.Substring(0, 250);
text = text.Substring(0, 144);
}

XSTools.SetTMPUIText(_charCounter, $"{text.Length}/144");
UpdateChatColor();

XSTools.SetTMPUIText(_oscBarText, text);
}

private static string ReplaceShortcodes(this string input)
{
var shortcodes = new Dictionary<string, string>
{
{"//shrug", "¯\\_(ツ)_/¯"},
{"//happy", "(¬‿¬)"},
{"//table", "┬─┬"},
{"//music", "🎵"},
{"//cookie", "🍪"},
{"//star", ""},
{"//hrt", "💗"},
{"//2hrt", "💕"},
{"//skull", "💀"},
{"//skull2", ""},
};

foreach (var shortcode in shortcodes)
{
input = input.Replace(shortcode.Key, shortcode.Value);
}

return input;
}
}
25 changes: 25 additions & 0 deletions KeyboardOSC/KeyboardOSC.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyboardOSC", "KeyboardOSC.csproj", "{72AD72A8-83B0-4A8B-B359-38C07643272E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{72AD72A8-83B0-4A8B-B359-38C07643272E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72AD72A8-83B0-4A8B-B359-38C07643272E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72AD72A8-83B0-4A8B-B359-38C07643272E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72AD72A8-83B0-4A8B-B359-38C07643272E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A5466BE1-0020-4D4B-9735-7FA8E10143B1}
EndGlobalSection
EndGlobal
Loading

0 comments on commit 5febf8f

Please sign in to comment.