Skip to content

Commit

Permalink
Подсветка текста в фильтрах QOL (#14)
Browse files Browse the repository at this point in the history
* Add basic chat highlighting

* Add saving of highlighted words

* Fix highlights not loading properly

* Major refactor

* highlights ftl, autofil, placeholders etc
highlight full word only
background to TextEdit

* fix conflicts

* text highlight russian localization

* corvax fixes

* russian localization

* Next adaptation

---------

Co-authored-by: VitusVeit <[email protected]>
Co-authored-by: AwareFoxy <[email protected]>
Co-authored-by: FN <[email protected]>
  • Loading branch information
4 people authored Nov 9, 2024
1 parent 7a6e411 commit 9933ab1
Show file tree
Hide file tree
Showing 16 changed files with 513 additions and 9 deletions.
70 changes: 70 additions & 0 deletions Content.Client/Options/UI/OptionsTabControlRow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Content.Client._CorvaxNext.Options.UI;
using Content.Client.Stylesheets;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
Expand Down Expand Up @@ -120,6 +121,21 @@ public OptionSliderFloatCVar AddOptionPercentSlider(
{
return AddOption(new OptionSliderFloatCVar(this, _cfg, cVar, slider, min, max, scale, FormatPercent));
}

// Corvax-Highlights-Start
/// <summary>
/// Add a color slider option, backed by a simple string CVar.
/// </summary>
/// <param name="cVar">The CVar represented by the slider.</param>
/// <param name="slider">The UI control for the option.</param>
/// <returns>The option instance backing the added option.</returns>
public OptionColorSliderCVar AddOptionColorSlider(
CVarDef<string> cVar,
OptionColorSlider slider)
{
return AddOption(new OptionColorSliderCVar(this, _cfg, cVar, slider));
}
// Corvax-Highlights-End

/// <summary>
/// Add a slider option, backed by a simple integer CVar.
Expand Down Expand Up @@ -518,6 +534,60 @@ private void UpdateLabelValue()
}
}

/// <summary>
/// Implementation of a CVar option that simply corresponds with a string <see cref="OptionColorSlider"/>.
/// </summary>
/// <seealso cref="OptionsTabControlRow"/>
public sealed class OptionColorSliderCVar : BaseOptionCVar<string>
{
private readonly OptionColorSlider _slider;

protected override string Value
{
get => _slider.Slider.Color.ToHex();
set
{
_slider.Slider.Color = Color.FromHex(value);
UpdateLabelColor();
}
}

// Corvax-Highlights-Start
/// <summary>
/// Creates a new instance of this type.
/// </summary>
/// <remarks>
/// <para>
/// It is generally more convenient to call overloads on <see cref="OptionsTabControlRow"/>
/// such as <see cref="OptionsTabControlRow.AddOptionPercentSlider"/> instead of instantiating this type directly.
/// </para>
/// </remarks>
/// <param name="controller">The control row that owns this option.</param>
/// <param name="cfg">The configuration manager to get and set values from.</param>
/// <param name="cVar">The CVar that is being controlled by this option.</param>
/// <param name="slider">The UI control for the option.</param>
public OptionColorSliderCVar(
OptionsTabControlRow controller,
IConfigurationManager cfg,
CVarDef<string> cVar,
OptionColorSlider slider) : base(controller, cfg, cVar)
{
_slider = slider;

slider.Slider.OnColorChanged += _ =>
{
ValueChanged();
UpdateLabelColor();
};
}
// Corvax-Highlights-End

private void UpdateLabelColor()
{
_slider.ExampleLabel.FontColorOverride = Color.FromHex(Value);
}
}

/// <summary>
/// Implementation of a CVar option that simply corresponds with an integer <see cref="OptionSlider"/>.
/// </summary>
Expand Down
9 changes: 8 additions & 1 deletion Content.Client/Options/UI/Tabs/AccessibilityTab.xaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Control xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:Content.Client.Options.UI">
xmlns:ui="clr-namespace:Content.Client.Options.UI"
xmlns:cnui="clr-namespace:Content.Client._CorvaxNext.Options.UI">
<BoxContainer Orientation="Vertical">
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Orientation="Vertical" Margin="8">
Expand All @@ -9,6 +10,12 @@
<CheckBox Name="ColorblindFriendlyCheckBox" Text="{Loc 'ui-options-colorblind-friendly'}" />
<ui:OptionSlider Name="ChatWindowOpacitySlider" Title="{Loc 'ui-options-chat-window-opacity'}" />
<ui:OptionSlider Name="ScreenShakeIntensitySlider" Title="{Loc 'ui-options-screen-shake-intensity'}" />
<!-- Corvax-Highlights-Start -->
<CheckBox Name="AutoFillHighlightsCheckBox" Text="{Loc 'ui-options-auto-fill-highlights'}" />
<cnui:OptionColorSlider Name="HighlightsColorSlider"
Title="{Loc 'ui-options-highlights-color'}"
Example="{Loc 'ui-options-highlights-color-example'}"/>
<!-- Corvax-Highlights-End -->
</BoxContainer>
</ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" />
Expand Down
7 changes: 7 additions & 0 deletions Content.Client/Options/UI/Tabs/AccessibilityTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
// Corvax-Highlights-Start
using Content.Shared.Corvax.CCCVars;
// Corvax-Highlights-End

