refactor: show child-window and modal dialog

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-07-04 14:49:04 +08:00
parent 4bfcfb8b24
commit a6ebc1502c
29 changed files with 131 additions and 121 deletions

View File

@@ -37,10 +37,10 @@ namespace SourceGit
}
}
public static readonly Command OpenPreferencesCommand = new Command(_ => ShowWindow(new Views.Preferences(), false));
public static readonly Command OpenHotkeysCommand = new Command(_ => ShowWindow(new Views.Hotkeys(), false));
public static readonly Command OpenPreferencesCommand = new Command(async _ => await ShowDailog(new Views.Preferences()));
public static readonly Command OpenHotkeysCommand = new Command(async _ => await ShowDailog(new Views.Hotkeys()));
public static readonly Command OpenAppDataDirCommand = new Command(_ => Native.OS.OpenInFileManager(Native.OS.DataDir));
public static readonly Command OpenAboutCommand = new Command(_ => ShowWindow(new Views.About(), false));
public static readonly Command OpenAboutCommand = new Command(async _ => await ShowDailog(new Views.About()));
public static readonly Command CheckForUpdateCommand = new Command(_ => (Current as App)?.Check4Update(true));
public static readonly Command QuitCommand = new Command(_ => Quit(0));
public static readonly Command CopyTextBlockCommand = new Command(async p =>

View File

@@ -106,43 +106,56 @@ namespace SourceGit
#endregion
#region Utility Functions
public static void ShowWindow(object data, bool showAsDialog)
public static object CreateViewForViewModel(object data)
{
var impl = (Views.ChromelessWindow target, bool isDialog) =>
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } owner })
{
if (isDialog)
target.ShowDialog(owner);
else
target.Show(owner);
}
else
{
target.Show();
}
};
if (data is Views.ChromelessWindow window)
{
impl(window, showAsDialog);
return;
}
var dataTypeName = data.GetType().FullName;
if (string.IsNullOrEmpty(dataTypeName) || !dataTypeName.Contains(".ViewModels.", StringComparison.Ordinal))
return;
return null;
var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
var viewType = Type.GetType(viewTypeName);
if (viewType == null || !viewType.IsSubclassOf(typeof(Views.ChromelessWindow)))
return;
if (viewType != null)
return Activator.CreateInstance(viewType);
window = Activator.CreateInstance(viewType) as Views.ChromelessWindow;
return null;
}
public static Task ShowDailog(object data, Window owner = null)
{
if (owner == null)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } mainWindow })
owner = mainWindow;
else
return null;
}
if (data is Views.ChromelessWindow window)
return window.ShowDialog(owner);
window = CreateViewForViewModel(data) as Views.ChromelessWindow;
if (window != null)
{
window.DataContext = data;
impl(window, showAsDialog);
return window.ShowDialog(owner);
}
return null;
}
public static void ShowWindow(object data)
{
if (data is Views.ChromelessWindow window)
{
window.Show();
return;
}
window = CreateViewForViewModel(data) as Views.ChromelessWindow;
if (window != null)
{
window.DataContext = data;
window.Show();
}
}
@@ -651,9 +664,9 @@ namespace SourceGit
private void ShowSelfUpdateResult(object data)
{
Dispatcher.UIThread.Post(() =>
Dispatcher.UIThread.Post(async () =>
{
ShowWindow(new ViewModels.SelfUpdate() { Data = data }, true);
await ShowDailog(new ViewModels.SelfUpdate { Data = data });
});
}

View File

@@ -259,7 +259,7 @@ namespace SourceGit.ViewModels
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new DirHistories(_repo, node.FullPath, _commit.SHA), false);
App.ShowWindow(new DirHistories(_repo, node.FullPath, _commit.SHA));
ev.Handled = true;
};
@@ -351,7 +351,7 @@ namespace SourceGit.ViewModels
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new FileHistories(_repo, change.Path, _commit.SHA), false);
App.ShowWindow(new FileHistories(_repo, change.Path, _commit.SHA));
ev.Handled = true;
};
@@ -361,7 +361,7 @@ namespace SourceGit.ViewModels
blame.IsEnabled = change.Index != Models.ChangeState.Deleted;
blame.Click += (_, ev) =>
{
App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit), false);
App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit));
ev.Handled = true;
};
@@ -470,7 +470,7 @@ namespace SourceGit.ViewModels
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new DirHistories(_repo, path, _commit.SHA), false);
App.ShowWindow(new DirHistories(_repo, path, _commit.SHA));
ev.Handled = true;
};
@@ -572,7 +572,7 @@ namespace SourceGit.ViewModels
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, ev) =>
{
App.ShowWindow(new FileHistories(_repo, file.Path, _commit.SHA), false);
App.ShowWindow(new FileHistories(_repo, file.Path, _commit.SHA));
ev.Handled = true;
};
@@ -582,7 +582,7 @@ namespace SourceGit.ViewModels
blame.IsEnabled = file.Type == Models.ObjectType.Blob;
blame.Click += (_, ev) =>
{
App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit), false);
App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit));
ev.Handled = true;
};

