mirror of
https://github.com/nsnail/dot.git
synced 2025-06-17 21:13:21 +08:00
..
This commit is contained in:
parent
a2eb6caf35
commit
fe181ca914
1
dot.sln
1
dot.sln
@ -18,7 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{AD79881E-7
|
||||
CodeQuality.props = CodeQuality.props
|
||||
Directory.Build.props = Directory.Build.props
|
||||
dot.sln.DotSettings = dot.sln.DotSettings
|
||||
dot.sln.DotSettings.user = dot.sln.DotSettings.user
|
||||
dotnet-tools.json = dotnet-tools.json
|
||||
GenerateResx.targets = GenerateResx.targets
|
||||
git-clean.cmd = git-clean.cmd
|
||||
|
@ -1,4 +1,8 @@
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ReSpeller/ReSpellerEnabled/@EntryValue">False</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using NSExt.Extensions;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
global using System.ComponentModel;
|
||||
global using System.Globalization;
|
||||
global using Dot.Lang;
|
||||
global using Spectre.Console;
|
||||
global using Spectre.Console.Cli;
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
using System.Globalization;
|
||||
#if NET7_0_WINDOWS
|
||||
using TextCopy;
|
||||
#endif
|
||||
|
@ -264,4 +264,7 @@
|
||||
<data name="BufferSize" xml:space="preserve">
|
||||
<value>缓冲区大小(千字节)</value>
|
||||
</data>
|
||||
<data name="Translating" xml:space="preserve">
|
||||
<value>翻译中...</value>
|
||||
</data>
|
||||
</root>
|
@ -1,5 +1,8 @@
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
|
||||
global using Spectre.Console;
|
||||
global using Spectre.Console.Cli;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
internal abstract class OptionBase : CommandSettings, IOption
|
||||
|
@ -1,4 +1,3 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Dot.Git;
|
||||
#if NET7_0_WINDOWS
|
||||
@ -7,7 +6,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dot;
|
||||
|
||||
internal sealed class Program
|
||||
internal static class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
@ -21,6 +20,7 @@ internal sealed class Program
|
||||
#if NET7_0_WINDOWS
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
|
||||
config.AddCommand<Color.Main>(nameof(Color).ToLower(CultureInfo.InvariantCulture));
|
||||
config.AddCommand<Tran.Main>(nameof(Tran).ToLower(CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endif
|
||||
config.AddCommand<Guid.Main>(nameof(Guid).ToLower(CultureInfo.InvariantCulture));
|
||||
|
@ -1,6 +1,5 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Dot.Time;
|
||||
|
233
src/Tran/FrmMain.cs
Normal file
233
src/Tran/FrmMain.cs
Normal file
@ -0,0 +1,233 @@
|
||||
#if NET7_0_WINDOWS
|
||||
using NSExt.Extensions;
|
||||
using Colors = System.Drawing.Color;
|
||||
using Padding = System.Windows.Forms.Padding;
|
||||
using Size = System.Drawing.Size;
|
||||
|
||||
namespace Dot.Tran;
|
||||
|
||||
internal sealed class FrmMain : Form
|
||||
{
|
||||
private const int _HIDING_MIL_SECS = 1000; // 鼠标离开超过此时间后隐藏窗体
|
||||
private const string _TOKEN_URL = "https://fanyi.qq.com/api/reauth12f";
|
||||
private const string _URL = "https://fanyi.qq.com/api/translate";
|
||||
private static ManualResetEvent _mre = new(false); // 隐藏窗体侦测线程信号
|
||||
|
||||
private readonly Colors _bgColor // 背景颜色
|
||||
= Colors.FromArgb(0xff, 0x1e, 0x1e, 0x1e);
|
||||
|
||||
private readonly Colors _foreColor = Colors.White; // 字体颜色
|
||||
|
||||
private readonly HttpClient _httpClient = new();
|
||||
private readonly RichTextBox _richTextSource = new(); // 显示剪贴板内容的富文本框
|
||||
private readonly TextBox _textDest = new(); // 显示翻译内容的文本框
|
||||
private readonly Padding _windowPadding = new(10, 10, 10, 10); // 窗体边距
|
||||
private readonly Size _windowSize = new(640, 360); // 窗体大小
|
||||
private DateTime? _mouseLeaveTime; // 鼠标离开窗体时间
|
||||
private nint _nextHwnd; // 下一个剪贴板监视链对象句柄
|
||||
|
||||
public FrmMain()
|
||||
{
|
||||
InitForm();
|
||||
|
||||
InitTextSource();
|
||||
InitTextDest();
|
||||
|
||||
_nextHwnd = Win32.SetClipboardViewer(Handle);
|
||||
|
||||
Task.Run(HideWindow);
|
||||
}
|
||||
|
||||
~FrmMain()
|
||||
{
|
||||
_mre?.Dispose();
|
||||
_richTextSource?.Dispose();
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
|
||||
// 结束定时隐藏窗体线程
|
||||
_mre = null;
|
||||
|
||||
// 从剪贴板监视链移除本窗体
|
||||
Win32.ChangeClipboardChain(Handle, _nextHwnd);
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
base.OnMouseEnter(e);
|
||||
_mouseLeaveTime = null;
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
if (ClientRectangle.Contains(PointToClient(MousePosition))) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mouseLeaveTime = DateTime.Now;
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
void SendToNext(Message message)
|
||||
{
|
||||
_ = Win32.SendMessageA(_nextHwnd, (uint)message.Msg, message.WParam, message.LParam);
|
||||
}
|
||||
|
||||
switch (m.Msg) {
|
||||
case Win32.WM_DRAWCLIPBOARD:
|
||||
DisplayClipboardData();
|
||||
SendToNext(m);
|
||||
break;
|
||||
case Win32.WM_CHANGECBCHAIN:
|
||||
if (m.WParam == _nextHwnd) {
|
||||
_nextHwnd = m.LParam;
|
||||
}
|
||||
else {
|
||||
SendToNext(m);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
base.WndProc(ref m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示剪贴板内容.
|
||||
/// </summary>
|
||||
private void DisplayClipboardData()
|
||||
{
|
||||
var clipData = Clipboard.GetDataObject();
|
||||
if (clipData is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (clipData.GetDataPresent(DataFormats.Rtf)) {
|
||||
_richTextSource.Rtf = clipData.GetData(DataFormats.Rtf) as string;
|
||||
}
|
||||
else if (clipData.GetDataPresent(DataFormats.Text)) {
|
||||
_richTextSource.Text = clipData.GetData(DataFormats.Text) as string;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
var mousePos = Cursor.Position;
|
||||
mousePos.Offset(-_windowPadding.Left, -_windowPadding.Top);
|
||||
Location = mousePos;
|
||||
Visible = true;
|
||||
_mouseLeaveTime = null;
|
||||
_mre.Set();
|
||||
}
|
||||
|
||||
private TokenStruct GetTranslateToken()
|
||||
{
|
||||
using var rsp = _httpClient.PostAsync(_TOKEN_URL, new StringContent(string.Empty)).Result;
|
||||
var rspStr = rsp.Content.ReadAsStringAsync().Result;
|
||||
return rspStr.Object<TokenStruct>();
|
||||
}
|
||||
|
||||
private int HalfHeight()
|
||||
{
|
||||
return (_windowSize.Height - _windowPadding.Top - _windowPadding.Bottom) / 2 - _windowPadding.Top / 2;
|
||||
}
|
||||
|
||||
private async Task HideWindow()
|
||||
{
|
||||
while (_mre is not null) {
|
||||
while (Visible) {
|
||||
await Task.Delay(100);
|
||||
if (_mouseLeaveTime is null ||
|
||||
!((DateTime.Now - _mouseLeaveTime.Value).TotalMilliseconds > _HIDING_MIL_SECS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Invoke(() => {
|
||||
Visible = false;
|
||||
_richTextSource.Clear();
|
||||
_textDest.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
_mre.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitForm()
|
||||
{
|
||||
BackColor = _bgColor;
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
Padding = _windowPadding;
|
||||
Size = _windowSize;
|
||||
TopMost = true;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
private void InitTextDest()
|
||||
{
|
||||
_textDest.BackColor = _bgColor;
|
||||
_textDest.BorderStyle = BorderStyle.None;
|
||||
_textDest.Dock = DockStyle.Bottom;
|
||||
_textDest.ForeColor = _foreColor;
|
||||
_textDest.Height = HalfHeight();
|
||||
_textDest.Margin = new Padding(0, _windowPadding.Top / 2, 0, 0);
|
||||
_textDest.MouseEnter += (_, e) => OnMouseEnter(e);
|
||||
_textDest.Multiline = true;
|
||||
_textDest.ScrollBars = ScrollBars.None;
|
||||
Controls.Add(_textDest);
|
||||
}
|
||||
|
||||
private void InitTextSource()
|
||||
{
|
||||
_richTextSource.TextChanged += (sender, e) => {
|
||||
if (_richTextSource.Text.NullOrWhiteSpace()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_textDest.Text = Str.Translating;
|
||||
_textDest.Text = TranslateText(_richTextSource.Text);
|
||||
};
|
||||
_richTextSource.BackColor = _bgColor;
|
||||
_richTextSource.BorderStyle = BorderStyle.None;
|
||||
_richTextSource.Dock = DockStyle.Top;
|
||||
_richTextSource.ForeColor = _foreColor;
|
||||
_richTextSource.Height = HalfHeight();
|
||||
_richTextSource.Margin = new Padding(0, 0, 0, _windowPadding.Top / 2);
|
||||
_richTextSource.MouseEnter += (_, e) => OnMouseEnter(e);
|
||||
_richTextSource.ScrollBars = RichTextBoxScrollBars.None;
|
||||
Controls.Add(_richTextSource);
|
||||
}
|
||||
|
||||
private string TranslateText(string sourceText)
|
||||
{
|
||||
var token = GetTranslateToken();
|
||||
var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>> {
|
||||
new("source", "auto")
|
||||
, new("target", "zh")
|
||||
, new("qtv", token.Qtv.Url())
|
||||
, new("qtk", token.Qtk.Url())
|
||||
, new("sourceText", sourceText.Url())
|
||||
});
|
||||
var rsp = _httpClient.PostAsync(_URL, content).Result;
|
||||
var ret = rsp.Content.ReadAsStringAsync().Result;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private readonly struct TokenStruct
|
||||
{
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
||||
public string Qtk { get; init; }
|
||||
|
||||
public string Qtv { get; init; }
|
||||
|
||||
// ReSharper restore UnusedAutoPropertyAccessor.Local
|
||||
}
|
||||
}
|
||||
#endif
|
20
src/Tran/Main.cs
Normal file
20
src/Tran/Main.cs
Normal file
@ -0,0 +1,20 @@
|
||||
#if NET7_0_WINDOWS
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Dot.Tran;
|
||||
|
||||
internal sealed class Main : ToolBase<Option>
|
||||
{
|
||||
[SupportedOSPlatform(nameof(OSPlatform.Windows))]
|
||||
protected override Task Core()
|
||||
{
|
||||
var th = new Thread(() => { Application.Run(new FrmMain()); });
|
||||
th.SetApartmentState(ApartmentState.STA);
|
||||
th.Start();
|
||||
th.Join();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
3
src/Tran/Option.cs
Normal file
3
src/Tran/Option.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace Dot.Tran;
|
||||
|
||||
internal sealed class Option : OptionBase { }
|
12
src/Win32.cs
12
src/Win32.cs
@ -8,6 +8,8 @@ namespace Dot;
|
||||
internal static partial class Win32
|
||||
{
|
||||
public const int SW_HIDE = 0;
|
||||
public const int WM_CHANGECBCHAIN = 0x030D;
|
||||
public const int WM_DRAWCLIPBOARD = 0x308;
|
||||
|
||||
private const string _GDI32_DLL = "gdi32.dll";
|
||||
private const string _KERNEL32_DLL = "kernel32.dll";
|
||||
@ -16,6 +18,10 @@ internal static partial class Win32
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial nint CallNextHookEx(nint hhk, int nCode, nint wParam, nint lParam);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool ChangeClipboardChain(nint hWndRemove, nint hWndNewNext);
|
||||
|
||||
[LibraryImport(_KERNEL32_DLL)]
|
||||
internal static partial nint GetConsoleWindow();
|
||||
|
||||
@ -34,6 +40,12 @@ internal static partial class Win32
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int ReleaseDC(nint hWnd, nint dc);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int SendMessageA(nint hwnd, uint wMsg, nint wParam, nint lParam);
|
||||
|
||||
[LibraryImport(_USER32_DLL)]
|
||||
internal static partial int SetClipboardViewer(nint hWnd);
|
||||
|
||||
[LibraryImport(_KERNEL32_DLL)]
|
||||
internal static partial void SetLocalTime(Systemtime st);
|
||||
|
||||
|
@ -13,10 +13,10 @@
|
||||
<DefineConstants>$(DefineConstants);NET7_0_WINDOWS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NSExt" Version="1.0.9-alpha.0.1"/>
|
||||
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.48"/>
|
||||
<PackageReference Include="Spectre.Console.NS" Version="0.45.1-preview.0.48"/>
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net7.0-windows'" Include="TextCopy" Version="6.2.0"/>
|
||||
<PackageReference Include="NSExt" Version="1.0.9-alpha.0.1" />
|
||||
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.48" />
|
||||
<PackageReference Include="Spectre.Console.NS" Version="0.45.1-preview.0.48" />
|
||||
<PackageReference Condition="'$(TargetFramework)' == 'net7.0-windows'" Include="TextCopy" Version="6.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Lang\Str.resx">
|
||||
@ -24,6 +24,6 @@
|
||||
<LastGenOutput>Str.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="../CodeQuality.props"/>
|
||||
<Import Project="../GenerateResx.targets"/>
|
||||
<Import Project="../CodeQuality.props" />
|
||||
<Import Project="../GenerateResx.targets" />
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user