Port fancy speech bubbles (#29349)

This commit is contained in:
lzk
2025-05-06 19:49:42 +02:00
committed by GitHub
parent a6f2172d05
commit 740ce0e8ad
26 changed files with 273 additions and 39 deletions

View File

@@ -16,6 +16,7 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
private readonly TimeSpan _typingTimeout = TimeSpan.FromSeconds(2);
private TimeSpan _lastTextChange;
private bool _isClientTyping;
private bool _isClientChatFocused;
public override void Initialize()
{
@@ -31,7 +32,8 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
return;
// client typed something - show typing indicator
ClientUpdateTyping(true);
_isClientTyping = true;
ClientUpdateTyping();
_lastTextChange = _time.CurTime;
}
@@ -42,7 +44,19 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
return;
// client submitted text - hide typing indicator
ClientUpdateTyping(false);
_isClientTyping = false;
ClientUpdateTyping();
}
public void ClientChangedChatFocus(bool isFocused)
{
// don't update it if player don't want to show typing
if (!_cfg.GetCVar(CCVars.ChatShowTypingIndicator))
return;
// client submitted text - hide typing indicator
_isClientChatFocused = isFocused;
ClientUpdateTyping();
}
public override void Update(float frameTime)
@@ -55,23 +69,25 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
var dif = _time.CurTime - _lastTextChange;
if (dif > _typingTimeout)
{
// client didn't typed anything for a long time - hide indicator
ClientUpdateTyping(false);
// client didn't typed anything for a long time - change indicator
_isClientTyping = false;
ClientUpdateTyping();
}
}
}
private void ClientUpdateTyping(bool isClientTyping)
private void ClientUpdateTyping()
{
if (_isClientTyping == isClientTyping)
return;
// check if player controls any entity.
// check if player controls any pawn
if (_playerManager.LocalEntity == null)
return;
_isClientTyping = isClientTyping;
RaisePredictiveEvent(new TypingChangedEvent(isClientTyping));
var state = TypingIndicatorState.None;
if (_isClientChatFocused)
state = _isClientTyping ? TypingIndicatorState.Typing : TypingIndicatorState.Idle;
// send a networked event to server
RaisePredictiveEvent(new TypingChangedEvent(state));
}
private void OnShowTypingChanged(bool showTyping)
@@ -79,7 +95,8 @@ public sealed class TypingIndicatorSystem : SharedTypingIndicatorSystem
// hide typing indicator immediately if player don't want to show it anymore
if (!showTyping)
{
ClientUpdateTyping(false);
_isClientTyping = false;
ClientUpdateTyping();
}
}
}

View File

@@ -35,7 +35,6 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
return;
}
AppearanceSystem.TryGetData<bool>(uid, TypingIndicatorVisuals.IsTyping, out var isTyping, args.Component);
var layerExists = args.Sprite.LayerMapTryGet(TypingIndicatorLayers.Base, out var layer);
if (!layerExists)
layer = args.Sprite.LayerMapReserveBlank(TypingIndicatorLayers.Base);
@@ -44,6 +43,17 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
args.Sprite.LayerSetState(layer, proto.TypingState);
args.Sprite.LayerSetShader(layer, proto.Shader);
args.Sprite.LayerSetOffset(layer, proto.Offset);
args.Sprite.LayerSetVisible(layer, isTyping);
AppearanceSystem.TryGetData<TypingIndicatorState>(uid, TypingIndicatorVisuals.State, out var state);
args.Sprite.LayerSetVisible(layer, state != TypingIndicatorState.None);
switch (state)
{
case TypingIndicatorState.Idle:
args.Sprite.LayerSetState(layer, proto.IdleState);
break;
case TypingIndicatorState.Typing:
args.Sprite.LayerSetState(layer, proto.TypingState);
break;
}
}
}

View File

@@ -46,7 +46,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
if (!HasComp<HolopadUserComponent>(uid))
return;
var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.IsTyping);
var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.State);
RaiseNetworkEvent(netEv);
}

View File

