feature<Welcome>: 欢迎界面增加最近使用记录和收藏/标签列表。支持拖拽,收藏支持自定义目录

This commit is contained in:
ZCShou 2021-09-09 17:26:29 +08:00
parent b6532c063d
commit f26c777577
4 changed files with 416 additions and 216 deletions

View file

@ -298,8 +298,8 @@
<sys:String x:Key="Text.Welcome.Title">SourceGit</sys:String> <sys:String x:Key="Text.Welcome.Title">SourceGit</sys:String>
<sys:String x:Key="Text.Welcome.OpenOrInit">Open Local Repository</sys:String> <sys:String x:Key="Text.Welcome.OpenOrInit">Open Local Repository</sys:String>
<sys:String x:Key="Text.Welcome.Clone">Clone Remote Repository</sys:String> <sys:String x:Key="Text.Welcome.Clone">Clone Remote Repository</sys:String>
<sys:String x:Key="Text.Welcome.Repositories">REPOSITORIES</sys:String> <sys:String x:Key="Text.Welcome.Bookmarks">Bookmarks</sys:String>
<sys:String x:Key="Text.Welcome.DragDrop">DRAG-DROP YOUR FOLDER</sys:String> <sys:String x:Key="Text.Welcome.Histories">Histories</sys:String>
<sys:String x:Key="Text.Welcome.NewFolder">Add Folder</sys:String> <sys:String x:Key="Text.Welcome.NewFolder">Add Folder</sys:String>
<sys:String x:Key="Text.Welcome.NewSubFolder">Add Sub-Folder</sys:String> <sys:String x:Key="Text.Welcome.NewSubFolder">Add Sub-Folder</sys:String>
<sys:String x:Key="Text.Welcome.Rename">Rename</sys:String> <sys:String x:Key="Text.Welcome.Rename">Rename</sys:String>

View file

@ -297,8 +297,8 @@
<sys:String x:Key="Text.Welcome.Title">欢迎使用本软件</sys:String> <sys:String x:Key="Text.Welcome.Title">欢迎使用本软件</sys:String>
<sys:String x:Key="Text.Welcome.OpenOrInit">打开本地仓库</sys:String> <sys:String x:Key="Text.Welcome.OpenOrInit">打开本地仓库</sys:String>
<sys:String x:Key="Text.Welcome.Clone">克隆远程仓库</sys:String> <sys:String x:Key="Text.Welcome.Clone">克隆远程仓库</sys:String>
<sys:String x:Key="Text.Welcome.Repositories">仓库列表</sys:String> <sys:String x:Key="Text.Welcome.Bookmarks">收藏/书签</sys:String>
<sys:String x:Key="Text.Welcome.DragDrop">支持拖放操作</sys:String> <sys:String x:Key="Text.Welcome.Histories">最近使用</sys:String>
<sys:String x:Key="Text.Welcome.NewFolder">新建分组</sys:String> <sys:String x:Key="Text.Welcome.NewFolder">新建分组</sys:String>
<sys:String x:Key="Text.Welcome.NewSubFolder">新建子分组</sys:String> <sys:String x:Key="Text.Welcome.NewSubFolder">新建子分组</sys:String>
<sys:String x:Key="Text.Welcome.Rename">重命名</sys:String> <sys:String x:Key="Text.Welcome.Rename">重命名</sys:String>

View file

