Skip to content

Commit f910a0f

Browse files
committed
feat(escape strings): Added string escape functionality page
- Added EscapePage for string escaping and unescaping operations - Add an escape function entry button on the homepage - Add multilingual resource strings - Clear the input fields after submission on the QR code and barcode pages - Optimize the creation of shortcuts, working directories, and window style settings
1 parent a9979a2 commit f910a0f

10 files changed

Lines changed: 336 additions & 4 deletions

Pages/BarcodePage.xaml.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ private void Generate_Click(object sender, RoutedEventArgs e)
167167
entry.TimestampString = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss");
168168

169169
_logs.Insert(0, entry);
170+
InputText.Text = string.Empty;
170171
}
171172
catch (Exception ex)
172173
{

Pages/EscapePage.xaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<Page x:Class="DevTools.Pages.EscapePage"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:resources="clr-namespace:DevTools.Resources"
5+
Title="EscapePage">
6+
<Grid Margin="20">
7+
<Grid.RowDefinitions>
8+
<RowDefinition Height="Auto" />
9+
<RowDefinition Height="Auto" />
10+
<RowDefinition Height="Auto" />
11+
<RowDefinition Height="Auto" />
12+
<RowDefinition Height="Auto" />
13+
<RowDefinition Height="Auto" />
14+
<RowDefinition Height="*" />
15+
</Grid.RowDefinitions>
16+
17+
<Button Grid.Row="0" Click="Back_Click" HorizontalAlignment="Left" Margin="0,0,0,8" Width="40" Height="40" ToolTip="{x:Static resources:Strings.Back}">
18+
<Button.Template>
19+
<ControlTemplate TargetType="Button">
20+
<Border Background="#4A4A4A" CornerRadius="20" Width="40" Height="40" SnapsToDevicePixels="True">
21+
<TextBlock FontFamily="{StaticResource FontAwesomeSolid}" Text="&#xf060;" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#FFFFFF" />
22+
</Border>
23+
</ControlTemplate>
24+
</Button.Template>
25+
</Button>
26+
<TextBlock Grid.Row="1" Text="{x:Static resources:Strings.PageEscape}" FontSize="18" Margin="0,10" />
27+
28+
<TextBlock Grid.Row="2" Text="{x:Static resources:Strings.Input}" FontWeight="Bold" FontSize="14" Margin="0,10,0,5" />
29+
<TextBox Grid.Row="3" x:Name="InputText" Height="150" TextWrapping="Wrap" AcceptsReturn="True" />
30+
31+
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="0,10">
32+
<Button Click="Escape_Click" Width="100" Height="32" ToolTip="{x:Static resources:Strings.Escape}">
33+
<TextBlock Text="{x:Static resources:Strings.Escape}" FontSize="12" FontWeight="Bold" Foreground="#FFFFFF" />
34+
</Button>
35+
<Button Click="Unescape_Click" Margin="8,0,0,0" Width="100" Height="32" ToolTip="{x:Static resources:Strings.Unescape}">
36+
<TextBlock Text="{x:Static resources:Strings.Unescape}" FontSize="12" FontWeight="Bold" Foreground="#FFFFFF" />
37+
</Button>
38+
<Button Click="CopyOutput_Click" Margin="8,0,0,0" Width="80" Height="32" ToolTip="{x:Static resources:Strings.Copy}">
39+
<TextBlock FontFamily="{StaticResource FontAwesomeSolid}" Text="&#xf0c5;" FontSize="16" FontWeight="Bold" Foreground="#FFFFFF" />
40+
</Button>
41+
</StackPanel>
42+
43+
<TextBlock Grid.Row="5" Text="{x:Static resources:Strings.Output}" FontWeight="Bold" FontSize="14" Margin="0,10,0,5" />
44+
<TextBox Grid.Row="6" x:Name="OutputText" Height="150" TextWrapping="Wrap" AcceptsReturn="True" IsReadOnly="True" VerticalAlignment="Top" />
45+
</Grid>
46+
</Page>

Pages/EscapePage.xaml.cs

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Text.RegularExpressions;
5+
using System.Windows;
6+
using System.Windows.Controls;
7+
using DevTools.Helpers;
8+
using DevTools.Resources;
9+
using MessageBox = System.Windows.MessageBox;
10+
11+
namespace DevTools.Pages
12+
{
13+
public partial class EscapePage : Page
14+
{
15+
public EscapePage()
16+
{
17+
InitializeComponent();
18+
LoadState();
19+
}
20+
21+
private void Back_Click(object sender, RoutedEventArgs e)
22+
{
23+
SaveState();
24+
NavigationService?.Navigate(new HomePage());
25+
}
26+
27+
private void SaveState()
28+
{
29+
var state = new Dictionary<string, string>
30+
{
31+
{ "InputText", InputText.Text ?? string.Empty },
32+
{ "OutputText", OutputText.Text ?? string.Empty }
33+
};
34+
PageStateManager.SavePageState(this, state);
35+
}
36+
37+
private void LoadState()
38+
{
39+
var state = PageStateManager.GetPageState(this);
40+
if (state != null)
41+
{
42+
InputText.Text = state.GetValueOrDefault("InputText", string.Empty);
43+
OutputText.Text = state.GetValueOrDefault("OutputText", string.Empty);
44+
}
45+
}
46+
47+
private void Escape_Click(object sender, RoutedEventArgs e)
48+
{
49+
try
50+
{
51+
var input = InputText.Text;
52+
if (string.IsNullOrWhiteSpace(input))
53+
{
54+
MessageBox.Show(Strings.InputEmpty, Strings.Error, MessageBoxButton.OK, MessageBoxImage.Warning);
55+
return;
56+
}
57+
58+
var escaped = EscapeString(input);
59+
OutputText.Text = escaped;
60+
}
61+
catch (Exception ex)
62+
{
63+
MessageBox.Show($"{Strings.EncodeFailed}: {ex.Message}", Strings.Error, MessageBoxButton.OK, MessageBoxImage.Error);
64+
}
65+
}
66+
67+
private void Unescape_Click(object sender, RoutedEventArgs e)
68+
{
69+
try
70+
{
71+
var input = InputText.Text;
72+
if (string.IsNullOrWhiteSpace(input))
73+
{
74+
MessageBox.Show(Strings.InputEmpty, Strings.Error, MessageBoxButton.OK, MessageBoxImage.Warning);
75+
return;
76+
}
77+
78+
var unescaped = UnescapeString(input);
79+
OutputText.Text = unescaped;
80+
}
81+
catch (Exception ex)
82+
{
83+
MessageBox.Show($"{Strings.DecodeFailed}: {ex.Message}", Strings.Error, MessageBoxButton.OK, MessageBoxImage.Error);
84+
}
85+
}
86+
87+
private void CopyOutput_Click(object sender, RoutedEventArgs e)
88+
{
89+
try
90+
{
91+
var output = OutputText.Text;
92+
if (string.IsNullOrWhiteSpace(output))
93+
{
94+
MessageBox.Show(Strings.OutputEmpty, Strings.Error, MessageBoxButton.OK, MessageBoxImage.Warning);
95+
return;
96+
}
97+
98+
ClipboardHelper.CopyWithFeedback(output, null);
99+
}
100+
catch (Exception ex)
101+
{
102+
MessageBox.Show($"{Strings.SaveFailed}: {ex.Message}", Strings.Error, MessageBoxButton.OK, MessageBoxImage.Error);
103+
}
104+
}
105+
106+
private string EscapeString(string input)
107+
{
108+
var sb = new StringBuilder();
109+
foreach (char c in input)
110+
{
111+
switch (c)
112+
{
113+
case '\a':
114+
sb.Append("\\a");
115+
break;
116+
case '\b':
117+
sb.Append("\\b");
118+
break;
119+
case '\f':
120+
sb.Append("\\f");
121+
break;
122+
case '\n':
123+
sb.Append("\\n");
124+
break;
125+
case '\r':
126+
sb.Append("\\r");
127+
break;
128+
case '\t':
129+
sb.Append("\\t");
130+
break;
131+
case '\v':
132+
sb.Append("\\v");
133+
break;
134+
case '\\':
135+
sb.Append("\\\\");
136+
break;
137+
case '\"':
138+
sb.Append("\\\"");
139+
break;
140+
case '\'':
141+
sb.Append("\\'");
142+
break;
143+
default:
144+
if (c >= 32 && c <= 126)
145+
{
146+
sb.Append(c);
147+
}
148+
else
149+
{
150+
sb.Append($"\\u{(int)c:X4}");
151+
}
152+
break;
153+
}
154+
}
155+
return sb.ToString();
156+
}
157+
158+
private string UnescapeString(string input)
159+
{
160+
var sb = new StringBuilder();
161+
for (int i = 0; i < input.Length; i++)
162+
{
163+
char c = input[i];
164+
165+
if (c == '\\' && i + 1 < input.Length)
166+
{
167+
char next = input[i + 1];
168+
switch (next)
169+
{
170+
case 'a':
171+
sb.Append('\a');
172+
i++;
173+
break;
174+
case 'b':
175+
sb.Append('\b');
176+
i++;
177+
break;
178+
case 'f':
179+
sb.Append('\f');
180+
i++;
181+
break;
182+
case 'n':
183+
sb.Append('\n');
184+
i++;
185+
break;
186+
case 'r':
187+
sb.Append('\r');
188+
i++;
189+
break;
190+
case 't':
191+
sb.Append('\t');
192+
i++;
193+
break;
194+
case 'v':
195+
sb.Append('\v');
196+
i++;
197+
break;
198+
case '\\':
199+
sb.Append('\\');
200+
i++;
201+
break;
202+
case '"':
203+
sb.Append('"');
204+
i++;
205+
break;
206+
case '\'':
207+
sb.Append('\'');
208+
i++;
209+
break;
210+
case '/':
211+
// JSON 风格的转义:\/ -> /
212+
sb.Append('/');
213+
i++;
214+
break;
215+
case 'u':
216+
if (i + 5 < input.Length)
217+
{
218+
var hex = input.Substring(i + 2, 4);
219+
if (ushort.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out ushort unicode))
220+
{
221+
sb.Append((char)unicode);
222+
i += 5;
223+
break;
224+
}
225+
}
226+
sb.Append(c);
227+
break;
228+
default:
229+
// 其他情况保留反斜杠
230+
sb.Append(c);
231+
break;
232+
}
233+
}
234+
else
235+
{
236+
sb.Append(c);
237+
}
238+
}
239+
return sb.ToString();
240+
}
241+
}
242+
}