@@ -918,6 +918,11 @@ public sealed class ChatUIController : UIController
_typingIndicator?.ClientChangedChatText();
}
public void NotifyChatFocus(bool isFocused)
{
_typingIndicator?.ClientChangedChatFocus(isFocused);
}
public void Repopulate()
{
foreach (var chat in _chats)

View File

@@ -34,6 +34,8 @@ public partial class ChatBox : UIWidget
ChatInput.Input.OnTextEntered += OnTextEntered;
ChatInput.Input.OnKeyBindDown += OnInputKeyBindDown;
ChatInput.Input.OnTextChanged += OnTextChanged;
ChatInput.Input.OnFocusEnter += OnFocusEnter;
ChatInput.Input.OnFocusExit += OnFocusExit;
ChatInput.ChannelSelector.OnChannelSelect += OnChannelSelect;
ChatInput.FilterButton.Popup.OnChannelFilter += OnChannelFilter;
@@ -174,6 +176,18 @@ public partial class ChatBox : UIWidget
_controller.NotifyChatTextChange();
}
private void OnFocusEnter(LineEditEventArgs args)
{
// Warn typing indicator about focus
_controller.NotifyChatFocus(true);
}
private void OnFocusExit(LineEditEventArgs args)
{
// Warn typing indicator about focus
_controller.NotifyChatFocus(false);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

View File

@@ -309,7 +309,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
if (receiverHolopad.Comp.Hologram == null)
continue;
_appearanceSystem.SetData(receiverHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.IsTyping, ev.IsTyping);
_appearanceSystem.SetData(receiverHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.State, ev.State);
}
}
}
@@ -591,7 +591,7 @@ public sealed class HolopadSystem : SharedHolopadSystem
continue;
if (user == null)
_appearanceSystem.SetData(linkedHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.IsTyping, false);
_appearanceSystem.SetData(linkedHolopad.Comp.Hologram.Value.Owner, TypingIndicatorVisuals.State, false);
linkedHolopad.Comp.Hologram.Value.Comp.LinkedEntity = user;
Dirty(linkedHolopad.Comp.Hologram.Value);

View File

@@ -45,7 +45,7 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
private void OnPlayerDetached(EntityUid uid, TypingIndicatorComponent component, PlayerDetachedEvent args)
{
// player left entity body - hide typing indicator
SetTypingIndicatorEnabled(uid, false);
SetTypingIndicatorState(uid, TypingIndicatorState.None);
}
private void OnGotEquipped(Entity<TypingIndicatorClothingComponent> entity, ref ClothingGotEquippedEvent args)
@@ -76,18 +76,18 @@ public abstract class SharedTypingIndicatorSystem : EntitySystem
if (!_actionBlocker.CanEmote(uid.Value) && !_actionBlocker.CanSpeak(uid.Value))
{
// nah, make sure that typing indicator is disabled
SetTypingIndicatorEnabled(uid.Value, false);
SetTypingIndicatorState(uid.Value, TypingIndicatorState.None);
return;
}
SetTypingIndicatorEnabled(uid.Value, ev.IsTyping);
SetTypingIndicatorState(uid.Value, ev.State);
}
private void SetTypingIndicatorEnabled(EntityUid uid, bool isEnabled, AppearanceComponent? appearance = null)
private void SetTypingIndicatorState(EntityUid uid, TypingIndicatorState state, AppearanceComponent? appearance = null)
{
if (!Resolve(uid, ref appearance, false))
return;
_appearance.SetData(uid, TypingIndicatorVisuals.IsTyping, isEnabled, appearance);
_appearance.SetData(uid, TypingIndicatorVisuals.State, state, appearance);
}
}

View File

@@ -12,11 +12,11 @@ namespace Content.Shared.Chat.TypingIndicator;
[Serializable, NetSerializable]
public sealed class TypingChangedEvent : EntityEventArgs
{
public readonly bool IsTyping;
public readonly TypingIndicatorState State;
public TypingChangedEvent(bool isTyping)
public TypingChangedEvent(TypingIndicatorState state)
{
IsTyping = isTyping;
State = state;
}
}

View File

@@ -19,6 +19,9 @@ public sealed partial class TypingIndicatorPrototype : IPrototype
[DataField("typingState", required: true)]
public string TypingState = default!;
[DataField("idleState", required: true)]
public string IdleState = default!;
[DataField("offset")]
public Vector2 Offset = new(0, 0);

View File

@@ -0,0 +1,11 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Chat.TypingIndicator;
[Serializable, NetSerializable]
public enum TypingIndicatorState
{
None = 0,
Idle = 1,
Typing = 2,
}

View File

@@ -5,7 +5,7 @@ namespace Content.Shared.Chat.TypingIndicator;
[Serializable, NetSerializable]
public enum TypingIndicatorVisuals : byte
{
IsTyping
State
}
[Serializable]

View File

