feature<FolderDialog>: add FolderDialog to remove dependency on System.Windows.Forms

This commit is contained in:
leo 2020-11-23 13:41:13 +08:00
parent 6bf06c87f1
commit e42290eef1
14 changed files with 336 additions and 121 deletions

View file

@ -122,6 +122,25 @@ namespace SourceGit.Helpers {
if (tree != null) tree.RemoveHandler(MultiSelectionChangedEvent, handler); if (tree != null) tree.RemoveHandler(MultiSelectionChangedEvent, handler);
} }
/// <summary>
/// Find ScrollViewer of a tree view
/// </summary>
/// <param name="owner"></param>
/// <returns></returns>
public static ScrollViewer GetScrollViewer(FrameworkElement owner) {
if (owner == null) return null;
if (owner is ScrollViewer) return owner as ScrollViewer;
int n = VisualTreeHelper.GetChildrenCount(owner);
for (int i = 0; i < n; i++) {
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
var deep = GetScrollViewer(child);
if (deep != null) return deep;
}
return null;
}
/// <summary> /// <summary>
/// Select all items in tree. /// Select all items in tree.
/// </summary> /// </summary>

View file

@ -3,7 +3,6 @@
<TargetFramework>net46</TargetFramework> <TargetFramework>net46</TargetFramework>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>App.ico</ApplicationIcon> <ApplicationIcon>App.ico</ApplicationIcon>
<Company>sourcegit</Company> <Company>sourcegit</Company>
<Description>OpenSource GIT client for Windows</Description> <Description>OpenSource GIT client for Windows</Description>

View file

@ -38,17 +38,6 @@ namespace SourceGit.UI {
} }
#region EVENTS #region EVENTS
private void SelectFolder(object sender, RoutedEventArgs e) {
var dialog = new System.Windows.Forms.FolderBrowserDialog();
dialog.Description = "Select Folder To Clone Repository";
dialog.SelectedPath = repo.Path;
dialog.ShowNewFolderButton = true;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
txtPath.Text = dialog.SelectedPath;
}
}
private async void Sure(object sender, RoutedEventArgs e) { private async void Sure(object sender, RoutedEventArgs e) {
txtRepoUrl.GetBindingExpression(TextBox.TextProperty).UpdateSource(); txtRepoUrl.GetBindingExpression(TextBox.TextProperty).UpdateSource();
if (Validation.GetHasError(txtRepoUrl)) return; if (Validation.GetHasError(txtRepoUrl)) return;

View file

@ -1,4 +1,3 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@ -46,14 +45,8 @@ namespace SourceGit.UI {
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void SelectParentFolder(object sender, RoutedEventArgs e) { private void SelectParentFolder(object sender, RoutedEventArgs e) {
var dialog = new System.Windows.Forms.FolderBrowserDialog(); var dialog = new FolderDailog("Select folder to store repository", null);
dialog.Description = "Git Repository URL"; dialog.Open(path => txtParentFolder.Text = path);
dialog.RootFolder = Environment.SpecialFolder.MyComputer;
dialog.ShowNewFolderButton = true;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
txtParentFolder.Text = dialog.SelectedPath;
}
} }
/// <summary> /// <summary>

View file

@ -297,14 +297,11 @@ namespace SourceGit.UI {
MenuItem saveAs = new MenuItem(); MenuItem saveAs = new MenuItem();
saveAs.Header = "Save As ..."; saveAs.Header = "Save As ...";
saveAs.Click += (obj, ev) => { saveAs.Click += (obj, ev) => {
var dialog = new System.Windows.Forms.FolderBrowserDialog(); var dialog = new FolderDailog("Save To ...", null);
dialog.Description = path; dialog.Open(saveTo => {
dialog.ShowNewFolderButton = true; var savePath = Path.Combine(saveTo, Path.GetFileName(path));
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
var savePath = Path.Combine(dialog.SelectedPath, Path.GetFileName(path));
commit.SaveFileTo(repo, path, savePath); commit.SaveFileTo(repo, path, savePath);
} });
}; };
menu.Items.Add(saveAs); menu.Items.Add(saveAs);
} }
@ -453,22 +450,8 @@ namespace SourceGit.UI {
} }
} }
private ScrollViewer GetScrollViewer(FrameworkElement owner) {
if (owner == null) return null;
if (owner is ScrollViewer) return owner as ScrollViewer;
int n = VisualTreeHelper.GetChildrenCount(owner);
for (int i = 0; i < n; i++) {
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
var deep = GetScrollViewer(child);
if (deep != null) return deep;
}
return null;
}
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) { private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
var scroll = GetScrollViewer(sender as TreeView); var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView);
if (scroll == null) return; if (scroll == null) return;
if (e.Delta > 0) { if (e.Delta > 0) {
@ -520,14 +503,11 @@ namespace SourceGit.UI {
saveAs.Header = "Save As ..."; saveAs.Header = "Save As ...";
saveAs.IsEnabled = node.CommitObject == null || node.CommitObject.Kind == Git.Commit.Object.Type.Blob; saveAs.IsEnabled = node.CommitObject == null || node.CommitObject.Kind == Git.Commit.Object.Type.Blob;
saveAs.Click += (obj, ev) => { saveAs.Click += (obj, ev) => {
var dialog = new System.Windows.Forms.FolderBrowserDialog(); var dialog = new FolderDailog("Save To ...", null);
dialog.Description = node.FilePath; dialog.Open(saveTo => {
dialog.ShowNewFolderButton = true; var path = Path.Combine(saveTo, node.Name);
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
var path = Path.Combine(dialog.SelectedPath, node.Name);
commit.SaveFileTo(repo, node.FilePath, path); commit.SaveFileTo(repo, node.FilePath, path);
} });
}; };
menu.Items.Add(saveAs); menu.Items.Add(saveAs);
} }

