mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 08:36:11 +02:00
feat: Implement chat line wrapping and viewport resizing for message display
This commit is contained in:
@@ -35,6 +35,58 @@ public partial class ChatLine
|
|||||||
|
|
||||||
public override string ToString() => string.Concat(Segments.Select(s => s.Text));
|
public override string ToString() => string.Concat(Segments.Select(s => s.Text));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrap this line into multiple lines that fit within the given width.
|
||||||
|
/// Continuation lines are indented with the specified number of spaces.
|
||||||
|
/// </summary>
|
||||||
|
public List<ChatLine> Wrap(int width, int continuationIndent = 0)
|
||||||
|
{
|
||||||
|
if (width <= 0 || TextLength <= width)
|
||||||
|
return [this];
|
||||||
|
|
||||||
|
var results = new List<ChatLine>();
|
||||||
|
var currentSegments = new List<ChatSegment>();
|
||||||
|
int col = 0;
|
||||||
|
|
||||||
|
foreach (var segment in Segments)
|
||||||
|
{
|
||||||
|
int segPos = 0;
|
||||||
|
while (segPos < segment.Text.Length)
|
||||||
|
{
|
||||||
|
int remaining = width - col;
|
||||||
|
if (remaining <= 0)
|
||||||
|
{
|
||||||
|
// Emit current line and start a new one
|
||||||
|
results.Add(new ChatLine(currentSegments));
|
||||||
|
currentSegments = [];
|
||||||
|
|
||||||
|
// Add indent for continuation
|
||||||
|
if (continuationIndent > 0)
|
||||||
|
{
|
||||||
|
currentSegments.Add(new ChatSegment(new string(' ', continuationIndent), null));
|
||||||
|
col = continuationIndent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
col = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining = width - col;
|
||||||
|
}
|
||||||
|
|
||||||
|
int take = Math.Min(segment.Text.Length - segPos, remaining);
|
||||||
|
currentSegments.Add(new ChatSegment(segment.Text.Substring(segPos, take), segment.Color));
|
||||||
|
col += take;
|
||||||
|
segPos += take;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSegments.Count > 0)
|
||||||
|
results.Add(new ChatLine(currentSegments));
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse a string containing ANSI 24-bit color escape codes into colored segments.
|
/// Parse a string containing ANSI 24-bit color escape codes into colored segments.
|
||||||
/// Format: \x1b[38;2;R;G;Bm (foreground color), \x1b[0m (reset)
|
/// Format: \x1b[38;2;R;G;Bm (foreground color), \x1b[0m (reset)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public sealed class MainWindow : Runnable
|
|||||||
private readonly Dictionary<string, string?> _channelTopics = [];
|
private readonly Dictionary<string, string?> _channelTopics = [];
|
||||||
private string _currentChannel = string.Empty;
|
private string _currentChannel = string.Empty;
|
||||||
private string _currentUser = string.Empty;
|
private string _currentUser = string.Empty;
|
||||||
|
private int _lastChatWidth;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the user selects a channel. Parameter is the channel name.
|
/// Fired when the user selects a channel. Parameter is the channel name.
|
||||||
@@ -192,6 +193,9 @@ public sealed class MainWindow : Runnable
|
|||||||
// Apply our custom color schemes to all views
|
// Apply our custom color schemes to all views
|
||||||
ApplyColorSchemes();
|
ApplyColorSchemes();
|
||||||
|
|
||||||
|
// Re-wrap messages when the chat area is resized
|
||||||
|
_messageList.ViewportChanged += (_, _) => OnChatViewportChanged();
|
||||||
|
|
||||||
// Window-level key handling for Ctrl+C (quit)
|
// Window-level key handling for Ctrl+C (quit)
|
||||||
KeyDown += OnWindowKeyDown;
|
KeyDown += OnWindowKeyDown;
|
||||||
}
|
}
|
||||||
@@ -368,6 +372,16 @@ public sealed class MainWindow : Runnable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnChatViewportChanged()
|
||||||
|
{
|
||||||
|
var newWidth = _messageList.Viewport.Width;
|
||||||
|
if (newWidth > 0 && newWidth != _lastChatWidth)
|
||||||
|
{
|
||||||
|
_lastChatWidth = newWidth;
|
||||||
|
RefreshMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnWindowKeyDown(object? sender, Key e)
|
private void OnWindowKeyDown(object? sender, Key e)
|
||||||
{
|
{
|
||||||
// Ctrl+C quits from anywhere
|
// Ctrl+C quits from anywhere
|
||||||
@@ -588,11 +602,22 @@ public sealed class MainWindow : Runnable
|
|||||||
{
|
{
|
||||||
if (_channelMessages.TryGetValue(_currentChannel, out var messages))
|
if (_channelMessages.TryGetValue(_currentChannel, out var messages))
|
||||||
{
|
{
|
||||||
|
var width = _messageList.Viewport.Width;
|
||||||
var source = new ChatListSource();
|
var source = new ChatListSource();
|
||||||
|
|
||||||
|
if (width > 0)
|
||||||
|
{
|
||||||
|
foreach (var line in messages)
|
||||||
|
source.AddRange(line.Wrap(width));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
source.AddRange(messages);
|
source.AddRange(messages);
|
||||||
|
}
|
||||||
|
|
||||||
_messageList.Source = source;
|
_messageList.Source = source;
|
||||||
if (messages.Count > 0)
|
if (source.Count > 0)
|
||||||
_messageList.SelectedItem = messages.Count - 1;
|
_messageList.SelectedItem = source.Count - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -146,7 +146,8 @@ public sealed class UserPanelDialog
|
|||||||
Width = Dim.Fill(1),
|
Width = Dim.Fill(1),
|
||||||
Height = 3,
|
Height = 3,
|
||||||
Text = bioText,
|
Text = bioText,
|
||||||
ReadOnly = true
|
ReadOnly = true,
|
||||||
|
WordWrap = true
|
||||||
};
|
};
|
||||||
bioView.SetScheme(new Scheme
|
bioView.SetScheme(new Scheme
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user