@@ -1,3 +1,4 @@
using Content.Shared.Chat.TypingIndicator;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -34,11 +35,11 @@ public sealed class HolopadUserTypingChangedEvent : EntityEventArgs
/// <summary>
/// The typing indicator state
/// </summary>
public readonly bool IsTyping;
public readonly TypingIndicatorState State;
public HolopadUserTypingChangedEvent(NetEntity user, bool isTyping)
public HolopadUserTypingChangedEvent(NetEntity user, TypingIndicatorState state)
{
User = user;
IsTyping = isTyping;
State = state;
}
}

View File

@@ -1,60 +1,73 @@
- type: typingIndicator
id: default
typingState: default0
idleState: default3
- type: typingIndicator
id: robot
typingState: robot0
idleState: robot3
- type: typingIndicator
id: alien
typingState: alien0
idleState: alien3
- type: typingIndicator
id: guardian
typingState: guardian0
idleState: guardian3
- type: typingIndicator
id: holo
typingState: holo0
idleState: holo3
offset: 0, 0.0625
- type: typingIndicator
id: lawyer
typingState: lawyer0
idleState: lawyer3
offset: 0, 0.125
- type: typingIndicator
id: moth
typingState: moth0
idleState: moth3
offset: 0, 0.125
- type: typingIndicator
id: spider
typingState: spider0
idleState: spider3
offset: 0, 0.125
- type: typingIndicator
id: vox
typingState: vox0
idleState: vox0 # TODO add idle state sprite
offset: -0.125, 0.125
- type: typingIndicator
id: lizard
typingState: lizard0
idleState: lizard3
offset: 0, 0.0625
- type: typingIndicator
id: slime
typingState: slime0
idleState: slime3
offset: 0, 0.125
- type: typingIndicator
id: gingerbread
typingState: gingerbread0
idleState: gingerbread0
offset: 0, 0.125
- type: typingIndicator
id: diona
typingState: diona0
idleState: diona0
offset: 0, 0.125

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

View File

@@ -1,11 +1,11 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24 | Moth sprites made by PuroSlavKing (Github) | Spider sprites made by PixelTheKermit (Github) | Lizard sprites made by AmalgoMyte (Github) | Diona and Gingerbread sprites made by YoungThugSS14 (Github)",
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24 | Moth sprites made by PuroSlavKing (Github) | Spider sprites made by PixelTheKermit (Github) | Lizard sprites made by AmalgoMyte (Github) | Diona and Gingerbread sprites made by YoungThugSS14 (Github)",
"states": [
{
"name": "alien0",
@@ -26,9 +26,20 @@
{
"name": "alien2"
},
{
"name": "alien3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "alienroyal0",
"delays": [
[
0.2,
@@ -46,6 +57,19 @@
{
"name": "alienroyal2"
},
{
"name": "alienroyal3",
"delays": [
[
0.2,
0.3,
0.3,
0.3,
0.3,
0.5
]
]
},
{
"name": "blob0",
"delays": [
@@ -125,6 +149,18 @@
{
"name": "default2"
},
{
"name": "default3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "diona0",
"delays": [
@@ -176,6 +212,18 @@
{
"name": "guardian2"
},
{
"name": "guardian3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "holo0",
"delays": [
@@ -195,6 +243,18 @@
{
"name": "holo2"
},
{
"name": "holo3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "lawyer0",
"delays": [
@@ -232,6 +292,21 @@
]
]
},
{
"name": "lawyer3",
"delays": [
[
0.15,
0.15,
0.15,
0.15,
0.125,
0.1,
0.125,
0.15
]
]
},
{
"name": "lizard0",
"delays": [
@@ -249,6 +324,18 @@
{
"name": "lizard2"
},
{
"name": "lizard3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "moth0",
"delays": [
@@ -286,6 +373,27 @@
]
]
},
{
"name": "moth3",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "machine0",
"delays": [
@@ -320,6 +428,20 @@
{
"name": "robot2"
},
{
"name": "robot3",
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "slime0",
"delays": [
@@ -361,6 +483,18 @@
]
]
},
{
"name": "slime3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "swarmer0",
"delays": [
@@ -429,6 +563,20 @@
{
"name": "syndibot2"
},
{
"name": "syndibot3",
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "spider0",
"delays": [
@@ -446,6 +594,18 @@
{
"name": "spider2"
},
{
"name": "spider3",
"delays": [
[
0.2,
0.3,
0.3,
0.5,
0.5
]
]
},
{
"name": "vox0",
"delays": [

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B