View File

@@ -645,7 +645,7 @@ namespace SourceGit.ViewModels
var interactiveRebase = new MenuItem();
interactiveRebase.Header = App.Text("CommitCM.InteractiveRebase", current.Name);
interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase");
interactiveRebase.Click += (_, e) =>
interactiveRebase.Click += async (_, e) =>
{
if (_repo.LocalChangesCount > 0)
{
@@ -653,7 +653,7 @@ namespace SourceGit.ViewModels
return;
}
App.ShowWindow(new InteractiveRebase(_repo, current, commit), true);
await App.ShowDailog(new InteractiveRebase(_repo, current, commit));
e.Handled = true;
};

View File

@@ -450,9 +450,9 @@ namespace SourceGit.ViewModels
var configure = new MenuItem();
configure.Header = App.Text("Workspace.Configure");
configure.Click += (_, e) =>
configure.Click += async (_, e) =>
{
App.ShowWindow(new ConfigureWorkspace(), true);
await App.ShowDailog(new ConfigureWorkspace());
e.Handled = true;
};
menu.Items.Add(configure);

View File

@@ -1666,9 +1666,9 @@ namespace SourceGit.ViewModels
locks.IsEnabled = _remotes.Count > 0;
if (_remotes.Count == 1)
{
locks.Click += (_, e) =>
locks.Click += async (_, e) =>
{
App.ShowWindow(new LFSLocks(this, _remotes[0].Name), true);
await App.ShowDailog(new LFSLocks(this, _remotes[0].Name));
e.Handled = true;
};
}
@@ -1679,9 +1679,9 @@ namespace SourceGit.ViewModels
var remoteName = remote.Name;
var lockRemote = new MenuItem();
lockRemote.Header = remoteName;
lockRemote.Click += (_, e) =>
lockRemote.Click += async (_, e) =>
{
App.ShowWindow(new LFSLocks(this, remoteName), true);
await App.ShowDailog(new LFSLocks(this, remoteName));
e.Handled = true;
};
locks.Items.Add(lockRemote);
@@ -1984,7 +1984,7 @@ namespace SourceGit.ViewModels
compareWithCurrent.Icon = App.CreateMenuIcon("Icons.Compare");
compareWithCurrent.Click += (_, _) =>
{
App.ShowWindow(new BranchCompare(_fullpath, branch, _currentBranch), false);
App.ShowWindow(new BranchCompare(_fullpath, branch, _currentBranch));
};
menu.Items.Add(new MenuItem() { Header = "-" });
menu.Items.Add(compareWithCurrent);
@@ -2264,7 +2264,7 @@ namespace SourceGit.ViewModels
compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare");
compareWithHead.Click += (_, _) =>
{
App.ShowWindow(new BranchCompare(_fullpath, branch, _currentBranch), false);
App.ShowWindow(new BranchCompare(_fullpath, branch, _currentBranch));
};
menu.Items.Add(compareWithHead);