@ -7,23 +7,22 @@
xmlns:widgets="clr-namespace:SourceGit.Views.Widgets" xmlns:widgets="clr-namespace:SourceGit.Views.Widgets"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="800"> d:DesignHeight="800" d:DesignWidth="800">
<Grid Background="Transparent" AllowDrop="True" DragEnter="OnPageDragEnter" DragLeave="OnPageDragLeave" Drop="OnPageDrop"> <Grid Background="Transparent" AllowDrop="True" Drop="OnPageDrop" MouseMove="OnPageMouseMove" MouseDown="OnPageMouseDown">
<Grid Width="420"> <Grid.RowDefinitions>
<Grid.RowDefinitions> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> </Grid.RowDefinitions>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical" Width="420" HorizontalAlignment="Center"> <StackPanel Grid.Row="0" Orientation="Vertical" Width="420" HorizontalAlignment="Center">
<!-- Logo --> <!-- Logo -->
<Path <Path
Margin="0,48,0,0" Margin="0,48,0,0"
Width="100" Height="100" Width="100" Height="100"
Data="{StaticResource Icon.Git}" Data="{StaticResource Icon.Git}"
Fill="{DynamicResource Brush.Logo}"/> Fill="{DynamicResource Brush.Logo}"/>
<!-- Welcome --> <!-- Welcome -->
<TextBlock <TextBlock
Margin="0,16" Margin="0,16"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Text="{DynamicResource Text.Welcome.Title}" Text="{DynamicResource Text.Welcome.Title}"
@ -33,57 +32,106 @@
TextOptions.TextRenderingMode="ClearType" TextOptions.TextRenderingMode="ClearType"
Foreground="{DynamicResource Brush.FG2}"/> Foreground="{DynamicResource Brush.FG2}"/>
<!-- Options --> <!-- Options -->
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/> <ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" Click="OnOpenClicked" Height="28"> <Button Grid.Column="0" Click="OnOpenClicked" Height="28">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Path Width="16" Height="16" Data="{StaticResource Icon.Folder.Open}"/> <Path Width="16" Height="16" Data="{StaticResource Icon.Folder.Open}"/>
<TextBlock Margin="12,0,0,0" Text="{DynamicResource Text.Welcome.OpenOrInit}"/> <TextBlock Margin="12,0,0,0" Text="{DynamicResource Text.Welcome.OpenOrInit}"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button Grid.Column="2" Click="OnCloneClicked" Height="28"> <Button Grid.Column="2" Click="OnCloneClicked" Height="28">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Path Width="16" Height="16" Data="{StaticResource Icon.Pull}"/> <Path Width="16" Height="16" Data="{StaticResource Icon.Pull}"/>
<TextBlock Margin="12,0,0,0" Text="{DynamicResource Text.Welcome.Clone}"/> <TextBlock Margin="12,0,0,0" Text="{DynamicResource Text.Welcome.Clone}"/>
</StackPanel> </StackPanel>
</Button> </Button>
</Grid> </Grid>
</StackPanel>
<!-- Horizontal line --> <Grid Grid.Row="1" Margin="80,80">
<Rectangle Height="1" Margin="0,36,0,8" Fill="{DynamicResource Brush.Border1}"/> <Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Labels --> <Grid.ColumnDefinitions>
<Grid> <ColumnDefinition Width="*"/>
<Grid.ColumnDefinitions> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock <!-- History -->
Grid.Column="0" <TextBlock
Text="{DynamicResource Text.Welcome.Repositories}" Grid.Row="0"
FontSize="13pt" FontWeight="ExtraBold" Grid.Column="0"
Foreground="{DynamicResource Brush.FG2}"/> Text="{DynamicResource Text.Welcome.Histories}"
FontSize="13pt" FontWeight="ExtraBold"
HorizontalAlignment="Center"
Foreground="{DynamicResource Brush.FG2}"/>
<!-- History Tree -->
<controls:Tree
Grid.Row="1"
Grid.Column="0"
x:Name="treeHistory"
Margin="0,4"
TextElement.FontSize="14"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ContextMenuOpening="OnTreeContextMenuOpening">
<controls:Tree.ItemContainerStyle>
<Style TargetType="{x:Type controls:TreeItem}" BasedOn="{StaticResource Style.TreeItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnTreeNodeDoubleClick"/>
</Style>
</controls:Tree.ItemContainerStyle>
<TextBlock <controls:Tree.ItemTemplate>
Grid.Column="2" <HierarchicalDataTemplate ItemsSource="{Binding Children}">
Text="{DynamicResource Text.Welcome.DragDrop}" <Border Height="32">
FontSize="10pt" <Grid IsHitTestVisible="False">
Foreground="{DynamicResource Brush.FG2}"/> <Grid.ColumnDefinitions>
</Grid> <ColumnDefinition Width="Auto"/>
</StackPanel> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="22"/>
</Grid.ColumnDefinitions>
<controls:Bookmark
Grid.Column="0"
Margin="2,0,0,0"
x:Name="BookmarkIcon"
Width="16" Height="16"
Color="{Binding Bookmark}"
IsNewPage="False"/>
<StackPanel Grid.Column="1" x:Name="ContentsHistory" Orientation="Horizontal">
<TextBlock Margin="8,0" Text="{Binding Name}"/>
<TextBlock x:Name="Path" Text="{Binding Id}" Foreground="{DynamicResource Brush.FG2}"/>
</StackPanel>
</Grid>
</Border>
</HierarchicalDataTemplate>
</controls:Tree.ItemTemplate>
</controls:Tree>
<!-- Bookmark -->
<TextBlock
Grid.Row="0"
Grid.Column="1"
Text="{DynamicResource Text.Welcome.Bookmarks}"
FontSize="13pt" FontWeight="ExtraBold"
HorizontalAlignment="Center"
Foreground="{DynamicResource Brush.FG2}"/>
<!-- Drop Area --> <!-- Drop Area -->
<Rectangle <Rectangle
Grid.Row="1" Grid.Row="1"
Grid.Column="1"
x:Name="dropArea" x:Name="dropArea"
Margin="0,2" Margin="0,2"
Stroke="{DynamicResource Brush.Border1}" Stroke="{DynamicResource Brush.Border1}"
@ -95,16 +143,18 @@
<!-- Tree --> <!-- Tree -->
<controls:Tree <controls:Tree
Grid.Row="1" Grid.Row="1"
x:Name="tree" Grid.Column="1"
Margin="2,4" x:Name="treeBookmarks"
Margin="0,4"
AllowDrop="True" AllowDrop="True"
TextElement.FontSize="14" TextElement.FontSize="14"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto"
ContextMenuOpening="OnTreeContextMenuOpening" ContextMenuOpening="OnTreeContextMenuOpening"
MouseMove="OnTreeMouseMove" DragEnter="OnTreeBookmarksDragEnter"
DragOver="OnTreeDragOver" DragLeave="OnTreeBookmarksDragLeave"
Drop="OnTreeDrop"> DragOver="OnTreeBookmarksDragOver"
Drop="OnTreeBookmarksDrop">
<controls:Tree.ItemContainerStyle> <controls:Tree.ItemContainerStyle>
<Style TargetType="{x:Type controls:TreeItem}" BasedOn="{StaticResource Style.TreeItem}"> <Style TargetType="{x:Type controls:TreeItem}" BasedOn="{StaticResource Style.TreeItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
@ -135,14 +185,14 @@
Color="{Binding Bookmark}" Color="{Binding Bookmark}"
IsNewPage="False"/> IsNewPage="False"/>
<StackPanel Grid.Column="1" x:Name="Contents" Orientation="Horizontal"> <StackPanel Grid.Column="1" x:Name="ContentsBookmark" Orientation="Horizontal">
<TextBlock Margin="8,0" Text="{Binding Name}"/> <TextBlock Margin="8,0" Text="{Binding Name}"/>
<TextBlock x:Name="Path" Text="{Binding Id}" Foreground="{DynamicResource Brush.FG2}"/> <TextBlock x:Name="Path" Text="{Binding Id}" Foreground="{DynamicResource Brush.FG2}"/>
</StackPanel> </StackPanel>
<controls:TextEdit <controls:TextEdit
Grid.Column="1" Grid.Column="1"
x:Name="Editor" x:Name="EditorBookmarks"
Height="20" Height="20"
Margin="4,0,0,0" Margin="4,0,0,0"
Text="{Binding Name}" Text="{Binding Name}"
@ -170,13 +220,14 @@
<Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/> <Setter TargetName="Icon" Property="Data" Value="{StaticResource Icon.Folder.Open}"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding IsEditing}" Value="True"> <DataTrigger Binding="{Binding IsEditing}" Value="True">
<Setter TargetName="Editor" Property="Visibility" Value="Visible"/> <Setter TargetName="EditorBookmarks" Property="Visibility" Value="Visible"/>
<Setter TargetName="Contents" Property="Visibility" Value="Collapsed"/> <Setter TargetName="ContentsBookmark" Property="Visibility" Value="Collapsed"/>
</DataTrigger> </DataTrigger>
</HierarchicalDataTemplate.Triggers> </HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate> </HierarchicalDataTemplate>
</controls:Tree.ItemTemplate> </controls:Tree.ItemTemplate>
</controls:Tree> </controls:Tree>
</Grid> </Grid>
<!-- Popup --> <!-- Popup -->

