Make messages selectable

This commit is contained in:
Stone_Red
2026-08-03 00:42:07 +02:00
parent b712ca6cc9
commit a4c245dbdc
5 changed files with 71 additions and 70 deletions
+1 -1
View File
@@ -33,4 +33,4 @@ public sealed class BoolToOpacityConverter : IValueConverter
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
@@ -202,6 +202,11 @@ public sealed class MessageComposerViewModel : ViewModelBase
ReplyTarget = null; ReplyTarget = null;
} }
public void UpdateAutocomplete(string text, int caretIndex)
{
Autocomplete.Update(text, caretIndex);
}
internal void Send() internal void Send()
{ {
string text = Draft.Trim(); string text = Draft.Trim();
@@ -230,9 +235,4 @@ public sealed class MessageComposerViewModel : ViewModelBase
? $"{StagedFiles.Count} file(s) staged" ? $"{StagedFiles.Count} file(s) staged"
: string.Empty; : string.Empty;
} }
public void UpdateAutocomplete(string text, int caretIndex)
{
Autocomplete.Update(text, caretIndex);
}
} }
+4 -4
View File
@@ -50,10 +50,10 @@
Opacity="0.5" Opacity="0.5"
IsVisible="{Binding HasReply}" IsVisible="{Binding HasReply}"
PointerPressed="OnReplyQuotePointerPressed" /> PointerPressed="OnReplyQuotePointerPressed" />
<TextBlock x:Name="MessageContent" <SelectableTextBlock x:Name="MessageContent"
VerticalAlignment="Top" VerticalAlignment="Top"
TextWrapping="Wrap" TextWrapping="Wrap"
IsVisible="{Binding ShowContent}" /> IsVisible="{Binding ShowContent}" />
<ItemsControl ItemsSource="{Binding Attachments}" <ItemsControl ItemsSource="{Binding Attachments}"
IsVisible="{Binding HasAttachments}" IsVisible="{Binding HasAttachments}"
Margin="0 4 0 0"> Margin="0 4 0 0">
+1 -1
View File
@@ -117,7 +117,7 @@ public partial class MessageItemView : UserControl
private void BuildMessageInlines(string content) private void BuildMessageInlines(string content)
{ {
TextBlock? tb = MessageContent; SelectableTextBlock? tb = MessageContent;
if (tb is null) if (tb is null)
{ {
return; return;
+60 -59
View File
@@ -17,12 +17,72 @@ public partial class MessageListView : UserControl
private bool _loadingMore; private bool _loadingMore;
private double _loadingMoreExtent; private double _loadingMoreExtent;
private MessageViewModel? _highlightedMessage;
private CancellationTokenSource? _highlightCts;
public MessageListView() public MessageListView()
{ {
InitializeComponent(); InitializeComponent();
DataContextChanged += OnDataContextChanged; DataContextChanged += OnDataContextChanged;
} }
public void ScrollToMessage(string messageId)
{
if (_chat is null) return;
MessageViewModel? target = _chat.Messages.FirstOrDefault(m => m.Model.Id == messageId);
if (target is null) return;
ScrollViewer? scroll = this.FindControl<ScrollViewer>("MessageScrollViewer");
if (scroll is null) return;
ClearHighlight();
target.IsHighlighted = true;
_highlightedMessage = target;
_highlightCts = new CancellationTokenSource();
CancellationToken ct = _highlightCts.Token;
Dispatcher.UIThread.Post(() =>
{
double? targetY = FindItemY(scroll, target);
if (targetY is not null)
scroll.Offset = new Vector(scroll.Offset.X, targetY.Value);
}, DispatcherPriority.Render);
_ = Task.Run(async () =>
{
try
{
await Task.Delay(2000, ct);
Dispatcher.UIThread.Post(() =>
{
target.IsHighlighted = false;
if (_highlightedMessage == target)
_highlightedMessage = null;
});
}
catch (OperationCanceledException)
{
}
});
}
private static double? FindItemY(Visual parent, MessageViewModel target)
{
foreach (Visual child in parent.GetVisualChildren())
{
if (child.DataContext == target)
return child.Bounds.Y;
double? result = FindItemY(child, target);
if (result.HasValue)
return result;
}
return null;
}
private void OnDataContextChanged(object? sender, EventArgs args) private void OnDataContextChanged(object? sender, EventArgs args)
{ {
if (_chat is not null) if (_chat is not null)
@@ -142,65 +202,6 @@ public partial class MessageListView : UserControl
Dispatcher.UIThread.Post(scroll.ScrollToEnd, DispatcherPriority.Background); Dispatcher.UIThread.Post(scroll.ScrollToEnd, DispatcherPriority.Background);
} }
public void ScrollToMessage(string messageId)
{
if (_chat is null) return;
MessageViewModel? target = _chat.Messages.FirstOrDefault(m => m.Model.Id == messageId);
if (target is null) return;
ScrollViewer? scroll = this.FindControl<ScrollViewer>("MessageScrollViewer");
if (scroll is null) return;
ClearHighlight();
target.IsHighlighted = true;
_highlightedMessage = target;
_highlightCts = new CancellationTokenSource();
CancellationToken ct = _highlightCts.Token;
Dispatcher.UIThread.Post(() =>
{
double? targetY = FindItemY(scroll, target);
if (targetY is not null)
scroll.Offset = new Vector(scroll.Offset.X, targetY.Value);
}, DispatcherPriority.Render);
_ = Task.Run(async () =>
{
try
{
await Task.Delay(2000, ct);
Dispatcher.UIThread.Post(() =>
{
target.IsHighlighted = false;
if (_highlightedMessage == target)
_highlightedMessage = null;
});
}
catch (OperationCanceledException)
{
}
});
}
private static double? FindItemY(Visual parent, MessageViewModel target)
{
foreach (Visual child in parent.GetVisualChildren())
{
if (child.DataContext == target)
return child.Bounds.Y;
double? result = FindItemY(child, target);
if (result.HasValue)
return result;
}
return null;
}
private MessageViewModel? _highlightedMessage;
private CancellationTokenSource? _highlightCts;
private void ClearHighlight() private void ClearHighlight()
{ {
_highlightCts?.Cancel(); _highlightCts?.Cancel();