View file

@ -1054,22 +1054,8 @@ namespace SourceGit.UI {
if (item != null) item.IsSelected = false; if (item != null) item.IsSelected = false;
} }
private ScrollViewer GetScrollViewer(FrameworkElement owner) {
if (owner == null) return null;
if (owner is ScrollViewer) return owner as ScrollViewer;
int n = VisualTreeHelper.GetChildrenCount(owner);
for (int i = 0; i < n; i++) {
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
var deep = GetScrollViewer(child);
if (deep != null) return deep;
}
return null;
}
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) { private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
var scroll = GetScrollViewer(sender as TreeView); var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView);
if (scroll == null) return; if (scroll == null) return;
if (e.Delta > 0) { if (e.Delta > 0) {

View file

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;

105
src/UI/FolderDailog.xaml Normal file
View file

@ -0,0 +1,105 @@
<Window x:Class="SourceGit.UI.FolderDailog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="FolderDailog"
Height="400" Width="400"
ResizeMode="NoResize">
<!-- Enable WindowChrome Feature -->
<WindowChrome.WindowChrome>
<WindowChrome UseAeroCaptionButtons="False" CornerRadius="0" CaptionHeight="32"/>
</WindowChrome.WindowChrome>
<!-- Window Layout -->
<Border Background="{StaticResource Brush.BG1}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="32"/>
<RowDefinition Height="*"/>
<RowDefinition Height="48"/>
</Grid.RowDefinitions>
<!-- Titlebar -->
<Grid Grid.Row="0" Background="{StaticResource Brush.BG4}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- LOGO -->
<Path Width="20" Height="20" Margin="6,-1,2,0" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Info}"/>
<!-- Title -->
<Label Grid.Column="1" x:Name="txtTitle" FontWeight="Light"/>
<!-- Close Button -->
<Button Click="OnQuit" Width="32" Grid.Column="3" WindowChrome.IsHitTestVisibleInChrome="True">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource Style.Button.HighlightHover}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Close}"/>
</Button>
</Grid>
<!-- Current -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,4,0,0">
<Label Content="Selected :" />
<Label x:Name="txtSelected" Content="NONE" Foreground="{StaticResource Brush.FG2}"/>
</StackPanel>
<!-- File system tree -->
<Border Grid.Row="2" Margin="4" Background="{StaticResource Brush.BG1}" BorderBrush="{StaticResource Brush.FG2}" BorderThickness="1">
<TreeView x:Name="treePath" FontFamily="Consolas" PreviewMouseWheel="OnTreeMouseWheel" SelectedItemChanged="OnTreeSelectedChanged">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource Style.TreeView.ItemContainerStyle}">
<Setter Property="IsExpanded" Value="{Binding IsOpened, Mode=TwoWay}"/>
<EventSetter Event="Expanded" Handler="OnTreeNodeExpanded"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Height="24">
<Path x:Name="icon" Width="14" Style="{StaticResource Style.Icon}" Fill="Goldenrod" Data="{StaticResource Icon.Folder.Fill}"/>
<TextBlock Text="{Binding Name}" Foreground="{StaticResource Brush.FG}" VerticalAlignment="Center" Margin="6,0,0,0" FontSize="11"/>
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding IsOpened}" Value="True">
<Setter TargetName="icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
<!-- Operations -->
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" x:Name="btnSure" Width="100" Height="32" Content="SURE" Click="OnSure" Style="{StaticResource Style.Button.AccentBordered}"/>
<Button Grid.Column="3" Width="100" Height="32" Content="CANCEL" Click="OnQuit" Style="{StaticResource Style.Button.Bordered}"/>
</Grid>
</Grid>
</Border>
</Window>

