diff --git a/src/Consolonia.Core/Dummy/DummyConsole.cs b/src/Consolonia.Core/Dummy/DummyConsole.cs index f3fc6efc..883fc56f 100644 --- a/src/Consolonia.Core/Dummy/DummyConsole.cs +++ b/src/Consolonia.Core/Dummy/DummyConsole.cs @@ -34,6 +34,8 @@ public bool CaretVisible public bool SupportsComplexEmoji => true; public bool SupportsAltSolo => false; + public bool SupportsMouse => false; + public bool SupportsMouseMove => false; public void ClearOutput() { diff --git a/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs index 3437cd57..ffffbc18 100644 --- a/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs @@ -48,6 +48,8 @@ public DefaultNetConsole() } public override bool SupportsAltSolo => false; + public override bool SupportsMouse => false; + public override bool SupportsMouseMove => false; protected override void Dispose(bool disposing) { diff --git a/src/Consolonia.Core/Infrastructure/IConsole.cs b/src/Consolonia.Core/Infrastructure/IConsole.cs index d4975340..d9f94b3b 100644 --- a/src/Consolonia.Core/Infrastructure/IConsole.cs +++ b/src/Consolonia.Core/Infrastructure/IConsole.cs @@ -22,6 +22,8 @@ public interface IConsole : IDisposable bool SupportsComplexEmoji { get; } bool SupportsAltSolo { get; } + bool SupportsMouse { get; } + bool SupportsMouseMove { get; } void SetTitle(string title); diff --git a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs index dff6c716..d5c39dab 100644 --- a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs @@ -37,6 +37,8 @@ protected InputLessDefaultNetConsole() public bool SupportsComplexEmoji => _supportEmoji ?? false; public abstract bool SupportsAltSolo { get; } + public abstract bool SupportsMouse { get; } + public abstract bool SupportsMouseMove { get; } public void SetTitle(string title) { diff --git a/src/Consolonia.Core/InternalHelpers/CommonInternalHelper.cs b/src/Consolonia.Core/InternalHelpers/CommonInternalHelper.cs index dc15318f..3a0658f3 100644 --- a/src/Consolonia.Core/InternalHelpers/CommonInternalHelper.cs +++ b/src/Consolonia.Core/InternalHelpers/CommonInternalHelper.cs @@ -3,7 +3,7 @@ namespace Consolonia.Core.InternalHelpers { - internal static class CommonInternalHelper + public static class CommonInternalHelper { public static bool IsNearlyEqual(this double value, double compareTo) { diff --git a/src/Consolonia.NUnit/UnitTestConsole.cs b/src/Consolonia.NUnit/UnitTestConsole.cs index 66017930..58566d59 100644 --- a/src/Consolonia.NUnit/UnitTestConsole.cs +++ b/src/Consolonia.NUnit/UnitTestConsole.cs @@ -40,6 +40,8 @@ public void Dispose() public bool SupportsComplexEmoji => true; public bool SupportsAltSolo => true; + public bool SupportsMouse => false; + public bool SupportsMouseMove => false; public void SetTitle(string title) { diff --git a/src/Consolonia.PlatformSupport/CursesConsole.cs b/src/Consolonia.PlatformSupport/CursesConsole.cs index bd732f99..78c8fcfc 100644 --- a/src/Consolonia.PlatformSupport/CursesConsole.cs +++ b/src/Consolonia.PlatformSupport/CursesConsole.cs @@ -116,6 +116,8 @@ public CursesConsole() } public override bool SupportsAltSolo => false; + public override bool SupportsMouse => true; + public override bool SupportsMouseMove => false; private void StartEventLoop() { diff --git a/src/Consolonia.PlatformSupport/WindowsConsole.cs b/src/Consolonia.PlatformSupport/WindowsConsole.cs index 596e37db..283756b1 100644 --- a/src/Consolonia.PlatformSupport/WindowsConsole.cs +++ b/src/Consolonia.PlatformSupport/WindowsConsole.cs @@ -62,6 +62,8 @@ public Win32Console() } public override bool SupportsAltSolo => true; + public override bool SupportsMouse => true; + public override bool SupportsMouseMove => true; public override void PauseIO(Task task) { diff --git a/src/Consolonia.Themes/Templates/Controls/Helpers/SelectTextWithPointerUpExtension.cs b/src/Consolonia.Themes/Templates/Controls/Helpers/SelectTextWithPointerUpExtension.cs new file mode 100644 index 00000000..59fd6b35 --- /dev/null +++ b/src/Consolonia.Themes/Templates/Controls/Helpers/SelectTextWithPointerUpExtension.cs @@ -0,0 +1,57 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.Utilities; +using Consolonia.Core.Helpers; +using Consolonia.Core.Infrastructure; +using Consolonia.Core.InternalHelpers; + +namespace Consolonia.Themes.Templates.Controls.Helpers +{ + internal static class SelectTextWithPointerUpExtension + { + public static readonly AttachedProperty SelectOnMouseRightUpProperty = + AvaloniaProperty.RegisterAttached(CommonInternalHelper.GetStyledPropertyName(), + typeof(SelectTextWithPointerUpExtension)); + + static SelectTextWithPointerUpExtension() + { + var console = AvaloniaLocator.Current.GetService(); + bool supportsMouse = console.SupportsMouse; + bool supportsMoveMove = console.SupportsMouseMove; + if (!supportsMouse || supportsMoveMove) + return; + + SelectOnMouseRightUpProperty.Changed.SubscribeAction(OnPropertyChanged); + } + + private static void OnPropertyChanged(AvaloniaPropertyChangedEventArgs args) + { + if (args.Sender is not SelectableTextBlock selectableTextBlock) return; + + selectableTextBlock.PointerReleased -= OnPointerReleased; + + if (args.GetNewValue()) selectableTextBlock.PointerReleased += OnPointerReleased; + } + + private static void OnPointerReleased(object sender, PointerReleasedEventArgs e) + { + // simplified copy of SelectableTextBlock.PointerMove + var tb = (SelectableTextBlock)sender; + + Thickness padding = tb.Padding; + + Point point = e.GetPosition(tb) - new Point(padding.Left, padding.Top); + + point = new Point( + MathUtilities.Clamp(point.X, 0, Math.Max(tb.TextLayout.WidthIncludingTrailingWhitespace, 0)), + MathUtilities.Clamp(point.Y, 0, Math.Max(tb.TextLayout.Height, 0))); + + TextHitTestResult hit = tb.TextLayout.HitTestPoint(point); + int textPosition = hit.TextPosition; + tb.SetCurrentValue(SelectableTextBlock.SelectionEndProperty, textPosition); + } + } +} \ No newline at end of file diff --git a/src/Consolonia.Themes/Templates/Controls/SelectableTextBlock.axaml b/src/Consolonia.Themes/Templates/Controls/SelectableTextBlock.axaml index 7e9ce71b..15a74edb 100644 --- a/src/Consolonia.Themes/Templates/Controls/SelectableTextBlock.axaml +++ b/src/Consolonia.Themes/Templates/Controls/SelectableTextBlock.axaml @@ -1,7 +1,11 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:helpers="clr-namespace:Consolonia.Themes.Templates.Controls.Helpers"> - + + \ No newline at end of file