namespace Content.Client.Options.UI.Tabs;

Expand All @@ -17,6 +20,10 @@ public AccessibilityTab()
Control.AddOptionCheckBox(CCVars.ReducedMotion, ReducedMotionCheckBox);
Control.AddOptionPercentSlider(CCVars.ChatWindowOpacity, ChatWindowOpacitySlider);
Control.AddOptionPercentSlider(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider);
// Corvax-Highlights-Start
Control.AddOptionCheckBox(CCCVars.ChatAutoFillHighlights, AutoFillHighlightsCheckBox);
Control.AddOptionColorSlider(CCCVars.ChatHighlightsColor, HighlightsColorSlider);
// Corvax-Highlights-End

Control.Initialize();
}
Expand Down
128 changes: 127 additions & 1 deletion Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@
using Robust.Shared.Timing;
using Robust.Shared.Utility;

// Corvax-Highlights-Start
using Content.Client.CharacterInfo;
using static Content.Client.CharacterInfo.CharacterInfoSystem;
using Content.Shared.Corvax.CCCVars;
// Corvax-Highlights-End

namespace Content.Client.UserInterface.Systems.Chat;

public sealed class ChatUIController : UIController
// Corvax-Highlights
public sealed class ChatUIController : UIController, IOnSystemChanged<CharacterInfoSystem>
{
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IChatManager _manager = default!;
Expand All @@ -65,6 +72,9 @@ public sealed class ChatUIController : UIController
[UISystemDependency] private readonly TransformSystem? _transform = default;
[UISystemDependency] private readonly MindSystem? _mindSystem = default!;
[UISystemDependency] private readonly RoleCodewordSystem? _roleCodewordSystem = default!;
// Corvax-Highlights-Start
[UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!;
// Corvax-Highlights-End

[ValidatePrototypeId<ColorPalettePrototype>]
private const string ChatNamePalette = "ChatNames";
Expand Down Expand Up @@ -149,6 +159,25 @@ private readonly Dictionary<EntityUid, SpeechBubbleQueueData> _queuedSpeechBubbl
/// </summary>
private readonly Dictionary<ChatChannel, int> _unreadMessages = new();

// Corvax-Highlights-Start
/// <summary>
/// A list of words to be highlighted in the chatbox.
/// </summary>
private List<string> _highlights = [];

/// <summary>
/// The color (hex) in witch the words will be highlighted as.
/// </summary>
private string? _highlightsColor;

private bool _autoFillHighlightsEnabled;

/// <summary>
/// A bool to keep track if the 'CharacterUpdated' event is a new player attaching or the opening of the character info panel.
/// </summary>
private bool _charInfoIsAttach = false;
// Corvax-Highlights-End

// TODO add a cap for this for non-replays
public readonly List<(GameTick Tick, ChatMessage Msg)> History = new();

Expand All @@ -172,6 +201,9 @@ private readonly Dictionary<EntityUid, SpeechBubbleQueueData> _queuedSpeechBubbl
public event Action<ChatSelectChannel>? SelectableChannelsChanged;
public event Action<ChatChannel, int?>? UnreadMessageCountsUpdated;
public event Action<ChatMessage>? MessageAdded;
// Corvax-Highlights-Start
public event Action<string>? HighlightsUpdated;
// Corvax-Highlights-End

public override void Initialize()
{
Expand Down Expand Up @@ -240,6 +272,21 @@ public override void Initialize()

_config.OnValueChanged(CCVars.ChatWindowOpacity, OnChatWindowOpacityChanged);

// Corvax-Highlights-Start
_config.OnValueChanged(CCCVars.ChatAutoFillHighlights, (value) => { _autoFillHighlightsEnabled = value; });
_autoFillHighlightsEnabled = _config.GetCVar(CCCVars.ChatAutoFillHighlights);

_config.OnValueChanged(CCCVars.ChatHighlightsColor, (value) => { _highlightsColor = value; });
_highlightsColor = _config.GetCVar(CCCVars.ChatHighlightsColor);

// Load highlights if any were saved.
string highlights = _config.GetCVar(CCCVars.ChatHighlights);

if (!string.IsNullOrEmpty(highlights))
{
UpdateHighlights(highlights);
}
// Corvax-Highlights-End
}

public void OnScreenLoad()
Expand All @@ -257,6 +304,69 @@ public void OnScreenUnload()
SetMainChat(false);
}

// Corvax-Highlights-Start
public void OnSystemLoaded(CharacterInfoSystem system)
{
system.OnCharacterUpdate += CharacterUpdated;
}

public void OnSystemUnloaded(CharacterInfoSystem system)
{
system.OnCharacterUpdate -= CharacterUpdated;
}

private void CharacterUpdated(CharacterData data)
{
// If the _charInfoIsAttach is false then the character panel created the event, dismiss.
if (!_charInfoIsAttach)
return;

var (_, job, _, _, entityName) = data;

// If the character has a normal name (eg. "Name Surname" and not "Name Initial Surname" or a particular species name)
// subdivide it so that the name and surname individually get highlighted.
if (entityName.Count(c => c == ' ') == 1)
entityName = entityName.Replace(' ', '\n');

string newHighlights = entityName;

// Convert the job title to kebab-case and use it as a key for the loc file.
string jobKey = job.Replace(' ', '-').ToLower();

if (Loc.TryGetString($"highlights-{jobKey}", out var jobMatches))
newHighlights += '\n' + jobMatches.Replace(", ", "\n");

UpdateHighlights(newHighlights);
HighlightsUpdated?.Invoke(newHighlights);
_charInfoIsAttach = false;
}

public void UpdateHighlights(string highlights)
{
// Save the newly provided list of highlighs if different.
if (!_config.GetCVar(CCCVars.ChatHighlights).Equals(highlights, StringComparison.CurrentCultureIgnoreCase))
{
_config.SetCVar(CCCVars.ChatHighlights, highlights);
_config.SaveToFile();
}

// If the word is surrounded by "" we replace them with a whole-word regex tag.
highlights = highlights.Replace("\"", "\\b");

// Fill the array with the highlights separated by newlines, disregarding empty entries.
string[] arrHighlights = highlights.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
_highlights.Clear();
foreach (var keyword in arrHighlights)
{
_highlights.Add(keyword);
}

// Arrange the list in descending order so that when highlighting,
// the full word (eg. "Security") appears before the abbreviation (eg. "Sec").
_highlights.Sort((x, y) => y.Length.CompareTo(x.Length));
}
// Corvax-Highlights-End

private void OnChatWindowOpacityChanged(float opacity)
{
SetChatWindowOpacity(opacity);
Expand Down Expand Up @@ -426,6 +536,14 @@ public void SetSpeechBubbleRoot(LayoutContainer root)
private void OnAttachedChanged(EntityUid uid)
{
UpdateChannelPermissions();

// Corvax-Highlights-Start
if (_autoFillHighlightsEnabled)
{
_charInfoIsAttach = true;
_characterInfo.RequestCharacterInfo();
}
// Corvax-Highlights-End
}

private void AddSpeechBubble(ChatMessage msg, SpeechBubble.SpeechType speechType)
Expand Down Expand Up @@ -824,6 +942,14 @@ public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true)
msg.WrappedMessage = SharedChatSystem.InjectTagInsideTag(msg, "Name", "color", GetNameColor(SharedChatSystem.GetStringInsideTag(msg, "Name")));
}