Pages/HomePage.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@
7171
<TextBlock Text="{x:Static resources:Strings.PageUrlEncode}" FontSize="14" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,8,0,0" TextWrapping="Wrap" Foreground="#FFFFFF" />
7272
</StackPanel>
7373
</Button>
74+
<Button x:Name="BtnEscape" Click="BtnEscape_Click" Style="{StaticResource CardButtonStyle}" Margin="8">
75+
<StackPanel Width="140" HorizontalAlignment="Center" VerticalAlignment="Center">
76+
<TextBlock FontFamily="{StaticResource FontAwesomeSolid}" Text="&#xf071;" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" TextAlignment="Center" Foreground="#FFFFFF" />
77+
<TextBlock Text="{x:Static resources:Strings.PageEscape}" FontSize="14" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,8,0,0" TextWrapping="Wrap" Foreground="#FFFFFF" />
78+
</StackPanel>
79+
</Button>
7480
</UniformGrid>
7581
</Grid>
7682
</Page>

Pages/HomePage.xaml.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ private void BtnUrlEncode_Click(object sender, RoutedEventArgs e)
5050
NavigationService?.Navigate(new UrlEncodePage());
5151
}
5252

53+
private void BtnEscape_Click(object sender, RoutedEventArgs e)
54+
{
55+
NavigationService?.Navigate(new EscapePage());
56+
}
57+
5358
private void BtnSettings_Click(object sender, RoutedEventArgs e)
5459
{
5560
NavigationService?.Navigate(new SettingsPage());

Pages/QrPage.xaml.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ private void Generate_Click(object sender, RoutedEventArgs e)
166166

167167
entry.TimestampString = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss");
168168
_logs.Insert(0, entry);
169+
InputText.Text = string.Empty;
169170
}
170171
catch (Exception ex)
171172
{

Pages/SettingsPage.xaml.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,24 +252,34 @@ private void CreateShortcut(string shortcutPath, string targetPath, string descr
252252
{
253253
try
254254
{
255+
if (string.IsNullOrEmpty(targetPath))
256+
{
257+
throw new InvalidOperationException("应用程序路径为空");
258+
}
259+
255260
var shellType = Type.GetTypeFromProgID("WScript.Shell");
256261
if (shellType == null)
257262
{
258-
MessageBox.Show(Strings.Error, Strings.Error, MessageBoxButton.OK, MessageBoxImage.Error);
259-
return;
263+
throw new InvalidOperationException("无法创建 WScript.Shell 对象");
260264
}
261265

262266
dynamic shell = Activator.CreateInstance(shellType)!;
263267
if (shell == null)
264268
{
265-
MessageBox.Show(Strings.Error, Strings.Error, MessageBoxButton.OK, MessageBoxImage.Error);
266-
return;
269+
throw new InvalidOperationException("WScript.Shell 实例创建失败");
267270
}
268271

269272
var shortcut = shell.CreateShortcut(shortcutPath);
270273
shortcut.TargetPath = targetPath;
271274
shortcut.Description = description;
275+
shortcut.WorkingDirectory = Path.GetDirectoryName(targetPath);
276+
shortcut.WindowStyle = 1;
272277
shortcut.Save();
278+
279+
if (!File.Exists(shortcutPath))
280+
{
281+
throw new InvalidOperationException("快捷方式文件创建失败");
282+
}
273283
}
274284
catch (Exception ex)
275285
{

Resources/Strings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ public static string Get(string key)
105105
public static string PageUrlEncode => Get("PageUrlEncode");
106106
public static string URLEncode => Get("URLEncode");
107107
public static string URLDecode => Get("URLDecode");
108+
public static string PageEscape => Get("PageEscape");
109+
public static string Escape => Get("Escape");
110+
public static string Unescape => Get("Unescape");
108111
public static string Input => Get("Input");
109112
public static string Output => Get("Output");
110113
public static string EncodeFailed => Get("EncodeFailed");

Resources/Strings.en-US.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,13 @@
361361
<data name="OutputEmpty" xml:space="preserve">
362362
<value>Output is empty</value>
363363
</data>
364+
<data name="PageEscape" xml:space="preserve">
365+
<value>String Escape</value>
366+
</data>
367+
<data name="Escape" xml:space="preserve">
368+
<value>Escape</value>
369+
</data>
370+
<data name="Unescape" xml:space="preserve">
371+
<value>Unescape</value>
372+
</data>
364373
</root>

0 commit comments

Comments
 (0)