Files
SbLauncher/tools/RenderLightThemes.cs
T
2026-10-10 14:58:04 +03:00

186 lines
11 KiB
C#

// Preview-only renderer. Uses real WPF controls and synthetic data; no app settings are loaded.
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using SbLauncher;
public static class RenderLightThemes
{
sealed class Variant
{
public string Id, Title, Description;
public string[] Background;
public string Colors;
public Variant(string id, string title, string description, string[] background, string colors)
{ Id = id; Title = title; Description = description; Background = background; Colors = colors; }
}
static readonly Variant[] Variants = {
new Variant("01-silver", "01 Серебро", "Нейтральный фон · графитовый акцент",
new[] { "#F3F4F6", "#E8EBEF", "#EFF1F4" },
"Panel=#EBF8F9FB;Card=#FFFFFFFF;CardHover=#FFF7F9FC;CardSel=#FFE3EAF3;Stroke=#FFCDD3DC;GlassEdge=#FFD5DAE2;Text=#242B35;Muted=#606C7B;Accent=#405B80;AccentHover=#4E6D96;AccentDim=#344C6E;OnAccent=#FFFFFF;SoftBg=#FFE9EDF2;SoftHover=#FFF6F8FB;SwitchTrack=#C5CDD7;SwitchThumb=#FFFFFF;BadgeBg=#E0E6EE;PingBg=#EDF1F5;LogBg=#F8F9FB;LogText=#49586B;ScrollTrack=#18758499;ScrollThumb=#8895A6;ScrollThumbHover=#687A90;HeroRing=#905F7591;HeroRingDim=#407D8FA6;GlowA=#127689A2;GlowB=#108398B4"),
new Variant("02-frost", "02 Лёд", "Холодное стекло · синий акцент",
new[] { "#EDF5FF", "#DDEAF9", "#EAF0FD" },
"Panel=#D9F9FCFF;Card=#EFFFFFFF;CardHover=#FFFFFFFF;CardSel=#FFDCEAFF;Stroke=#FFC4D3E6;GlassEdge=#FFE8F0FA;Text=#1F3351;Muted=#536A86;Accent=#2E6CCC;AccentHover=#3A7CE1;AccentDim=#245ABD;OnAccent=#FFFFFF;SoftBg=#DBF7FBFF;SoftHover=#FFFFFFFF;SwitchTrack=#A9BED8;SwitchThumb=#FFFFFF;BadgeBg=#DCE8F8;PingBg=#F4F8FE;LogBg=#EAF9FCFF;LogText=#405E82;ScrollTrack=#207A9BCA;ScrollThumb=#829CBD;ScrollThumbHover=#577CA8;HeroRing=#AC6593D3;HeroRingDim=#406C99D8;GlowA=#387FAFEE;GlowB=#247F91D6"),
new Variant("03-linen", "03 Лён", "Тёплый молочный фон · бронзовый акцент",
new[] { "#F7F3EC", "#EDE5D9", "#F5EFE6" },
"Panel=#EDFBF8F2;Card=#FFFFFDFA;CardHover=#FFFFFFFF;CardSel=#FFECE0CF;Stroke=#FFD9CEBE;GlassEdge=#FFE4DACD;Text=#352F28;Muted=#746858;Accent=#89603B;AccentHover=#9A7049;AccentDim=#714D2C;OnAccent=#FFFFFF;SoftBg=#FFF0E9DF;SoftHover=#FFFFFCF7;SwitchTrack=#CBBEAC;SwitchThumb=#FFFFFF;BadgeBg=#EDE2D2;PingBg=#F5F0E8;LogBg=#FCFAF5;LogText=#6B5A46;ScrollTrack=#209A866C;ScrollThumb=#A69680;ScrollThumbHover=#857359;HeroRing=#9CA18765;HeroRingDim=#40AE9370;GlowA=#209E875F;GlowB=#16C8AE80"),
new Variant("04-sage", "04 Шалфей", "Серо-зелёный фон · хвойный акцент",
new[] { "#F0F5F1", "#DFE9E2", "#EBF1E9" },
"Panel=#E8F7FBF7;Card=#FFFDFEFC;CardHover=#FFFFFFFF;CardSel=#FFDDECE2;Stroke=#FFC5D4CB;GlassEdge=#FFD5E1D7;Text=#25372F;Muted=#5C7064;Accent=#37715B;AccentHover=#46846C;AccentDim=#295C49;OnAccent=#FFFFFF;SoftBg=#FFE5EEE7;SoftHover=#FFF6FCF7;SwitchTrack=#B5C9BC;SwitchThumb=#FFFFFF;BadgeBg=#DCE9DF;PingBg=#EDF4EE;LogBg=#F7FBF7;LogText=#486757;ScrollTrack=#20829A8A;ScrollThumb=#869E8E;ScrollThumbHover=#608570;HeroRing=#9C729B83;HeroRingDim=#407FA78D;GlowA=#207DAF93;GlowB=#2084A26C")
};
[STAThread]
public static int Main(string[] args)
{
try
{
string source = Path.GetFullPath(args[0]);
string output = Path.GetFullPath(args[1]);
Directory.CreateDirectory(output);
var app = new Application { ShutdownMode = ShutdownMode.OnExplicitShutdown };
var screenshots = new BitmapSource[Variants.Length];
for (int i = 0; i < Variants.Length; i++)
{
Variant variant = Variants[i];
var window = (Window)XamlReader.Parse(File.ReadAllText(Path.Combine(source, "Ui.xaml")));
var palette = (ResourceDictionary)window.Resources["Theme.Light"];
var background = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(1, 1) };
for (int j = 0; j < 3; j++) background.GradientStops.Add(new GradientStop(Color(variant.Background[j]), j * 0.5));
palette["Bg"] = background;
foreach (string token in variant.Colors.Split(';'))
{
string[] pair = token.Split('=');
if (!palette.Contains(pair[0])) throw new InvalidOperationException("Unknown token: " + pair[0]);
if (pair[0].StartsWith("Glow"))
{
Color center = Color(pair[1]);
palette[pair[0]] = new RadialGradientBrush(center, System.Windows.Media.Color.FromArgb(0, center.R, center.G, center.B));
}
else palette[pair[0]] = new SolidColorBrush(Color(pair[1]));
}
palette["Ok"] = new SolidColorBrush(Color("#207553"));
palette["Warn"] = new SolidColorBrush(Color("#95620D"));
palette["Bad"] = new SolidColorBrush(Color("#BD3848"));
palette["Idle"] = palette["Muted"];
palette["Na"] = palette["Muted"];
palette["ShadowOpacity"] = 0.10;
File.WriteAllText(Path.Combine(output, variant.Id + ".xaml"), XamlWriter.Save(palette));
MainVm vm = CreateDemo();
window.DataContext = vm;
vm.ThemeApplier = delegate(string theme) { Theme.Apply(window, theme); };
vm.SetTheme(Theme.Light);
((Image)window.FindName("LogoImg")).Source = new BitmapImage(new Uri(Path.Combine(source, "logo.png")));
var root = (FrameworkElement)window.Content;
root.Measure(new Size(window.Width, window.Height));
root.Arrange(new Rect(0, 0, window.Width, window.Height));
root.UpdateLayout();
Pump();
root.UpdateLayout();
Call("UpdateSwitch", window.FindName("WireguardSwitch"), false);
Call("MoveServerSelection", window.FindName("ServerList"), window.FindName("ServerListHost"), window.FindName("ServerSelectionWindow"), false);
vm.ShowSettings = true;
Call("UpdateTabs", window, vm, false);
root.UpdateLayout();
screenshots[i] = Render(root, 1120, 790, 1.5);
Save(screenshots[i], Path.Combine(output, variant.Id + ".png"));
vm.ShowSettings = false;
Call("UpdateTabs", window, vm, false);
root.UpdateLayout();
Save(Render(root, 1120, 790, 1.5), Path.Combine(output, variant.Id + "-log.png"));
vm.DisposeTimers();
window.Close();
Console.WriteLine("Rendered " + variant.Id);
}
Board(screenshots, Path.Combine(output, "comparison.png"));
app.Shutdown();
return 0;
}
catch (Exception ex) { Console.Error.WriteLine(ex); return 1; }
}
static MainVm CreateDemo()
{
var vm = new MainVm();
string[] codes = { "FI", "EE", "DE" };
string[] titles = { "Finland", "Estonia", "Germany" };
string[] pings = { "28 ms", "41 ms", "96 ms" };
for (int i = 0; i < 3; i++)
{
string host = codes[i].ToLowerInvariant() + ".example.com";
vm.Servers.Add(new ServerVm {
Code = codes[i], Title = titles[i], Subtitle = "VLESS · " + host + ":443",
PingText = pings[i], PingKey = i == 2 ? "Warn" : "Ok",
Cfg = new ServerCfg { Code = codes[i], Name = titles[i], Server = host, Port = 443, Protocol = "vless" }
});
}
vm.Servers.Add(new ServerVm { Code = "RU", Title = "Только WireGuard", IsWgOnly = true,
Subtitle = "выход внутри РФ · 198.51.100.10", PingText = "н/д", PingKey = "Na" });
vm.Selected = vm.Servers[0];
vm.LogText = "12:00:00 демонстрация интерфейса — sing-box не запускался";
return vm;
}
static void Call(string method, params object[] args)
{ typeof(SbLauncher.App).GetMethod(method, BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, args); }
static void Pump()
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(delegate { frame.Continue = false; }));
Dispatcher.PushFrame(frame);
}
static Color Color(string hex) { return (Color)ColorConverter.ConvertFromString(hex); }
static BitmapSource Render(Visual visual, int width, int height, double scale)
{
var bitmap = new RenderTargetBitmap((int)(width * scale), (int)(height * scale), 96 * scale, 96 * scale, PixelFormats.Pbgra32);
bitmap.Render(visual);
bitmap.Freeze();
return bitmap;
}
static void Save(BitmapSource bitmap, string path)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = File.Create(path)) encoder.Save(stream);
}
static void Board(BitmapSource[] screenshots, string path)
{
var visual = new DrawingVisual();
RenderOptions.SetBitmapScalingMode(visual, BitmapScalingMode.HighQuality);
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawRectangle(new SolidColorBrush(Color("#E5E7EB")), null, new Rect(0, 0, 2336, 1840));
for (int i = 0; i < screenshots.Length; i++)
{
double x = 32 + (i % 2) * 1152;
double y = 28 + (i / 2) * 902;
DrawText(dc, Variants[i].Title, x, y, 30, "#29323D", true);
DrawText(dc, Variants[i].Description, x, y + 43, 21, "#566373", false);
dc.DrawImage(screenshots[i], new Rect(x, y + 83, 1120, 790));
}
}
Save(Render(visual, 2336, 1840, 1), path);
}
static void DrawText(DrawingContext dc, string text, double x, double y, double size, string color, bool bold)
{
var face = new Typeface(new FontFamily("Segoe UI"), FontStyles.Normal, bold ? FontWeights.SemiBold : FontWeights.Normal, FontStretches.Normal);
dc.DrawText(new FormattedText(text, CultureInfo.GetCultureInfo("ru-RU"), FlowDirection.LeftToRight,
face, size, new SolidColorBrush(Color(color))), new Point(x, y));
}
}