View File

@@ -334,11 +334,6 @@ namespace SourceGit.ViewModels
UseExternalMergeTool(null);
}
public void OpenAssumeUnchanged()
{
App.ShowWindow(new AssumeUnchangedManager(_repo), true);
}
public void StashAll(bool autoStart)
{
if (!_repo.CanCreatePopup())
@@ -973,9 +968,9 @@ namespace SourceGit.ViewModels
history.Click += (_, e) =>
{
if (hasSelectedFolder)
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder));
else
App.ShowWindow(new FileHistories(_repo, change.Path), false);
App.ShowWindow(new FileHistories(_repo, change.Path));
e.Handled = true;
};
@@ -1166,7 +1161,7 @@ namespace SourceGit.ViewModels
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder));
e.Handled = true;
};
menu.Items.Add(new MenuItem() { Header = "-" });
@@ -1215,9 +1210,9 @@ namespace SourceGit.ViewModels
if (services.Count == 1)
{
ai.Click += (_, e) =>
ai.Click += async (_, e) =>
{
App.ShowWindow(new AIAssistant(_repo, services[0], _selectedStaged, t => CommitMessage = t), true);
await App.ShowDailog(new AIAssistant(_repo, services[0], _selectedStaged, t => CommitMessage = t));
e.Handled = true;
};
}
@@ -1229,9 +1224,9 @@ namespace SourceGit.ViewModels
var item = new MenuItem();
item.Header = service.Name;
item.Click += (_, e) =>
item.Click += async (_, e) =>
{
App.ShowWindow(new AIAssistant(_repo, dup, _selectedStaged, t => CommitMessage = t), true);
await App.ShowDailog(new AIAssistant(_repo, dup, _selectedStaged, t => CommitMessage = t));
e.Handled = true;
};
@@ -1421,9 +1416,9 @@ namespace SourceGit.ViewModels
history.Click += (_, e) =>
{
if (hasSelectedFolder)
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder));
else
App.ShowWindow(new FileHistories(_repo, change.Path), false);
App.ShowWindow(new FileHistories(_repo, change.Path));
e.Handled = true;
};
@@ -1532,7 +1527,7 @@ namespace SourceGit.ViewModels
history.Icon = App.CreateMenuIcon("Icons.Histories");
history.Click += (_, e) =>
{
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder), false);
App.ShowWindow(new DirHistories(_repo, selectedSingleFolder));
e.Handled = true;
};
@@ -1672,7 +1667,7 @@ namespace SourceGit.ViewModels
if (services.Count == 1)
{
App.ShowWindow(new AIAssistant(_repo, services[0], _staged, t => CommitMessage = t), true);
_ = App.ShowDailog(new AIAssistant(_repo, services[0], _staged, t => CommitMessage = t));
return null;
}
@@ -1682,9 +1677,9 @@ namespace SourceGit.ViewModels
var dup = service;
var item = new MenuItem();
item.Header = service.Name;
item.Click += (_, e) =>
item.Click += async (_, e) =>
{
App.ShowWindow(new AIAssistant(_repo, dup, _staged, t => CommitMessage = t), true);
await App.ShowDailog(new AIAssistant(_repo, dup, _staged, t => CommitMessage = t));
e.Handled = true;
};
@@ -1902,7 +1897,7 @@ namespace SourceGit.ViewModels
{
if ((!autoStage && _staged.Count == 0) || (autoStage && _cached.Count == 0))
{
App.ShowWindow(new ConfirmEmptyCommit(_cached.Count > 0, stageAll => DoCommit(stageAll, autoPush, CommitCheckPassed.FileCount)), true);
_ = App.ShowDailog(new ConfirmEmptyCommit(_cached.Count > 0, stageAll => DoCommit(stageAll, autoPush, CommitCheckPassed.FileCount)));
return;
}
}

View File

