Files

448 lines
15 KiB
C#
Raw Permalink Normal View History

2026-09-07 08:07:08 +08:00
using AntdUI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace JinYuan.ControlLib
{
public partial class CustomRichTextBox : RichTextBox
{
private Color darkBackColor = Color.Black;
private Color lightBackColor = Color.White;
private Color darkForeColor = Color.White;
private Color lightForeColor = Color.Black;
private Color darkBorderColor = Color.FromArgb(64, 64, 64);
private Color lightBorderColor = Color.FromArgb(217, 217, 217);
private bool isDarkTheme;
private const int BatchSize = 5000;
private bool isUpdatingTheme = false;
private bool isInitialized = false;
public CustomRichTextBox()
{
BorderStyle = BorderStyle.None;
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
// 在句柄创建后初始化主题
HandleCreated += CustomRichTextBox_HandleCreated;
}
private void CustomRichTextBox_HandleCreated(object sender, EventArgs e)
{
if (!isInitialized)
{
isInitialized = true;
InitializeThemeAsync();
}
}
private async void InitializeThemeAsync()
{
try
{
await UpdateThemeAsync(Config.IsDark);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Theme initialization failed: {ex.Message}");
}
}
public async Task UpdateThemeAsync(bool isDark)
{
if (isDarkTheme == isDark || isUpdatingTheme || !IsHandleCreated) return;
isUpdatingTheme = true;
isDarkTheme = isDark;
try
{
if (InvokeRequired)
{
await Task.Run(() =>
{
Invoke((MethodInvoker)(() => UpdateThemeInternal()));
});
}
else
{
UpdateThemeInternal();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Theme update failed: {ex.Message}");
}
finally
{
isUpdatingTheme = false;
}
}
private void UpdateThemeInternal()
{
if (!IsHandleCreated) return;
BackColor = isDarkTheme ? darkBackColor : lightBackColor;
ForeColor = isDarkTheme ? darkForeColor : lightForeColor;
SuspendLayout();
try
{
int visibleStart = GetCharIndexFromPosition(new Point(0, 0));
int visibleEnd = GetCharIndexFromPosition(new Point(ClientSize.Width, ClientSize.Height));
// Update visible text first
UpdateTextColors(visibleStart, visibleEnd - visibleStart);
// Then update the rest in batches
for (int i = 0; i < TextLength; i += BatchSize)
{
if (i >= visibleStart && i < visibleEnd) continue;
int length = Math.Min(BatchSize, TextLength - i);
UpdateTextColors(i, length);
if (i % (BatchSize * 10) == 0) Application.DoEvents();
}
}
finally
{
ResumeLayout();
}
Invalidate();
}
private void UpdateTextColors(int start, int length)
{
if (start < 0 || length <= 0 || start + length > TextLength) return;
Select(start, length);
SelectionColor = ForeColor;
SelectionBackColor = BackColor;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (var pen = new Pen(isDarkTheme ? darkBorderColor : lightBorderColor))
{
e.Graphics.DrawRectangle(pen, 0, 0, Width - 1, Height - 1);
}
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
if (!isUpdatingTheme && TextLength > 0 && IsHandleCreated)
{
int start = TextLength - 1;
int length = 1;
if (start < 0) start = 0;
if (start + length > TextLength) length = TextLength - start;
UpdateTextColors(start, length);
SelectionLength = 0;
SelectionStart = TextLength;
}
}
[Browsable(true)]
[Category("Appearance")]
[DefaultValue(typeof(Color), "30, 30, 30")]
public Color DarkBackColor
{
get => darkBackColor;
set
{
if (darkBackColor != value)
{
darkBackColor = value;
if (isDarkTheme && IsHandleCreated) SafeRefreshTheme();
}
}
}
[Browsable(true)]
[Category("Appearance")]
[DefaultValue(typeof(Color), "White")]
public Color LightBackColor
{
get => lightBackColor;
set
{
if (lightBackColor != value)
{
lightBackColor = value;
if (!isDarkTheme && IsHandleCreated) SafeRefreshTheme();
}
}
}
[Browsable(true)]
[Category("Appearance")]
[DefaultValue(typeof(Color), "White")]
public Color DarkForeColor
{
get => darkForeColor;
set
{
if (darkForeColor != value)
{
darkForeColor = value;
if (isDarkTheme && IsHandleCreated) SafeRefreshTheme();
}
}
}
[Browsable(true)]
[Category("Appearance")]
[DefaultValue(typeof(Color), "Black")]
public Color LightForeColor
{
get => lightForeColor;
set
{
if (lightForeColor != value)
{
lightForeColor = value;
if (!isDarkTheme && IsHandleCreated) SafeRefreshTheme();
}
}
}
[Browsable(true)]
[Category("Appearance")]
[DefaultValue(typeof(Color), "64, 64, 64")]
public Color DarkBorderColor
{
get => darkBorderColor;
set
{
if (darkBorderColor != value)
{
darkBorderColor = value;
if (isDarkTheme && IsHandleCreated) Invalidate();
}
}
}
[Browsable(true)]
[Category("Appearance")]
[DefaultValue(typeof(Color), "217, 217, 217")]
public Color LightBorderColor
{
get => lightBorderColor;
set
{
if (lightBorderColor != value)
{
lightBorderColor = value;
if (!isDarkTheme && IsHandleCreated) Invalidate();
}
}
}
private async void SafeRefreshTheme()
{
try
{
await RefreshThemeAsync();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Theme refresh failed: {ex.Message}");
}
}
public async Task RefreshThemeAsync()
{
if (!IsHandleCreated) return;
await UpdateThemeAsync(isDarkTheme);
}
}
}
//public partial class CustomRichTextBox : RichTextBox
//{
// private Color darkBackColor = Color.Black;
// private Color lightBackColor = Color.White;
// private Color darkForeColor = Color.White;
// private Color lightForeColor = Color.Black;
// private Color darkBorderColor = Color.FromArgb(64, 64, 64);
// private Color lightBorderColor = Color.FromArgb(217, 217, 217);
// private bool isDarkTheme;
// private const int BatchSize = 5000; // 每批处理的字符数
// private bool isUpdatingTheme = false;
// public CustomRichTextBox()
// {
// BorderStyle = BorderStyle.None;
// SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
// InitializeThemeAsync();
// }
// private async void InitializeThemeAsync()
// {
// await UpdateThemeAsync(Config.IsDark);
// }
// public async Task UpdateThemeAsync(bool isDark)
// {
// if (isDarkTheme == isDark || isUpdatingTheme) return;
// isUpdatingTheme = true;
// isDarkTheme = isDark;
// try
// {
// await Task.Run(async () =>
// {
// await InvokeAsync(() =>
// {
// BackColor = isDark ? darkBackColor : lightBackColor;
// ForeColor = isDark ? darkForeColor : lightForeColor;
// SuspendLayout();
// try
// {
// int visibleStart = GetCharIndexFromPosition(new Point(0, 0));
// int visibleEnd = GetCharIndexFromPosition(new Point(ClientSize.Width, ClientSize.Height));
// // Update visible text first
// UpdateTextColors(visibleStart, visibleEnd - visibleStart);
// // Then update the rest in batches
// for (int i = 0; i < TextLength; i += BatchSize)
// {
// if (i >= visibleStart && i < visibleEnd) continue; // Skip already updated visible part
// int length = Math.Min(BatchSize, TextLength - i);
// UpdateTextColors(i, length);
// if (i % (BatchSize * 10) == 0) Application.DoEvents();
// }
// }
// finally
// {
// ResumeLayout();
// }
// Invalidate();
// });
// });
// }
// finally
// {
// isUpdatingTheme = false;
// }
// }
// private void UpdateTextColors(int start, int length)
// {
// Select(start, length);
// SelectionColor = ForeColor;
// SelectionBackColor = BackColor;
// }
// protected override void OnPaint(PaintEventArgs e)
// {
// base.OnPaint(e);
// using (var pen = new Pen(isDarkTheme ? darkBorderColor : lightBorderColor))
// {
// e.Graphics.DrawRectangle(pen, 0, 0, Width - 1, Height - 1);
// }
// }
// protected override void OnTextChanged(EventArgs e)
// {
// base.OnTextChanged(e);
// if (!isUpdatingTheme && TextLength > 0)
// {
// int start = TextLength - 1;
// int length = 1;
// if (start < 0) start = 0;
// if (start + length > TextLength) length = TextLength - start;
// UpdateTextColors(start, length);
// SelectionLength = 0;
// SelectionStart = TextLength;
// }
// }
// [Browsable(true)]
// [Category("Appearance")]
// [DefaultValue(typeof(Color), "30, 30, 30")]
// public Color DarkBackColor
// {
// get => darkBackColor;
// set { darkBackColor = value; if (isDarkTheme) RefreshThemeAsync(); }
// }
// [Browsable(true)]
// [Category("Appearance")]
// [DefaultValue(typeof(Color), "White")]
// public Color LightBackColor
// {
// get => lightBackColor;
// set { lightBackColor = value; if (!isDarkTheme) RefreshThemeAsync(); }
// }
// [Browsable(true)]
// [Category("Appearance")]
// [DefaultValue(typeof(Color), "White")]
// public Color DarkForeColor
// {
// get => darkForeColor;
// set { darkForeColor = value; if (isDarkTheme) RefreshThemeAsync(); }
// }
// [Browsable(true)]
// [Category("Appearance")]
// [DefaultValue(typeof(Color), "Black")]
// public Color LightForeColor
// {
// get => lightForeColor;
// set { lightForeColor = value; if (!isDarkTheme) RefreshThemeAsync(); }
// }
// [Browsable(true)]
// [Category("Appearance")]
// [DefaultValue(typeof(Color), "64, 64, 64")]
// public Color DarkBorderColor
// {
// get => darkBorderColor;
// set { darkBorderColor = value; if (isDarkTheme) Invalidate(); }
// }
// [Browsable(true)]
// [Category("Appearance")]
// [DefaultValue(typeof(Color), "217, 217, 217")]
// public Color LightBorderColor
// {
// get => lightBorderColor;
// set { lightBorderColor = value; if (!isDarkTheme) Invalidate(); }
// }
// public async Task RefreshThemeAsync()
// {
// await UpdateThemeAsync(isDarkTheme);
// }
// private Task InvokeAsync(Action action)
// {
// return Task.Factory.FromAsync(BeginInvoke(action), EndInvoke);
// }
//}