// Corvax-Highlights-Start
// Color any words choosen by the client.
foreach (var highlight in _highlights)
{
msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, highlight, "color", _highlightsColor);
}
// Corvax-Highlights-End

// Color any codewords for minds that have roles that use them
if (_player.LocalUser != null && _mindSystem != null && _roleCodewordSystem != null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
<controls:ChannelFilterPopup
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls">
<PanelContainer Name="FilterPopupPanel" StyleClasses="BorderedWindowPanel">
<BoxContainer Orientation="Horizontal">
<Control MinSize="4 0"/>
<BoxContainer Name="FilterVBox" MinWidth="110" Margin="0 10" Orientation="Vertical" SeparationOverride="4"/>
<!-- Corvax-Highlights-Start -->
<BoxContainer Orientation="Horizontal" SeparationOverride="8" Margin="10 0">
<BoxContainer Name="FilterVBox" MinWidth="105" Margin="0 10" Orientation="Vertical" SeparationOverride="4"/>
<BoxContainer Name="HighlightsVBox" MinWidth="120" Margin="0 10" Orientation="Vertical" SeparationOverride="4">
<Label Text="{Loc 'hud-chatbox-highlights'}"/>
<!-- Custom background for the TextEdit -->
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#323446"/>
</PanelContainer.PanelOverride>
<TextEdit Name="HighlightEdit" MinHeight="150" Margin="5 5"/>
</PanelContainer>
<Button Name="HighlightButton" Text="{Loc 'hud-chatbox-highlights-button'}" ToolTip="{Loc 'hud-chatbox-highlights-tooltip'}"/>
</BoxContainer>
<!-- Corvax-Highlights-End -->
</BoxContainer>
</PanelContainer>
</controls:ChannelFilterPopup>
Loading

0 comments on commit 9933ab1

Please sign in to comment.