@@ -115,6 +115,7 @@ namespace SourceGit.Views
{
public AIAssistant()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -7,6 +7,7 @@ namespace SourceGit.Views
{
public About()
{
CloseOnESC = true;
InitializeComponent();
var assembly = Assembly.GetExecutingAssembly();

View File

@@ -7,6 +7,7 @@ namespace SourceGit.Views
{
public AssumeUnchangedManager()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -11,8 +11,7 @@
x:Name="ThisControl"
Icon="/App.ico"
Title="{DynamicResource Text.BranchCompare}"
MinWidth="1280" MinHeight="720"
WindowStartupLocation="CenterOwner">
MinWidth="1280" MinHeight="720">
<Grid RowDefinitions="Auto,64,*">
<!-- TitleBar -->
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">

View File

@@ -13,6 +13,12 @@ namespace SourceGit.Views
get => Native.OS.UseSystemWindowFrame;
}
public bool CloseOnESC
{
get;
set;
} = false;
protected override Type StyleKeyOverride => typeof(Window);
public ChromelessWindow()
@@ -68,6 +74,17 @@ namespace SourceGit.Views
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e is { Handled: false, Key: Key.Escape, KeyModifiers: KeyModifiers.None } && CloseOnESC)
{
Close();
e.Handled = true;
}
}
private void OnWindowBorderPointerPressed(object sender, PointerPressedEventArgs e)
{
if (sender is Border { Tag: WindowEdge edge } && CanResize)

View File

@@ -9,6 +9,7 @@ namespace SourceGit.Views
{
public CommitMessageEditor()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -4,6 +4,7 @@ namespace SourceGit.Views
{
public ConfigureCustomActionControls()
{
CloseOnESC = true;
InitializeComponent();
}
}

View File

@@ -9,6 +9,7 @@ namespace SourceGit.Views
{
public ConfigureWorkspace()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -6,6 +6,7 @@ namespace SourceGit.Views
{
public ConventionalCommitMessageBuilder()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -1,20 +1,11 @@
using Avalonia.Input;
namespace SourceGit.Views
{
public partial class Hotkeys : ChromelessWindow
{
public Hotkeys()
{
CloseOnESC = true;
InitializeComponent();
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled && e.Key == Key.Escape)
Close();
}
}
}

View File

@@ -68,6 +68,7 @@ namespace SourceGit.Views
{
public InteractiveRebase()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -7,6 +7,7 @@ namespace SourceGit.Views
{
public LFSLocks()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -143,7 +143,7 @@ namespace SourceGit.Views
}
}
protected override void OnKeyDown(KeyEventArgs e)
protected override async void OnKeyDown(KeyEventArgs e)
{
var vm = DataContext as ViewModels.Launcher;
if (vm == null)
@@ -166,7 +166,7 @@ namespace SourceGit.Views
// Ctrl+, opens preference dialog (macOS use hotkeys in system menu bar)
if (!OperatingSystem.IsMacOS() && e is { KeyModifiers: KeyModifiers.Control, Key: Key.OemComma })
{
App.ShowWindow(new Preferences(), true);
await App.ShowDailog(new Preferences());
e.Handled = true;
return;
}
@@ -174,7 +174,7 @@ namespace SourceGit.Views
// F1 opens preference dialog (macOS use hotkeys in system menu bar)
if (!OperatingSystem.IsMacOS() && e.Key == Key.F1)
{
App.ShowWindow(new Hotkeys(), true);
await App.ShowDailog(new Hotkeys());
return;
}

View File

@@ -56,28 +56,9 @@ namespace SourceGit.Views
if (sender is ContentPresenter presenter)
{
if (presenter.DataContext is not ViewModels.Popup)
{
presenter.Content = null;
return;
}
var dataTypeName = presenter.DataContext.GetType().FullName;
if (string.IsNullOrEmpty(dataTypeName))
{
presenter.Content = null;
return;
}
var viewTypeName = dataTypeName.Replace(".ViewModels.", ".Views.");
var viewType = Type.GetType(viewTypeName);
if (viewType == null)
{
presenter.Content = null;
return;
}
var view = Activator.CreateInstance(viewType);
presenter.Content = view;
else
presenter.Content = App.CreateViewForViewModel(presenter.DataContext);
}
}
}

View File

@@ -117,6 +117,7 @@ namespace SourceGit.Views
{
var pref = ViewModels.Preferences.Instance;
DataContext = pref;
CloseOnESC = true;
if (pref.IsGitConfigured())
{
@@ -378,12 +379,12 @@ namespace SourceGit.Views
await new Commands.Config(null).SetAsync(key, value);
}
private void OnUseNativeWindowFrameChanged(object sender, RoutedEventArgs e)
private async void OnUseNativeWindowFrameChanged(object sender, RoutedEventArgs e)
{
if (sender is CheckBox box)
{
ViewModels.Preferences.Instance.UseSystemWindowFrame = box.IsChecked == true;
App.ShowWindow(new ConfirmRestart(), true);
await App.ShowDailog(new ConfirmRestart());
}
e.Handled = true;

View File

@@ -1,5 +1,4 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
@@ -9,17 +8,10 @@ namespace SourceGit.Views
{
public RepositoryConfigure()
{
CloseOnESC = true;
InitializeComponent();
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled && e.Key == Key.Escape)
Close();
}
protected override async void OnClosing(WindowClosingEventArgs e)
{
base.OnClosing(e);

View File

@@ -22,20 +22,20 @@ namespace SourceGit.Views
}
}
private void OpenStatistics(object _, RoutedEventArgs e)
private async void OpenStatistics(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo)
{
App.ShowWindow(new ViewModels.Statistics(repo.FullPath), true);
await App.ShowDailog(new ViewModels.Statistics(repo.FullPath));
e.Handled = true;
}
}
private void OpenConfigure(object sender, RoutedEventArgs e)
private async void OpenConfigure(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo)
{
App.ShowWindow(new ViewModels.RepositoryConfigure(repo), true);
await App.ShowDailog(new ViewModels.RepositoryConfigure(repo));
e.Handled = true;
}
}
@@ -143,11 +143,11 @@ namespace SourceGit.Views
e.Handled = true;
}
private void OpenGitLogs(object sender, RoutedEventArgs e)
private async void OpenGitLogs(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo)
{
App.ShowWindow(new ViewModels.ViewLogs(repo), true);
await App.ShowDailog(new ViewModels.ViewLogs(repo));
e.Handled = true;
}
}