185
src/UI/FolderDailog.xaml.cs Normal file
View file

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace SourceGit.UI {
/// <summary>
/// Interaction logic for FolderDailog.xaml
/// </summary>
public partial class FolderDailog : Window {
private Action<string> cb = null;
private Node root = new Node("", "");
private Node selected = null;
/// <summary>
/// Tree node.
/// </summary>
public class Node : INotifyPropertyChanged {
public string Name { get; set; }
public string Path { get; set; }
public bool IsOpened { get; set; }
public ObservableCollection<Node> Children { get; set; }
/// <summary>
/// INotifyPropertyChanged.PropertyChanged
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="path"></param>
/// <param name="isOpen"></param>
public Node(string name, string path) {
Name = name;
Path = path;
IsOpened = false;
Children = new ObservableCollection<Node>();
}
/// <summary>
/// Collect children.
/// </summary>
public void CollectChildren() {
Children.Clear();
try {
var dir = new DirectoryInfo(Path);
var subs = dir.GetDirectories();
foreach (var sub in subs) {
if ((sub.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
Children.Add(new Node(sub.Name, sub.FullName));
}
} catch {}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Children"));
}
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="title"></param>
/// <param name="initPath"></param>
public FolderDailog(string title, string initPath) {
InitializeComponent();
// Move to center.
var parent = App.Current.MainWindow;
Left = parent.Left + (parent.Width - Width) * 0.5;
Top = parent.Top + (parent.Height - Height) * 0.5;
var drives = DriveInfo.GetDrives();
foreach (var drive in drives) {
var node = new Node(drive.Name, drive.Name);
node.CollectChildren();
if (initPath != null && initPath.StartsWith(drive.Name)) InitializePath(node, initPath);
root.Children.Add(node);
}
txtTitle.Content = title.ToUpper();
treePath.ItemsSource = root.Children;
if (selected != null) {
Helpers.TreeViewHelper.SelectOneByContext(treePath, selected);
} else {
btnSure.IsEnabled = false;
}
}
/// <summary>
/// Set callbacks on click OK.
/// </summary>
/// <param name="onOK"></param>
public void Open(Action<string> onOK) {
cb = onOK;
Show();
}
/// <summary>
/// Initialize given path.
/// </summary>
/// <param name="parent"></param>
/// <param name="initPath"></param>
private void InitializePath(Node parent, string initPath) {
foreach (var child in parent.Children) {
child.CollectChildren();
if (child.Path == initPath) {
selected = child;
} else if (initPath.StartsWith(child.Path)) {
InitializePath(child, initPath);
}
}
}
#region EVENTS
private void OnSure(object sender, RoutedEventArgs e) {
if (selected != null) cb?.Invoke(selected.Path);
Close();
}
private void OnQuit(object sender, RoutedEventArgs e) {
Close();
}
private void OnTreeMouseWheel(object sender, MouseWheelEventArgs e) {
var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView);
if (scroll == null) return;
if (e.Delta > 0) {
scroll.LineUp();
} else {
scroll.LineDown();
}
e.Handled = true;
}
private void OnTreeSelectedChanged(object sender, RoutedPropertyChangedEventArgs<object> e) {
selected = treePath.SelectedItem as Node;
if (selected != null) {
btnSure.IsEnabled = true;
txtSelected.Content = selected.Path;
} else {
btnSure.IsEnabled = false;
txtSelected.Content = "NONE";
}
e.Handled = true;
}
private void OnTreeNodeExpanded(object sender, RoutedEventArgs e) {
var item = sender as TreeViewItem;
if (item == null) return;
var node = item.DataContext as Node;
if (node == null) return;
foreach (var c in node.Children) {
c.CollectChildren();
}
e.Handled = true;
}
#endregion
}
}

View file

@ -586,12 +586,10 @@ namespace SourceGit.UI {
var patch = new MenuItem(); var patch = new MenuItem();
patch.Header = "Save As Patch"; patch.Header = "Save As Patch";
patch.Click += (o, e) => { patch.Click += (o, e) => {
var dialog = new System.Windows.Forms.FolderBrowserDialog(); var dialog = new FolderDailog("Save To ...", null);
dialog.ShowNewFolderButton = true; dialog.Open(saveTo => {
Repo.RunCommand($"format-patch {commit.SHA} -1 -o \"{saveTo}\"", null);
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { });
Repo.RunCommand($"format-patch {commit.SHA} -1 -o \"{dialog.SelectedPath}\"", null);
}
}; };
menu.Items.Add(patch); menu.Items.Add(patch);
menu.Items.Add(new Separator()); menu.Items.Add(new Separator());

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -45,14 +44,10 @@ namespace SourceGit.UI {
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void OpenOrAddRepo(object sender, RoutedEventArgs e) { private void OpenOrAddRepo(object sender, RoutedEventArgs e) {
var dialog = new System.Windows.Forms.FolderBrowserDialog(); var dialog = new FolderDailog("Open or init local repository", null);
dialog.Description = "Open or init local repository"; dialog.Open(path => {
dialog.RootFolder = Environment.SpecialFolder.MyComputer; CheckAndOpenRepo(path);
dialog.ShowNewFolderButton = true; });
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
CheckAndOpenRepo(dialog.SelectedPath);
}
} }
/// <summary> /// <summary>

View file

@ -116,15 +116,11 @@ namespace SourceGit.UI {
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
private void SelectDefaultClonePath(object sender, RoutedEventArgs e) { private void SelectDefaultClonePath(object sender, RoutedEventArgs e) {
var dialog = new System.Windows.Forms.FolderBrowserDialog(); var dialog = new FolderDailog("Select Folder To Clone Repository Into As Default", null);
dialog.Description = "Select Folder To Clone Repository Into As Default"; dialog.Open(path => {
dialog.RootFolder = Environment.SpecialFolder.MyComputer; txtGitCloneDir.Text = path;
dialog.ShowNewFolderButton = true; App.Preference.GitDefaultCloneDir = path;
});
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
txtGitCloneDir.Text = dialog.SelectedPath;
App.Preference.GitDefaultCloneDir = dialog.SelectedPath;
}
} }
/// <summary> /// <summary>

View file

@ -5,7 +5,6 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media;
namespace SourceGit.UI { namespace SourceGit.UI {
@ -233,22 +232,8 @@ namespace SourceGit.UI {
} }
} }
private ScrollViewer GetScrollViewer(FrameworkElement owner) {
if (owner == null) return null;
if (owner is ScrollViewer) return owner as ScrollViewer;
int n = VisualTreeHelper.GetChildrenCount(owner);
for (int i = 0; i < n; i++) {
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
var deep = GetScrollViewer(child);
if (deep != null) return deep;
}
return null;
}
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) { private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
var scroll = GetScrollViewer(sender as TreeView); var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView);
if (scroll == null) return; if (scroll == null) return;
if (e.Delta > 0) { if (e.Delta > 0) {

View file

@ -1043,23 +1043,9 @@ namespace SourceGit.UI {
if (sub.Children.Count > 0) SortTreeNodes(sub.Children); if (sub.Children.Count > 0) SortTreeNodes(sub.Children);
} }
} }
private ScrollViewer GetScrollViewer(FrameworkElement owner) {
if (owner == null) return null;
if (owner is ScrollViewer) return owner as ScrollViewer;
int n = VisualTreeHelper.GetChildrenCount(owner);
for (int i = 0; i < n; i++) {
var child = VisualTreeHelper.GetChild(owner, i) as FrameworkElement;
var deep = GetScrollViewer(child);
if (deep != null) return deep;
}
return null;
}
private void TreeMouseWheel(object sender, MouseWheelEventArgs e) { private void TreeMouseWheel(object sender, MouseWheelEventArgs e) {
var scroll = GetScrollViewer(sender as TreeView); var scroll = Helpers.TreeViewHelper.GetScrollViewer(sender as TreeView);
if (scroll == null) return; if (scroll == null) return;
if (e.Delta > 0) { if (e.Delta > 0) {