Files
One_click_run/src/OneClickRun.App/Pages/CommandsPage.xaml.cs
T

342 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using OneClickRun.App.Services;
using OneClickRun.Core.Data;
using OneClickRun.Core.Models;
using OneClickRun.Core.Services;
using Windows.Storage.Pickers;
namespace OneClickRun.App.Pages;
/// <summary>快捷指令列表:增删、排序、编辑器(名称/类型/目标/参数/图标/颜色/测试运行)。</summary>
public sealed partial class CommandsPage : Page
{
public CommandsPage()
{
InitializeComponent();
Loaded += (_, _) => RefreshList();
}
private static List<CommandItem> Items => App.Instance.Settings.Commands;
private void RefreshList()
{
var items = Items.ToList();
CommandList.ItemsSource = items;
CountText.Text = "共 " + items.Count + " 个指令(轮盘扇区数量与指令数量一致,上限 " + AppSettings.MaxCommands + " 个)";
}
private int SelectedIndex => CommandList.SelectedIndex;
private async void Add_Click(object sender, RoutedEventArgs e)
{
if (Items.Count >= AppSettings.MaxCommands)
{
App.Instance.ReportError("最多支持 " + AppSettings.MaxCommands + " 个指令,请先删除部分指令。");
return;
}
var item = new CommandItem { IconGlyph = GlyphCatalog.DefaultGlyphFor(ActionType.App) };
if (await EditItemAsync(item, isNew: true))
{
Items.Add(item);
App.Instance.SaveSettings();
App.Instance.ApplySettingsToSystem();
RefreshList();
CommandList.SelectedIndex = Items.Count - 1;
}
}
private async void Edit_Click(object sender, RoutedEventArgs e)
{
if (SelectedIndex < 0)
{
App.Instance.ReportError("请先在列表中选择一个指令。");
return;
}
var original = Items[SelectedIndex];
var copy = new CommandItem
{
Id = original.Id,
Name = original.Name,
ActionType = original.ActionType,
Target = original.Target,
Arguments = original.Arguments,
IconGlyph = original.IconGlyph,
ColorIndex = original.ColorIndex,
RunAsAdmin = original.RunAsAdmin,
HideWindow = original.HideWindow,
};
if (await EditItemAsync(copy, isNew: false))
{
Items[SelectedIndex] = copy;
App.Instance.SaveSettings();
App.Instance.ApplySettingsToSystem();
RefreshList();
}
}
private async void Delete_Click(object sender, RoutedEventArgs e)
{
if (SelectedIndex < 0)
{
App.Instance.ReportError("请先在列表中选择一个指令。");
return;
}
var item = Items[SelectedIndex];
var dialog = new ContentDialog
{
Title = "删除指令",
Content = "确定删除指令「" + item.Name + "」吗?",
PrimaryButtonText = "删除",
CloseButtonText = "取消",
DefaultButton = ContentDialogButton.Close,
XamlRoot = XamlRoot,
};
if (await dialog.ShowAsync() == ContentDialogResult.Primary)
{
Items.RemoveAt(SelectedIndex);
App.Instance.SaveSettings();
App.Instance.ApplySettingsToSystem();
RefreshList();
}
}
private void MoveUp_Click(object sender, RoutedEventArgs e)
{
int i = SelectedIndex;
if (i <= 0) return;
(Items[i], Items[i - 1]) = (Items[i - 1], Items[i]);
App.Instance.SaveSettings();
App.Instance.ApplySettingsToSystem();
RefreshList();
CommandList.SelectedIndex = i - 1;
}
private void MoveDown_Click(object sender, RoutedEventArgs e)
{
int i = SelectedIndex;
if (i < 0 || i >= Items.Count - 1) return;
(Items[i], Items[i + 1]) = (Items[i + 1], Items[i]);
App.Instance.SaveSettings();
App.Instance.ApplySettingsToSystem();
RefreshList();
CommandList.SelectedIndex = i + 1;
}
private void CommandList_DoubleTapped(object sender, Microsoft.UI.Xaml.Input.DoubleTappedRoutedEventArgs e)
{
if (SelectedIndex >= 0) Edit_Click(sender, e);
}
/// <summary>打开指令编辑器;返回 true 表示用户保存。</summary>
private async Task<bool> EditItemAsync(CommandItem item, bool isNew)
{
var dialog = new ContentDialog
{
Title = isNew ? "新增指令" : "编辑指令",
PrimaryButtonText = "保存",
CloseButtonText = "取消",
DefaultButton = ContentDialogButton.Primary,
XamlRoot = XamlRoot,
};
var nameBox = new TextBox { Header = "名称", Text = item.Name, PlaceholderText = "例如:记事本" };
var typeBox = new ComboBox { Header = "类型", HorizontalAlignment = HorizontalAlignment.Stretch };
typeBox.Items.Add(new ComboBoxItem { Content = "打开软件" });
typeBox.Items.Add(new ComboBoxItem { Content = "打开文件夹" });
typeBox.Items.Add(new ComboBoxItem { Content = "打开网址" });
typeBox.Items.Add(new ComboBoxItem { Content = "运行 PowerShell 脚本" });
typeBox.SelectedIndex = (int)item.ActionType;
var targetBox = new TextBox { Header = "目标", Text = item.Target };
var browseButton = new Button { Content = "浏览…", Margin = new Thickness(8, 26, 0, 0) };
var targetGrid = new Grid();
targetGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
targetGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
Grid.SetColumn(targetBox, 0);
Grid.SetColumn(browseButton, 1);
targetGrid.Children.Add(targetBox);
targetGrid.Children.Add(browseButton);
var argsBox = new TextBox { Header = "参数(可选)", Text = item.Arguments ?? "" };
var adminBox = new CheckBox { Content = "以管理员身份运行", IsChecked = item.RunAsAdmin };
var hideBox = new CheckBox { Content = "隐藏窗口运行", IsChecked = item.HideWindow };
bool glyphAuto = true;
var glyphBox = new ComboBox { Header = "图标", HorizontalAlignment = HorizontalAlignment.Stretch, MinWidth = 200 };
foreach (var option in GlyphCatalog.All)
{
var icon = new FontIcon { Glyph = option.Glyph, FontSize = 16 };
var text = new TextBlock { Text = option.Name, Margin = new Thickness(8, 0, 0, 0) };
var optionPanel = new StackPanel { Orientation = Orientation.Horizontal };
optionPanel.Children.Add(icon);
optionPanel.Children.Add(text);
glyphBox.Items.Add(new ComboBoxItem { Content = optionPanel, Tag = option.Glyph });
}
int glyphIndex = GlyphCatalog.IndexOf(item.IconGlyph);
if (glyphIndex < 0)
{
glyphBox.Items.Add(new ComboBoxItem { Content = new TextBlock { Text = "自定义" }, Tag = item.IconGlyph });
glyphIndex = glyphBox.Items.Count - 1;
}
glyphBox.SelectedIndex = glyphIndex;
glyphBox.SelectionChanged += (_, _) => glyphAuto = false;
var colorBox = new ComboBox { Header = "颜色", HorizontalAlignment = HorizontalAlignment.Stretch, MinWidth = 200 };
for (int i = 0; i < WheelPalette.Colors.Length; i++)
{
var swatch = new Border
{
Background = new SolidColorBrush(ColorHelper.FromHex(WheelPalette.Colors[i])),
Width = 64,
Height = 14,
CornerRadius = new CornerRadius(3),
};
var swatchPanel = new StackPanel { Orientation = Orientation.Horizontal };
swatchPanel.Children.Add(swatch);
swatchPanel.Children.Add(new TextBlock { Text = "颜色 " + (i + 1), Margin = new Thickness(8, 0, 0, 0) });
colorBox.Items.Add(new ComboBoxItem { Content = swatchPanel, Tag = i });
}
colorBox.SelectedIndex = Math.Clamp(item.ColorIndex, 0, WheelPalette.Colors.Length - 1);
var warnText = new TextBlock
{
Foreground = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 230, 72, 72)),
TextWrapping = TextWrapping.Wrap,
Visibility = Visibility.Collapsed,
};
var testButton = new Button { Content = "测试运行", HorizontalAlignment = HorizontalAlignment.Left };
var panel = new StackPanel { Spacing = 12, MinWidth = 440 };
panel.Children.Add(nameBox);
panel.Children.Add(typeBox);
panel.Children.Add(targetGrid);
panel.Children.Add(argsBox);
panel.Children.Add(adminBox);
panel.Children.Add(hideBox);
panel.Children.Add(glyphBox);
panel.Children.Add(colorBox);
panel.Children.Add(warnText);
panel.Children.Add(testButton);
dialog.Content = panel;
void UpdateVisibility()
{
var type = (ActionType)typeBox.SelectedIndex;
argsBox.Visibility = type == ActionType.App ? Visibility.Visible : Visibility.Collapsed;
adminBox.Visibility = type is ActionType.App or ActionType.Script ? Visibility.Visible : Visibility.Collapsed;
hideBox.Visibility = type == ActionType.Script ? Visibility.Visible : Visibility.Collapsed;
browseButton.Visibility = type == ActionType.Url ? Visibility.Collapsed : Visibility.Visible;
targetBox.Header = type switch
{
ActionType.App => "程序路径",
ActionType.Folder => "文件夹路径",
ActionType.Url => "网址(无协议时自动补 https://",
ActionType.Script => "脚本文件(.ps1",
_ => "目标",
};
if (glyphAuto)
{
var glyph = GlyphCatalog.DefaultGlyphFor(type);
int idx = GlyphCatalog.IndexOf(glyph);
if (idx >= 0) glyphBox.SelectedIndex = idx;
}
}
typeBox.SelectionChanged += (_, _) => UpdateVisibility();
UpdateVisibility();
browseButton.Click += async (_, _) =>
{
var type = (ActionType)typeBox.SelectedIndex;
if (type == ActionType.Folder)
{
var picker = new FolderPicker();
WinRT.Interop.InitializeWithWindow.Initialize(picker, WinRT.Interop.WindowNative.GetWindowHandle(App.Instance.Main));
var folder = await picker.PickSingleFolderAsync();
if (folder != null) targetBox.Text = folder.Path;
}
else if (type is ActionType.App or ActionType.Script)
{
var picker = new FileOpenPicker();
WinRT.Interop.InitializeWithWindow.Initialize(picker, WinRT.Interop.WindowNative.GetWindowHandle(App.Instance.Main));
if (type == ActionType.Script)
{
picker.FileTypeFilter.Add(".ps1");
picker.FileTypeFilter.Add(".cmd");
picker.FileTypeFilter.Add(".bat");
}
else
{
picker.FileTypeFilter.Add(".exe");
picker.FileTypeFilter.Add(".lnk");
picker.FileTypeFilter.Add("*");
}
var file = await picker.PickSingleFileAsync();
if (file != null) targetBox.Text = file.Path;
}
};
CommandItem BuildFromInputs()
{
return new CommandItem
{
Name = nameBox.Text.Trim(),
ActionType = (ActionType)typeBox.SelectedIndex,
Target = targetBox.Text.Trim(),
Arguments = argsBox.Text.Trim(),
IconGlyph = (glyphBox.SelectedItem as ComboBoxItem)?.Tag as string ?? "\uE756",
ColorIndex = (colorBox.SelectedItem as ComboBoxItem)?.Tag is int c ? c : 0,
RunAsAdmin = adminBox.IsChecked == true,
HideWindow = hideBox.IsChecked == true,
};
}
testButton.Click += (_, _) =>
{
var probe = BuildFromInputs();
var error = CommandRunner.Validate(probe);
if (error != null)
{
warnText.Text = error;
warnText.Visibility = Visibility.Visible;
return;
}
try
{
CommandRunner.Run(probe);
warnText.Text = "已启动,请检查目标是否按预期打开。";
warnText.Visibility = Visibility.Visible;
}
catch (Exception ex)
{
warnText.Text = "启动失败:" + ex.Message;
warnText.Visibility = Visibility.Visible;
}
};
dialog.PrimaryButtonClick += (_, args) =>
{
var probe = BuildFromInputs();
var error = CommandRunner.Validate(probe);
if (error != null)
{
warnText.Text = error;
warnText.Visibility = Visibility.Visible;
args.Cancel = true;
return;
}
item.Name = probe.Name;
item.ActionType = probe.ActionType;
item.Target = probe.Target;
item.Arguments = probe.Arguments;
item.IconGlyph = probe.IconGlyph;
item.ColorIndex = probe.ColorIndex;
item.RunAsAdmin = probe.RunAsAdmin;
item.HideWindow = probe.HideWindow;
};
return await dialog.ShowAsync() == ContentDialogResult.Primary;
}
}