View File

@@ -66,6 +66,7 @@ namespace SourceGit.Views
{
public SelfUpdate()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -4,6 +4,7 @@ namespace SourceGit.Views
{
public Statistics()
{
CloseOnESC = true;
InitializeComponent();
}
}

View File

@@ -7,6 +7,7 @@ namespace SourceGit.Views
{
public ViewLogs()
{
CloseOnESC = true;
InitializeComponent();
}

View File

@@ -73,7 +73,7 @@
Width="26" Height="14"
Padding="0"
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.ViewAssumeUnchanged}"
Command="{Binding OpenAssumeUnchanged}">
Click="OnOpenAssumeUnchanged">
<Path Width="14" Height="14" Data="{StaticResource Icons.File.Ignore}"/>
</Button>
<ToggleButton Grid.Column="5"

View File

@@ -26,6 +26,15 @@ namespace SourceGit.Views
layout.WorkingCopyLeftWidth = new GridLength(maxLeft, GridUnitType.Pixel);
}
private async void OnOpenAssumeUnchanged(object sender, RoutedEventArgs e)
{
var repoView = this.FindAncestorOfType<Repository>();
if (repoView is { DataContext: ViewModels.Repository repo })
await App.ShowDailog(new ViewModels.AssumeUnchangedManager(repo));
e.Handled = true;
}
private void OnUnstagedContextRequested(object sender, ContextRequestedEventArgs e)
{
if (DataContext is ViewModels.WorkingCopy vm && sender is Control control)