View file

@ -19,8 +19,14 @@ namespace SourceGit.Views.Widgets {
/// 树节点数据 /// 树节点数据
/// </summary> /// </summary>
public class Node : Controls.BindableBase { public class Node : Controls.BindableBase {
public string Id { get; set; } public string Id {
public string ParentId { get; set; } get;
set;
}
public string ParentId {
get;
set;
}
private string name; private string name;
public string Name { public string Name {
@ -28,7 +34,10 @@ namespace SourceGit.Views.Widgets {
set => SetProperty(ref name, value); set => SetProperty(ref name, value);
} }
public bool IsGroup { get; set; } public bool IsGroup {
get;
set;
}
private bool isEditing = false; private bool isEditing = false;
public bool IsEditing { public bool IsEditing {
@ -36,7 +45,10 @@ namespace SourceGit.Views.Widgets {
set => SetProperty(ref isEditing, value); set => SetProperty(ref isEditing, value);
} }
public bool IsExpanded { get; set; } public bool IsExpanded {
get;
set;
}
private int bookmark = 0; private int bookmark = 0;
public int Bookmark { public int Bookmark {
@ -44,7 +56,10 @@ namespace SourceGit.Views.Widgets {
set => SetProperty(ref bookmark, value); set => SetProperty(ref bookmark, value);
} }
public List<Node> Children { get; set; } public List<Node> Children {
get;
set;
}
} }
/// <summary> /// <summary>
@ -52,6 +67,8 @@ namespace SourceGit.Views.Widgets {
/// </summary> /// </summary>
public event Action<Node> OnNodeEdited; public event Action<Node> OnNodeEdited;
private bool clearBookmark = false;
public Welcome() { public Welcome() {
InitializeComponent(); InitializeComponent();
UpdateTree(); UpdateTree();
@ -74,15 +91,21 @@ namespace SourceGit.Views.Widgets {
#region FUNC_EVENTS #region FUNC_EVENTS
private void OnOpenClicked(object sender, RoutedEventArgs e) { private void OnOpenClicked(object sender, RoutedEventArgs e) {
var dialog = new Controls.FolderDialog(); var dialog = new Controls.FolderDialog();
if (dialog.ShowDialog() == true) CheckAndOpen(dialog.SelectedPath);
if (dialog.ShowDialog() == true) {
CheckAndOpen(dialog.SelectedPath);
}
} }
private void OnCloneClicked(object sender, RoutedEventArgs e) { private void OnCloneClicked(object sender, RoutedEventArgs e) {
if (MakeSureReady()) new Popups.Clone().Show(); if (MakeSureReady()) {
new Popups.Clone().Show();
}
} }
private void OnTreeNodeStatusChange(object sender, RoutedEventArgs e) { private void OnTreeNodeStatusChange(object sender, RoutedEventArgs e) {
var node = (sender as Controls.TreeItem).DataContext as Node; var node = (sender as Controls.TreeItem).DataContext as Node;
if (node != null) { if (node != null) {
var group = Models.Preference.Instance.FindGroup(node.Id); var group = Models.Preference.Instance.FindGroup(node.Id);
group.IsExpanded = node.IsExpanded; group.IsExpanded = node.IsExpanded;
@ -92,6 +115,7 @@ namespace SourceGit.Views.Widgets {
private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs e) { private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs e) {
var node = (sender as Controls.TreeItem).DataContext as Node; var node = (sender as Controls.TreeItem).DataContext as Node;
if (node != null && !node.IsGroup) { if (node != null && !node.IsGroup) {
CheckAndOpen(node.Id); CheckAndOpen(node.Id);
e.Handled = true; e.Handled = true;
@ -99,7 +123,8 @@ namespace SourceGit.Views.Widgets {
} }
private void OnTreeContextMenuOpening(object sender, ContextMenuEventArgs e) { private void OnTreeContextMenuOpening(object sender, ContextMenuEventArgs e) {
var item = tree.FindItem(e.OriginalSource as DependencyObject); var item = treeHistory.FindItem(e.OriginalSource as DependencyObject);
if (item == null) { if (item == null) {
var addFolder = new MenuItem(); var addFolder = new MenuItem();
addFolder.Header = App.Text("Welcome.NewFolder"); addFolder.Header = App.Text("Welcome.NewFolder");
@ -108,16 +133,20 @@ namespace SourceGit.Views.Widgets {
UpdateTree(group.Id); UpdateTree(group.Id);
ev.Handled = true; ev.Handled = true;
}; };
var menu = new ContextMenu(); var menu = new ContextMenu();
menu.Items.Add(addFolder); menu.Items.Add(addFolder);
menu.IsOpen = true; menu.IsOpen = true;
e.Handled = true; e.Handled = true;
} else { }
else {
var node = item.DataContext as Node; var node = item.DataContext as Node;
if (node == null) return;
if (node == null) {
return;
}
var menu = new ContextMenu(); var menu = new ContextMenu();
if (!node.IsGroup) { if (!node.IsGroup) {
var open = new MenuItem(); var open = new MenuItem();
open.Header = App.Text("RepoCM.Open"); open.Header = App.Text("RepoCM.Open");
@ -125,56 +154,56 @@ namespace SourceGit.Views.Widgets {
CheckAndOpen(node.Id); CheckAndOpen(node.Id);
ev.Handled = true; ev.Handled = true;
}; };
var explore = new MenuItem(); var explore = new MenuItem();
explore.Header = App.Text("RepoCM.Explore"); explore.Header = App.Text("RepoCM.Explore");
explore.Click += (o, ev) => { explore.Click += (o, ev) => {
Process.Start("explorer", node.Id); Process.Start("explorer", node.Id);
ev.Handled = true; ev.Handled = true;
}; };
var iconBookmark = FindResource("Icon.Bookmark") as Geometry; var iconBookmark = FindResource("Icon.Bookmark") as Geometry;
var bookmark = new MenuItem(); var bookmark = new MenuItem();
bookmark.Header = App.Text("RepoCM.Bookmark"); bookmark.Header = App.Text("RepoCM.Bookmark");
for (int i = 0; i < Controls.Bookmark.COLORS.Length; i++) { for (int i = 0; i < Controls.Bookmark.COLORS.Length; i++) {
var icon = new System.Windows.Shapes.Path(); var icon = new System.Windows.Shapes.Path();
icon.Data = iconBookmark; icon.Data = iconBookmark;
icon.Fill = Controls.Bookmark.COLORS[i]; icon.Fill = Controls.Bookmark.COLORS[i];
icon.Width = 8; icon.Width = 8;
var mark = new MenuItem(); var mark = new MenuItem();
mark.Icon = icon; mark.Icon = icon;
mark.Header = $"{i}"; mark.Header = $"{i}";
var refIdx = i; var refIdx = i;
mark.Click += (o, ev) => { mark.Click += (o, ev) => {
var repo = Models.Preference.Instance.FindRepository(node.Id); var repo = Models.Preference.Instance.FindRepository(node.Id);
if (repo != null) { if (repo != null) {
repo.Bookmark = refIdx; repo.Bookmark = refIdx;
node.Bookmark = refIdx; UpdateTree();
OnNodeEdited?.Invoke(node);
} }
ev.Handled = true; ev.Handled = true;
}; };
bookmark.Items.Add(mark); bookmark.Items.Add(mark);
} }
menu.Items.Add(open); menu.Items.Add(open);
menu.Items.Add(explore); menu.Items.Add(explore);
menu.Items.Add(bookmark); menu.Items.Add(bookmark);
} else { }
else {
var addSubFolder = new MenuItem(); var addSubFolder = new MenuItem();
addSubFolder.Header = App.Text("Welcome.NewSubFolder"); addSubFolder.Header = App.Text("Welcome.NewSubFolder");
addSubFolder.Click += (o, ev) => { addSubFolder.Click += (o, ev) => {
var parent = Models.Preference.Instance.FindGroup(node.Id); var parent = Models.Preference.Instance.FindGroup(node.Id);
if (parent != null) parent.IsExpanded = true;
if (parent != null) {
parent.IsExpanded = true;
}
var group = Models.Preference.Instance.AddGroup("New Group", node.Id); var group = Models.Preference.Instance.AddGroup("New Group", node.Id);
UpdateTree(group.Id); UpdateTree(group.Id);
ev.Handled = true; ev.Handled = true;
}; };
menu.Items.Add(addSubFolder); menu.Items.Add(addSubFolder);
} }
@ -184,14 +213,12 @@ namespace SourceGit.Views.Widgets {
UpdateTree(node.Id); UpdateTree(node.Id);
ev.Handled = true; ev.Handled = true;
}; };
var delete = new MenuItem(); var delete = new MenuItem();
delete.Header = App.Text("Welcome.Delete"); delete.Header = App.Text("Welcome.Delete");
delete.Click += (o, ev) => { delete.Click += (o, ev) => {
DeleteNode(node); DeleteNode(node);
ev.Handled = true; ev.Handled = true;
}; };
menu.Items.Add(rename); menu.Items.Add(rename);
menu.Items.Add(delete); menu.Items.Add(delete);
menu.IsOpen = true; menu.IsOpen = true;
@ -201,82 +228,176 @@ namespace SourceGit.Views.Widgets {
#endregion #endregion
#region DRAP_DROP_EVENTS #region DRAP_DROP_EVENTS
private void OnPageDragEnter(object sender, DragEventArgs e) { private void OnPageMouseDown(object sender, MouseButtonEventArgs e) {
if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(typeof(Node))) { var itemHistory = treeHistory.FindItem(e.OriginalSource as DependencyObject);
dropArea.Visibility = Visibility.Visible;
if (itemHistory == null) {
treeHistory.UnselectAll();
} }
var itemBookmark = treeBookmarks.FindItem(e.OriginalSource as DependencyObject);
if (itemBookmark == null) {
treeBookmarks.UnselectAll();
}
clearBookmark = false;
} }
private void OnPageDragLeave(object sender, DragEventArgs e) { private void OnPageMouseMove(object sender, MouseEventArgs e) {
dropArea.Visibility = Visibility.Hidden; if (e.LeftButton != MouseButtonState.Pressed) {
} return;
}
private void OnPageDrop(object sender, DragEventArgs e) { var item = treeHistory.FindItem(e.OriginalSource as DependencyObject);
dropArea.Visibility = Visibility.Hidden;
}
private void OnTreeMouseMove(object sender, MouseEventArgs e) { if (item == null) {
if (e.LeftButton != MouseButtonState.Pressed) return; return;
}
var item = tree.FindItem(e.OriginalSource as DependencyObject);
if (item == null) return;
tree.UnselectAll();
treeHistory.UnselectAll();
var adorner = new Controls.DragDropAdorner(item); var adorner = new Controls.DragDropAdorner(item);
DragDrop.DoDragDrop(item, item.DataContext, DragDropEffects.Move); DragDrop.DoDragDrop(item, item.DataContext, DragDropEffects.Move);
adorner.Remove(); adorner.Remove();
} }
private void OnTreeDragOver(object sender, DragEventArgs e) { private void OnPageDrop(object sender, DragEventArgs e) {
if (!e.Data.GetDataPresent(DataFormats.FileDrop) && !e.Data.GetDataPresent(typeof(Node))) return; bool rebuild = false;
var item = tree.FindItem(e.OriginalSource as DependencyObject); if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
if (item == null) return; if (!MakeSureReady()) {
return;
}
var paths = e.Data.GetData(DataFormats.FileDrop) as string[];
foreach (var path in paths) {
var dir = new Commands.QueryGitDir(path).Result();
if (dir != null) {
var root = new Commands.GetRepositoryRootPath(path).Result();
Models.Preference.Instance.AddRepository(root, dir, "");
CheckAndOpen(path);
rebuild = true;
}
}
}
else if (e.Data.GetDataPresent(typeof(Node))) {
var node = e.Data.GetData(typeof(Node)) as Node;
if (node.IsGroup) {
e.Handled = true;
return;
}
else {
var repo = Models.Preference.Instance.FindRepository(node.Id);
if (repo != null && repo.Bookmark != 0 && clearBookmark) {
repo.Bookmark = 0;
}
clearBookmark = false;
rebuild = true;
}
}
if (rebuild) {
UpdateTree();
}
var node = item.DataContext as Node;
if (node.IsGroup && !item.IsExpanded) item.IsExpanded = true;
e.Handled = true; e.Handled = true;
} }
private void OnTreeDrop(object sender, DragEventArgs e) { private void OnTreeBookmarksDragEnter(object sender, DragEventArgs e) {
bool rebuild = false; if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(typeof(Node))) {
dropArea.Visibility = Visibility.Hidden; dropArea.Visibility = Visibility.Visible;
}
}
private void OnTreeBookmarksDragLeave(object sender, DragEventArgs e) {
dropArea.Visibility = Visibility.Hidden;
}
private void OnTreeBookmarksDragOver(object sender, DragEventArgs e) {
if (!e.Data.GetDataPresent(DataFormats.FileDrop) && !e.Data.GetDataPresent(typeof(Node))) {
return;
}
var item = treeBookmarks.FindItem(e.OriginalSource as DependencyObject);
if (item == null) {
return;
}
var node = item.DataContext as Node;
if (node.IsGroup && !item.IsExpanded) {
item.IsExpanded = true;
}
clearBookmark = true;
e.Handled = true;
}
private void OnTreeBookmarksDrop(object sender, DragEventArgs e) {
bool rebuild = false;
var parent = ""; var parent = "";
var to = tree.FindItem(e.OriginalSource as DependencyObject);
clearBookmark = false;
dropArea.Visibility = Visibility.Hidden;
var to = treeBookmarks.FindItem(e.OriginalSource as DependencyObject);
if (to != null) { if (to != null) {
var dst = to.DataContext as Node; var dst = to.DataContext as Node;
parent = dst.IsGroup ? dst.Id : dst.ParentId; parent = dst.IsGroup ? dst.Id : dst.ParentId;
} }
if (e.Data.GetDataPresent(DataFormats.FileDrop)) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
if (!MakeSureReady()) return; if (!MakeSureReady()) {
return;
}
var paths = e.Data.GetData(DataFormats.FileDrop) as string[]; var paths = e.Data.GetData(DataFormats.FileDrop) as string[];
foreach (var path in paths) { foreach (var path in paths) {
var dir = new Commands.QueryGitDir(path).Result(); var dir = new Commands.QueryGitDir(path).Result();
if (dir != null) { if (dir != null) {
var root = new Commands.GetRepositoryRootPath(path).Result(); var root = new Commands.GetRepositoryRootPath(path).Result();
Models.Preference.Instance.AddRepository(root, dir, parent); Models.Preference.Instance.AddRepository(root, dir, parent).Bookmark = 1; // 默认添加的标签;
CheckAndOpen(path); // CheckAndOpen(path);
rebuild = true; rebuild = true;
} }
} }
} else if (e.Data.GetDataPresent(typeof(Node))) { }
var src = e.Data.GetData(typeof(Node)) as Node; else if (e.Data.GetDataPresent(typeof(Node))) {
if (src.IsGroup) { var node = e.Data.GetData(typeof(Node)) as Node;
if (!Models.Preference.Instance.IsSubGroup(src.Id, parent)) {
Models.Preference.Instance.FindGroup(src.Id).Parent = parent; if (node.IsGroup) {
if (!Models.Preference.Instance.IsSubGroup(node.Id, parent)) {
Models.Preference.Instance.FindGroup(node.Id).Parent = parent;
rebuild = true; rebuild = true;
} }
} else { }
Models.Preference.Instance.FindRepository(src.Id).GroupId = parent; else {
var repo = Models.Preference.Instance.FindRepository(node.Id);
if (repo != null) {
repo.GroupId = parent;
if (repo.Bookmark == 0) {
repo.Bookmark = 1;
}
}
rebuild = true; rebuild = true;
} }
} }
if (rebuild) UpdateTree(); if (rebuild) {
UpdateTree();
}
e.Handled = true; e.Handled = true;
} }
#endregion #endregion
@ -284,7 +405,8 @@ namespace SourceGit.Views.Widgets {
#region DATA #region DATA
private void UpdateTree(string editingNodeId = null) { private void UpdateTree(string editingNodeId = null) {
var groupNodes = new Dictionary<string, Node>(); var groupNodes = new Dictionary<string, Node>();
var nodes = new List<Node>(); var nodesHistory = new List<Node>();
var nodesBookmarks = new List<Node>();
foreach (var group in Models.Preference.Instance.Groups) { foreach (var group in Models.Preference.Instance.Groups) {
Node node = new Node() { Node node = new Node() {
@ -297,17 +419,15 @@ namespace SourceGit.Views.Widgets {
Bookmark = 0, Bookmark = 0,
Children = new List<Node>(), Children = new List<Node>(),
}; };
groupNodes.Add(node.Id, node); groupNodes.Add(node.Id, node);
} }
nodes.Clear();
foreach (var kv in groupNodes) { foreach (var kv in groupNodes) {
if (groupNodes.ContainsKey(kv.Value.ParentId)) { if (groupNodes.ContainsKey(kv.Value.ParentId)) {
groupNodes[kv.Value.ParentId].Children.Add(kv.Value); groupNodes[kv.Value.ParentId].Children.Add(kv.Value);
} else { }
nodes.Add(kv.Value); else {
nodesBookmarks.Add(kv.Value);
} }
} }
@ -322,21 +442,29 @@ namespace SourceGit.Views.Widgets {
Bookmark = repo.Bookmark, Bookmark = repo.Bookmark,
Children = new List<Node>(), Children = new List<Node>(),
}; };
nodesHistory.Add(node);
if (groupNodes.ContainsKey(repo.GroupId)) { if (repo.Bookmark != 0) {
groupNodes[repo.GroupId].Children.Add(node); if (groupNodes.ContainsKey(repo.GroupId)) {
} else { groupNodes[repo.GroupId].Children.Add(node);
nodes.Add(node); }
else {
nodesBookmarks.Add(node);
}
} }
OnNodeEdited?.Invoke(node);
} }
tree.ItemsSource = nodes; treeHistory.ItemsSource = nodesHistory;
treeBookmarks.ItemsSource = nodesBookmarks;
} }
private void DeleteNode(Node node) { private void DeleteNode(Node node) {
if (node.IsGroup) { if (node.IsGroup) {
Models.Preference.Instance.RemoveGroup(node.Id); Models.Preference.Instance.RemoveGroup(node.Id);
} else { }
else {
Models.Preference.Instance.RemoveRepository(node.Id); Models.Preference.Instance.RemoveRepository(node.Id);
} }
@ -348,11 +476,14 @@ namespace SourceGit.Views.Widgets {
Models.Exception.Raise(App.Text("NotConfigured")); Models.Exception.Raise(App.Text("NotConfigured"));
return false; return false;
} }
return true; return true;
} }
private void CheckAndOpen(string path) { private void CheckAndOpen(string path) {
if (!MakeSureReady()) return; if (!MakeSureReady()) {
return;
}
if (!Directory.Exists(path)) { if (!Directory.Exists(path)) {
Models.Exception.Raise(App.Text("PathNotFound", path)); Models.Exception.Raise(App.Text("PathNotFound", path));
@ -360,6 +491,7 @@ namespace SourceGit.Views.Widgets {
} }
var root = new Commands.GetRepositoryRootPath(path).Result(); var root = new Commands.GetRepositoryRootPath(path).Result();
if (root == null) { if (root == null) {
new Popups.Init(path).Show(); new Popups.Init(path).Show();
return; return;
@ -368,28 +500,37 @@ namespace SourceGit.Views.Widgets {
var gitDir = new Commands.QueryGitDir(root).Result(); var gitDir = new Commands.QueryGitDir(root).Result();
var repo = Models.Preference.Instance.AddRepository(root, gitDir, ""); var repo = Models.Preference.Instance.AddRepository(root, gitDir, "");
Models.Watcher.Open(repo); Models.Watcher.Open(repo);
treeHistory.UnselectAll();
treeBookmarks.UnselectAll();
} }
public void UpdateNodes(string id, int bookmark, IEnumerable<Node> nodes = null) { public void UpdateNodes(string id, int bookmark, IEnumerable<Node> nodes = null) {
if (nodes == null) nodes = tree.ItemsSource.OfType<Node>(); if (nodes == null) {
nodes = treeHistory.ItemsSource.OfType<Node>();
}
foreach (var node in nodes) { foreach (var node in nodes) {
if (!node.IsGroup) { if (!node.IsGroup) {
if (node.Id == id) { if (node.Id == id) {
node.Bookmark = bookmark; Models.Preference.Instance.FindRepository(node.Id).Bookmark = bookmark;
UpdateTree();
break; break;
} }
} else if (node.Children.Count > 0) { }
else if (node.Children.Count > 0) {
UpdateNodes(id, bookmark, node.Children); UpdateNodes(id, bookmark, node.Children);
} }
} }
} }
#endregion #endregion
#region RENAME_NODES #region RENAME_NODES
private void RenameStart(object sender, RoutedEventArgs e) { private void RenameStart(object sender, RoutedEventArgs e) {
var edit = sender as Controls.TextEdit; var edit = sender as Controls.TextEdit;
if (edit == null || !edit.IsVisible) return;
if (edit == null || !edit.IsVisible) {
return;
}
edit.SelectAll(); edit.SelectAll();
edit.Focus(); edit.Focus();
@ -399,7 +540,8 @@ namespace SourceGit.Views.Widgets {
if (e.Key == Key.Escape) { if (e.Key == Key.Escape) {
UpdateTree(); UpdateTree();
e.Handled = true; e.Handled = true;
} else if (e.Key == Key.Enter) { }
else if (e.Key == Key.Enter) {
RenameEnd(sender, e); RenameEnd(sender, e);
e.Handled = true; e.Handled = true;
} }
@ -407,7 +549,10 @@ namespace SourceGit.Views.Widgets {
private void RenameEnd(object sender, RoutedEventArgs e) { private void RenameEnd(object sender, RoutedEventArgs e) {
var edit = sender as Controls.TextEdit; var edit = sender as Controls.TextEdit;
if (edit == null) return;
if (edit == null) {
return;
}
if (string.IsNullOrWhiteSpace(edit.Text)) { if (string.IsNullOrWhiteSpace(edit.Text)) {
UpdateTree(); UpdateTree();
@ -416,15 +561,19 @@ namespace SourceGit.Views.Widgets {
} }
var node = edit.DataContext as Node; var node = edit.DataContext as Node;
if (node != null) { if (node != null) {
node.Name = edit.Text; node.Name = edit.Text;
node.IsEditing = false; node.IsEditing = false;
if (node.IsGroup) { if (node.IsGroup) {
Models.Preference.Instance.RenameGroup(node.Id, edit.Text); Models.Preference.Instance.RenameGroup(node.Id, edit.Text);
} else { }
else {
Models.Preference.Instance.RenameRepository(node.Id, node.Name); Models.Preference.Instance.RenameRepository(node.Id, node.Name);
OnNodeEdited?.Invoke(node); OnNodeEdited?.Invoke(node);
} }
e.Handled = false; e.Handled = false;
} }
} }