mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
refactor: use TreeDataGrid instead of TreeView/DataGrid to improve performance (#148)
This commit is contained in:
parent
3160f1d142
commit
b192a1c423
24 changed files with 1333 additions and 1330 deletions
|
@ -19,6 +19,7 @@
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
<FluentTheme />
|
||||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||||
|
<StyleInclude Source="avares://Avalonia.Controls.TreeDataGrid/Themes/Fluent.axaml"/>
|
||||||
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
||||||
<StyleInclude Source="/Resources/Styles.axaml"/>
|
<StyleInclude Source="/Resources/Styles.axaml"/>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|
421
src/Models/TreeDataGridSelectionModel.cs
Normal file
421
src/Models/TreeDataGridSelectionModel.cs
Normal file
|
@ -0,0 +1,421 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Models.TreeDataGrid;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Controls.Selection;
|
||||||
|
using Avalonia.Input;
|
||||||
|
|
||||||
|
namespace SourceGit.Models
|
||||||
|
{
|
||||||
|
public class TreeDataGridSelectionModel<TModel> : TreeSelectionModelBase<TModel>,
|
||||||
|
ITreeDataGridRowSelectionModel<TModel>,
|
||||||
|
ITreeDataGridSelectionInteraction
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
private static readonly Point s_InvalidPoint = new(double.NegativeInfinity, double.NegativeInfinity);
|
||||||
|
|
||||||
|
private readonly ITreeDataGridSource<TModel> _source;
|
||||||
|
private EventHandler _viewSelectionChanged;
|
||||||
|
private EventHandler _rowDoubleTapped;
|
||||||
|
private Point _pressedPoint = s_InvalidPoint;
|
||||||
|
private bool _raiseViewSelectionChanged;
|
||||||
|
private Func<TModel, IEnumerable<TModel>> _childrenGetter;
|
||||||
|
|
||||||
|
public TreeDataGridSelectionModel(ITreeDataGridSource<TModel> source, Func<TModel, IEnumerable<TModel>> childrenGetter)
|
||||||
|
: base(source.Items)
|
||||||
|
{
|
||||||
|
_source = source;
|
||||||
|
_childrenGetter = childrenGetter;
|
||||||
|
|
||||||
|
SelectionChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (!IsSourceCollectionChanging)
|
||||||
|
_viewSelectionChanged?.Invoke(this, e);
|
||||||
|
else
|
||||||
|
_raiseViewSelectionChanged = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(IEnumerable<TModel> items)
|
||||||
|
{
|
||||||
|
var sets = new HashSet<TModel>();
|
||||||
|
foreach (var item in items)
|
||||||
|
sets.Add(item);
|
||||||
|
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
int num = _source.Rows.Count;
|
||||||
|
for (int i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
var m = _source.Rows[i].Model as TModel;
|
||||||
|
if (m != null && sets.Contains(m))
|
||||||
|
{
|
||||||
|
var idx = _source.Rows.RowIndexToModelIndex(i);
|
||||||
|
Select(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event EventHandler ITreeDataGridSelectionInteraction.SelectionChanged
|
||||||
|
{
|
||||||
|
add => _viewSelectionChanged += value;
|
||||||
|
remove => _viewSelectionChanged -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler RowDoubleTapped
|
||||||
|
{
|
||||||
|
add => _rowDoubleTapped += value;
|
||||||
|
remove => _rowDoubleTapped -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable ITreeDataGridSelection.Source
|
||||||
|
{
|
||||||
|
get => Source;
|
||||||
|
set => Source = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITreeDataGridSelectionInteraction.IsRowSelected(IRow rowModel)
|
||||||
|
{
|
||||||
|
if (rowModel is IModelIndexableRow indexable)
|
||||||
|
return IsSelected(indexable.ModelIndexPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITreeDataGridSelectionInteraction.IsRowSelected(int rowIndex)
|
||||||
|
{
|
||||||
|
if (rowIndex >= 0 && rowIndex < _source.Rows.Count)
|
||||||
|
{
|
||||||
|
if (_source.Rows[rowIndex] is IModelIndexableRow indexable)
|
||||||
|
return IsSelected(indexable.ModelIndexPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITreeDataGridSelectionInteraction.OnKeyDown(TreeDataGrid sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender.RowsPresenter is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!e.Handled)
|
||||||
|
{
|
||||||
|
var ctrl = e.KeyModifiers.HasFlag(KeyModifiers.Control);
|
||||||
|
if (e.Key == Key.A && ctrl && !SingleSelect)
|
||||||
|
{
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
int num = _source.Rows.Count;
|
||||||
|
for (int i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
var m = _source.Rows.RowIndexToModelIndex(i);
|
||||||
|
Select(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = e.Key.ToNavigationDirection();
|
||||||
|
var shift = e.KeyModifiers.HasFlag(KeyModifiers.Shift);
|
||||||
|
if (direction.HasValue)
|
||||||
|
{
|
||||||
|
var anchorRowIndex = _source.Rows.ModelIndexToRowIndex(AnchorIndex);
|
||||||
|
sender.RowsPresenter.BringIntoView(anchorRowIndex);
|
||||||
|
|
||||||
|
var anchor = sender.TryGetRow(anchorRowIndex);
|
||||||
|
if (anchor is not null && !ctrl)
|
||||||
|
{
|
||||||
|
e.Handled = TryKeyExpandCollapse(sender, direction.Value, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.Handled && (!ctrl || shift))
|
||||||
|
{
|
||||||
|
e.Handled = MoveSelection(sender, direction.Value, shift, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.Handled && direction == NavigationDirection.Left
|
||||||
|
&& anchor?.Rows is HierarchicalRows<TModel> hierarchicalRows && anchorRowIndex > 0)
|
||||||
|
{
|
||||||
|
var newIndex = hierarchicalRows.GetParentRowIndex(AnchorIndex);
|
||||||
|
UpdateSelection(sender, newIndex, true);
|
||||||
|
FocusRow(sender, sender.RowsPresenter.BringIntoView(newIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.Handled && direction == NavigationDirection.Right
|
||||||
|
&& anchor?.Rows is HierarchicalRows<TModel> hierarchicalRows2 && hierarchicalRows2[anchorRowIndex].IsExpanded)
|
||||||
|
{
|
||||||
|
var newIndex = anchorRowIndex + 1;
|
||||||
|
UpdateSelection(sender, newIndex, true);
|
||||||
|
sender.RowsPresenter.BringIntoView(newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITreeDataGridSelectionInteraction.OnPointerPressed(TreeDataGrid sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.Handled &&
|
||||||
|
e.Pointer.Type == PointerType.Mouse &&
|
||||||
|
e.Source is Control source &&
|
||||||
|
sender.TryGetRow(source, out var row) &&
|
||||||
|
_source.Rows.RowIndexToModelIndex(row.RowIndex) is { } modelIndex)
|
||||||
|
{
|
||||||
|
if (!IsSelected(modelIndex))
|
||||||
|
{
|
||||||
|
PointerSelect(sender, row, e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var point = e.GetCurrentPoint(sender);
|
||||||
|
if (point.Properties.IsRightButtonPressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (e.KeyModifiers == KeyModifiers.Control)
|
||||||
|
{
|
||||||
|
Deselect(modelIndex);
|
||||||
|
}
|
||||||
|
else if (e.ClickCount == 2)
|
||||||
|
{
|
||||||
|
_rowDoubleTapped?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
Select(modelIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pressedPoint = s_InvalidPoint;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!sender.TryGetRow(e.Source as Control, out var test))
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
_pressedPoint = e.GetPosition(sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITreeDataGridSelectionInteraction.OnPointerReleased(TreeDataGrid sender, PointerReleasedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.Handled &&
|
||||||
|
_pressedPoint != s_InvalidPoint &&
|
||||||
|
e.Source is Control source &&
|
||||||
|
sender.TryGetRow(source, out var row) &&
|
||||||
|
_source.Rows.RowIndexToModelIndex(row.RowIndex) is { } modelIndex)
|
||||||
|
{
|
||||||
|
if (!IsSelected(modelIndex))
|
||||||
|
{
|
||||||
|
var p = e.GetPosition(sender);
|
||||||
|
if (Math.Abs(p.X - _pressedPoint.X) <= 3 || Math.Abs(p.Y - _pressedPoint.Y) <= 3)
|
||||||
|
PointerSelect(sender, row, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSourceCollectionChangeFinished()
|
||||||
|
{
|
||||||
|
if (_raiseViewSelectionChanged)
|
||||||
|
{
|
||||||
|
_viewSelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
_raiseViewSelectionChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PointerSelect(TreeDataGrid sender, TreeDataGridRow row, PointerEventArgs e)
|
||||||
|
{
|
||||||
|
var point = e.GetCurrentPoint(sender);
|
||||||
|
|
||||||
|
var commandModifiers = TopLevel.GetTopLevel(sender)?.PlatformSettings?.HotkeyConfiguration.CommandModifiers;
|
||||||
|
var toggleModifier = commandModifiers is not null && e.KeyModifiers.HasFlag(commandModifiers);
|
||||||
|
var isRightButton = point.Properties.PointerUpdateKind is PointerUpdateKind.RightButtonPressed or
|
||||||
|
PointerUpdateKind.RightButtonReleased;
|
||||||
|
|
||||||
|
UpdateSelection(
|
||||||
|
sender,
|
||||||
|
row.RowIndex,
|
||||||
|
select: true,
|
||||||
|
rangeModifier: e.KeyModifiers.HasFlag(KeyModifiers.Shift),
|
||||||
|
toggleModifier: toggleModifier,
|
||||||
|
rightButton: isRightButton);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSelection(TreeDataGrid treeDataGrid, int rowIndex, bool select = true, bool rangeModifier = false, bool toggleModifier = false, bool rightButton = false)
|
||||||
|
{
|
||||||
|
var modelIndex = _source.Rows.RowIndexToModelIndex(rowIndex);
|
||||||
|
if (modelIndex == default)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mode = SingleSelect ? SelectionMode.Single : SelectionMode.Multiple;
|
||||||
|
var multi = (mode & SelectionMode.Multiple) != 0;
|
||||||
|
var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0);
|
||||||
|
var range = multi && rangeModifier;
|
||||||
|
|
||||||
|
if (!select)
|
||||||
|
{
|
||||||
|
if (IsSelected(modelIndex) && !treeDataGrid.QueryCancelSelection())
|
||||||
|
Deselect(modelIndex);
|
||||||
|
}
|
||||||
|
else if (rightButton)
|
||||||
|
{
|
||||||
|
if (IsSelected(modelIndex) == false && !treeDataGrid.QueryCancelSelection())
|
||||||
|
SelectedIndex = modelIndex;
|
||||||
|
}
|
||||||
|
else if (range)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
{
|
||||||
|
var anchor = RangeAnchorIndex;
|
||||||
|
var i = Math.Max(_source.Rows.ModelIndexToRowIndex(anchor), 0);
|
||||||
|
var step = i < rowIndex ? 1 : -1;
|
||||||
|
|
||||||
|
using (BatchUpdate())
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var m = _source.Rows.RowIndexToModelIndex(i);
|
||||||
|
Select(m);
|
||||||
|
anchor = m;
|
||||||
|
if (i == rowIndex)
|
||||||
|
break;
|
||||||
|
i += step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (multi && toggle)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
{
|
||||||
|
if (IsSelected(modelIndex) == true)
|
||||||
|
Deselect(modelIndex);
|
||||||
|
else
|
||||||
|
Select(modelIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (toggle)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
SelectedIndex = (SelectedIndex == modelIndex) ? -1 : modelIndex;
|
||||||
|
}
|
||||||
|
else if (SelectedIndex != modelIndex || Count > 1)
|
||||||
|
{
|
||||||
|
if (!treeDataGrid.QueryCancelSelection())
|
||||||
|
SelectedIndex = modelIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryKeyExpandCollapse(TreeDataGrid treeDataGrid, NavigationDirection direction, TreeDataGridRow focused)
|
||||||
|
{
|
||||||
|
if (treeDataGrid.RowsPresenter is null || focused.RowIndex < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var row = _source.Rows[focused.RowIndex];
|
||||||
|
|
||||||
|
if (row is IExpander expander)
|
||||||
|
{
|
||||||
|
if (direction == NavigationDirection.Right && !expander.IsExpanded)
|
||||||
|
{
|
||||||
|
expander.IsExpanded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (direction == NavigationDirection.Left && expander.IsExpanded)
|
||||||
|
{
|
||||||
|
expander.IsExpanded = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MoveSelection(TreeDataGrid treeDataGrid, NavigationDirection direction, bool rangeModifier, TreeDataGridRow focused)
|
||||||
|
{
|
||||||
|
if (treeDataGrid.RowsPresenter is null || _source.Columns.Count == 0 || _source.Rows.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var currentRowIndex = focused?.RowIndex ?? _source.Rows.ModelIndexToRowIndex(SelectedIndex);
|
||||||
|
int newRowIndex;
|
||||||
|
|
||||||
|
if (direction == NavigationDirection.First || direction == NavigationDirection.Last)
|
||||||
|
{
|
||||||
|
newRowIndex = direction == NavigationDirection.First ? 0 : _source.Rows.Count - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(var x, var y) = direction switch
|
||||||
|
{
|
||||||
|
NavigationDirection.Up => (0, -1),
|
||||||
|
NavigationDirection.Down => (0, 1),
|
||||||
|
NavigationDirection.Left => (-1, 0),
|
||||||
|
NavigationDirection.Right => (1, 0),
|
||||||
|
_ => (0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
newRowIndex = Math.Max(0, Math.Min(currentRowIndex + y, _source.Rows.Count - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newRowIndex != currentRowIndex)
|
||||||
|
UpdateSelection(treeDataGrid, newRowIndex, true, rangeModifier);
|
||||||
|
|
||||||
|
if (newRowIndex != currentRowIndex)
|
||||||
|
{
|
||||||
|
treeDataGrid.RowsPresenter?.BringIntoView(newRowIndex);
|
||||||
|
FocusRow(treeDataGrid, treeDataGrid.TryGetRow(newRowIndex));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FocusRow(TreeDataGrid owner, Control control)
|
||||||
|
{
|
||||||
|
if (!owner.TryGetRow(control, out var row) || row.CellsPresenter is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the column index of the currently focused cell if possible: we'll try to focus the
|
||||||
|
// same column in the new row.
|
||||||
|
if (TopLevel.GetTopLevel(owner)?.FocusManager is { } focusManager &&
|
||||||
|
focusManager.GetFocusedElement() is Control currentFocus &&
|
||||||
|
owner.TryGetCell(currentFocus, out var currentCell) &&
|
||||||
|
row.TryGetCell(currentCell.ColumnIndex) is { } newCell &&
|
||||||
|
newCell.Focusable)
|
||||||
|
{
|
||||||
|
newCell.Focus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, just focus the first focusable cell in the row.
|
||||||
|
foreach (var cell in row.CellsPresenter.GetRealizedElements())
|
||||||
|
{
|
||||||
|
if (cell.Focusable)
|
||||||
|
{
|
||||||
|
cell.Focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<TModel> GetChildren(TModel node)
|
||||||
|
{
|
||||||
|
return _childrenGetter?.Invoke(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -330,7 +330,7 @@
|
||||||
<x:String x:Key="Text.Push.Remote" xml:space="preserve">Remote :</x:String>
|
<x:String x:Key="Text.Push.Remote" xml:space="preserve">Remote :</x:String>
|
||||||
<x:String x:Key="Text.Push.Title" xml:space="preserve">Push Changes To Remote</x:String>
|
<x:String x:Key="Text.Push.Title" xml:space="preserve">Push Changes To Remote</x:String>
|
||||||
<x:String x:Key="Text.Push.To" xml:space="preserve">Remote Branch :</x:String>
|
<x:String x:Key="Text.Push.To" xml:space="preserve">Remote Branch :</x:String>
|
||||||
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">Tracking remote branch(--set-upstream)</x:String>
|
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">Tracking remote branch</x:String>
|
||||||
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">Push all tags</x:String>
|
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">Push all tags</x:String>
|
||||||
<x:String x:Key="Text.PushTag" xml:space="preserve">Push Tag To Remote</x:String>
|
<x:String x:Key="Text.PushTag" xml:space="preserve">Push Tag To Remote</x:String>
|
||||||
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">Push to all remotes</x:String>
|
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">Push to all remotes</x:String>
|
||||||
|
|
|
@ -330,7 +330,7 @@
|
||||||
<x:String x:Key="Text.Push.Remote" xml:space="preserve">远程仓库 :</x:String>
|
<x:String x:Key="Text.Push.Remote" xml:space="preserve">远程仓库 :</x:String>
|
||||||
<x:String x:Key="Text.Push.Title" xml:space="preserve">推送到远程仓库</x:String>
|
<x:String x:Key="Text.Push.Title" xml:space="preserve">推送到远程仓库</x:String>
|
||||||
<x:String x:Key="Text.Push.To" xml:space="preserve">远程分支 :</x:String>
|
<x:String x:Key="Text.Push.To" xml:space="preserve">远程分支 :</x:String>
|
||||||
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">跟踪远程分支(--set-upstream)</x:String>
|
<x:String x:Key="Text.Push.Tracking" xml:space="preserve">跟踪远程分支</x:String>
|
||||||
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">同时推送标签</x:String>
|
<x:String x:Key="Text.Push.WithAllTags" xml:space="preserve">同时推送标签</x:String>
|
||||||
<x:String x:Key="Text.PushTag" xml:space="preserve">推送标签到远程仓库</x:String>
|
<x:String x:Key="Text.PushTag" xml:space="preserve">推送标签到远程仓库</x:String>
|
||||||
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">推送到所有远程仓库</x:String>
|
<x:String x:Key="Text.PushTag.PushAllRemotes" xml:space="preserve">推送到所有远程仓库</x:String>
|
||||||
|
|
|
@ -1075,4 +1075,58 @@
|
||||||
<Style Selector="TreeViewItem[IsExpanded=False] Path.folder_icon">
|
<Style Selector="TreeViewItem[IsExpanded=False] Path.folder_icon">
|
||||||
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TreeDataGrid">
|
||||||
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="RootBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<DockPanel>
|
||||||
|
<ScrollViewer Name="PART_HeaderScrollViewer"
|
||||||
|
DockPanel.Dock="Top"
|
||||||
|
IsVisible="{TemplateBinding ShowColumnHeaders}"
|
||||||
|
HorizontalScrollBarVisibility="Hidden"
|
||||||
|
VerticalScrollBarVisibility="Disabled"
|
||||||
|
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||||
|
<Border x:Name="ColumnHeadersPresenterBorder">
|
||||||
|
<TreeDataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
|
||||||
|
ElementFactory="{TemplateBinding ElementFactory}"
|
||||||
|
Items="{TemplateBinding Columns}" />
|
||||||
|
</Border>
|
||||||
|
</ScrollViewer>
|
||||||
|
<ScrollViewer Name="PART_ScrollViewer"
|
||||||
|
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
|
||||||
|
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
|
||||||
|
<TreeDataGridRowsPresenter Name="PART_RowsPresenter"
|
||||||
|
Columns="{TemplateBinding Columns}"
|
||||||
|
ElementFactory="{TemplateBinding ElementFactory}"
|
||||||
|
Items="{TemplateBinding Rows}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^/template/ Border#ColumnHeadersPresenterBorder">
|
||||||
|
<Setter Property="BorderThickness" Value="0 0 0 1" />
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource TreeDataGridGridLinesBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TreeDataGridColumnHeader">
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeDataGridTextCell TextBlock">
|
||||||
|
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeDataGridExpanderCell[IsExpanded=True] Path.folder_icon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Icons.Folder.Open}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeDataGridExpanderCell[IsExpanded=False] Path.folder_icon">
|
||||||
|
<Setter Property="Data" Value="{StaticResource Icons.Folder.Fill}"/>
|
||||||
|
</Style>
|
||||||
</Styles>
|
</Styles>
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
||||||
|
<PackageReference Include="Avalonia.Controls.TreeDataGrid" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
|
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
||||||
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
|
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
|
||||||
|
|
|
@ -4,6 +4,8 @@ using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Models.TreeDataGrid;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
@ -48,48 +50,17 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _visibleChanges, value);
|
set => SetProperty(ref _visibleChanges, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> ChangeTree
|
public List<Models.Change> SelectedChanges
|
||||||
{
|
{
|
||||||
get => _changeTree;
|
get => _selectedChanges;
|
||||||
set => SetProperty(ref _changeTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedChange
|
|
||||||
{
|
|
||||||
get => _selectedChange;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedChange, value))
|
if (SetProperty(ref _selectedChanges, value))
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null || value.Count != 1)
|
||||||
{
|
|
||||||
SelectedChangeNode = null;
|
|
||||||
DiffContext = null;
|
DiffContext = null;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value[0]), _diffContext);
|
||||||
SelectedChangeNode = FileTreeNode.SelectByPath(_changeTree, value.Path);
|
|
||||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(_commit, value), _diffContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedChangeNode
|
|
||||||
{
|
|
||||||
get => _selectedChangeNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedChangeNode, value))
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
SelectedChange = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedChange = value.Backend as Models.Change;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,26 +77,10 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> RevisionFilesTree
|
public HierarchicalTreeDataGridSource<FileTreeNode> RevisionFiles
|
||||||
{
|
{
|
||||||
get => _revisionFilesTree;
|
get => _revisionFiles;
|
||||||
set => SetProperty(ref _revisionFilesTree, value);
|
private set => SetProperty(ref _revisionFiles, value);
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedRevisionFileNode
|
|
||||||
{
|
|
||||||
get => _selectedRevisionFileNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedRevisionFileNode, value) && value != null && !value.IsFolder)
|
|
||||||
{
|
|
||||||
RefreshViewRevisionFile(value.Backend as Models.Object);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ViewRevisionFileContent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SearchFileFilter
|
public string SearchFileFilter
|
||||||
|
@ -159,17 +114,14 @@ namespace SourceGit.ViewModels
|
||||||
_changes.Clear();
|
_changes.Clear();
|
||||||
if (_visibleChanges != null)
|
if (_visibleChanges != null)
|
||||||
_visibleChanges.Clear();
|
_visibleChanges.Clear();
|
||||||
if (_changeTree != null)
|
if (_selectedChanges != null)
|
||||||
_changeTree.Clear();
|
_selectedChanges.Clear();
|
||||||
_selectedChange = null;
|
|
||||||
_selectedChangeNode = null;
|
|
||||||
_searchChangeFilter = null;
|
_searchChangeFilter = null;
|
||||||
_diffContext = null;
|
_diffContext = null;
|
||||||
|
if (_revisionFilesBackup != null)
|
||||||
|
_revisionFilesBackup.Clear();
|
||||||
if (_revisionFiles != null)
|
if (_revisionFiles != null)
|
||||||
_revisionFiles.Clear();
|
_revisionFiles.Dispose();
|
||||||
if (_revisionFilesTree != null)
|
|
||||||
_revisionFilesTree.Clear();
|
|
||||||
_selectedRevisionFileNode = null;
|
|
||||||
_searchFileFilter = null;
|
_searchFileFilter = null;
|
||||||
_viewRevisionFileContent = null;
|
_viewRevisionFileContent = null;
|
||||||
_cancelToken = null;
|
_cancelToken = null;
|
||||||
|
@ -346,9 +298,14 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
_changes = null;
|
_changes = null;
|
||||||
VisibleChanges = null;
|
VisibleChanges = null;
|
||||||
SelectedChange = null;
|
SelectedChanges = null;
|
||||||
RevisionFilesTree = null;
|
|
||||||
SelectedRevisionFileNode = null;
|
if (_revisionFiles != null)
|
||||||
|
{
|
||||||
|
_revisionFiles.Dispose();
|
||||||
|
_revisionFiles = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (_commit == null)
|
if (_commit == null)
|
||||||
return;
|
return;
|
||||||
if (_cancelToken != null)
|
if (_cancelToken != null)
|
||||||
|
@ -379,40 +336,34 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tree = FileTreeNode.Build(visible);
|
var tree = FileTreeNode.Build(visible, true);
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
Changes = changes;
|
Changes = changes;
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
ChangeTree = tree;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
var files = cmdRevisionFiles.Result();
|
_revisionFilesBackup = cmdRevisionFiles.Result();
|
||||||
if (cmdRevisionFiles.Cancel.Requested)
|
if (cmdRevisionFiles.Cancel.Requested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visible = files;
|
var visible = _revisionFilesBackup;
|
||||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter))
|
var isSearching = !string.IsNullOrWhiteSpace(_searchFileFilter);
|
||||||
|
if (isSearching)
|
||||||
{
|
{
|
||||||
visible = new List<Models.Object>();
|
visible = new List<Models.Object>();
|
||||||
foreach (var f in files)
|
foreach (var f in _revisionFilesBackup)
|
||||||
{
|
{
|
||||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(f);
|
visible.Add(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var tree = FileTreeNode.Build(visible);
|
var tree = FileTreeNode.Build(visible, isSearching || visible.Count <= 100);
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() => BuildRevisionFilesSource(tree));
|
||||||
{
|
|
||||||
_revisionFiles = files;
|
|
||||||
RevisionFilesTree = tree;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,15 +382,11 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var c in _changes)
|
foreach (var c in _changes)
|
||||||
{
|
{
|
||||||
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase))
|
if (c.Path.Contains(_searchChangeFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(c);
|
visible.Add(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeTree = FileTreeNode.Build(_visibleChanges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshVisibleFiles()
|
private void RefreshVisibleFiles()
|
||||||
|
@ -447,24 +394,29 @@ namespace SourceGit.ViewModels
|
||||||
if (_revisionFiles == null)
|
if (_revisionFiles == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visible = _revisionFiles;
|
var visible = _revisionFilesBackup;
|
||||||
if (!string.IsNullOrWhiteSpace(_searchFileFilter))
|
var isSearching = !string.IsNullOrWhiteSpace(_searchFileFilter);
|
||||||
|
if (isSearching)
|
||||||
{
|
{
|
||||||
visible = new List<Models.Object>();
|
visible = new List<Models.Object>();
|
||||||
foreach (var f in _revisionFiles)
|
foreach (var f in _revisionFilesBackup)
|
||||||
{
|
{
|
||||||
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
if (f.Path.Contains(_searchFileFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(f);
|
visible.Add(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RevisionFilesTree = FileTreeNode.Build(visible);
|
BuildRevisionFilesSource(FileTreeNode.Build(visible, isSearching || visible.Count < 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshViewRevisionFile(Models.Object file)
|
private void RefreshViewRevisionFile(Models.Object file)
|
||||||
{
|
{
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
ViewRevisionFileContent = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (file.Type)
|
switch (file.Type)
|
||||||
{
|
{
|
||||||
case Models.ObjectType.Blob:
|
case Models.ObjectType.Blob:
|
||||||
|
@ -541,6 +493,35 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BuildRevisionFilesSource(List<FileTreeNode> tree)
|
||||||
|
{
|
||||||
|
var source = new HierarchicalTreeDataGridSource<FileTreeNode>(tree)
|
||||||
|
{
|
||||||
|
Columns =
|
||||||
|
{
|
||||||
|
new HierarchicalExpanderColumn<FileTreeNode>(
|
||||||
|
new TemplateColumn<FileTreeNode>("Icon", "FileTreeNodeExpanderTemplate", null, GridLength.Auto),
|
||||||
|
x => x.Children,
|
||||||
|
x => x.Children.Count > 0,
|
||||||
|
x => x.IsExpanded),
|
||||||
|
new TextColumn<FileTreeNode, string>(
|
||||||
|
null,
|
||||||
|
x => string.Empty,
|
||||||
|
GridLength.Star)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
source.Selection = new Models.TreeDataGridSelectionModel<FileTreeNode>(source, x => x.Children);
|
||||||
|
source.RowSelection.SingleSelect = true;
|
||||||
|
source.RowSelection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (s is Models.TreeDataGridSelectionModel<FileTreeNode> selection)
|
||||||
|
RefreshViewRevisionFile(selection.SelectedItem?.Backend as Models.Object);
|
||||||
|
};
|
||||||
|
|
||||||
|
RevisionFiles = source;
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly HashSet<string> IMG_EXTS = new HashSet<string>()
|
private static readonly HashSet<string> IMG_EXTS = new HashSet<string>()
|
||||||
{
|
{
|
||||||
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
".ico", ".bmp", ".jpg", ".png", ".jpeg"
|
||||||
|
@ -551,14 +532,11 @@ namespace SourceGit.ViewModels
|
||||||
private Models.Commit _commit = null;
|
private Models.Commit _commit = null;
|
||||||
private List<Models.Change> _changes = null;
|
private List<Models.Change> _changes = null;
|
||||||
private List<Models.Change> _visibleChanges = null;
|
private List<Models.Change> _visibleChanges = null;
|
||||||
private List<FileTreeNode> _changeTree = null;
|
private List<Models.Change> _selectedChanges = null;
|
||||||
private Models.Change _selectedChange = null;
|
|
||||||
private FileTreeNode _selectedChangeNode = null;
|
|
||||||
private string _searchChangeFilter = string.Empty;
|
private string _searchChangeFilter = string.Empty;
|
||||||
private DiffContext _diffContext = null;
|
private DiffContext _diffContext = null;
|
||||||
private List<Models.Object> _revisionFiles = null;
|
private List<Models.Object> _revisionFilesBackup = null;
|
||||||
private List<FileTreeNode> _revisionFilesTree = null;
|
private HierarchicalTreeDataGridSource<FileTreeNode> _revisionFiles = null;
|
||||||
private FileTreeNode _selectedRevisionFileNode = null;
|
|
||||||
private string _searchFileFilter = string.Empty;
|
private string _searchFileFilter = string.Empty;
|
||||||
private object _viewRevisionFileContent = null;
|
private object _viewRevisionFileContent = null;
|
||||||
private Commands.Command.CancelToken _cancelToken = null;
|
private Commands.Command.CancelToken _cancelToken = null;
|
||||||
|
|
|
@ -85,6 +85,11 @@ namespace SourceGit.ViewModels
|
||||||
_content = previous._content;
|
_content = previous._content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null")
|
||||||
|
_title = _option.Path;
|
||||||
|
else
|
||||||
|
_title = $"{_option.OrgPath} → {_option.Path}";
|
||||||
|
|
||||||
LoadDiffContent();
|
LoadDiffContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,11 +180,6 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_option.OrgPath) || _option.OrgPath == "/dev/null")
|
|
||||||
Title = _option.Path;
|
|
||||||
else
|
|
||||||
Title = $"{_option.OrgPath} → {_option.Path}";
|
|
||||||
|
|
||||||
FileModeChange = latest.FileModeChange;
|
FileModeChange = latest.FileModeChange;
|
||||||
Content = rs;
|
Content = rs;
|
||||||
IsTextDiff = latest.TextDiff != null;
|
IsTextDiff = latest.TextDiff != null;
|
||||||
|
|
|
@ -18,11 +18,10 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _isExpanded, value);
|
set => SetProperty(ref _isExpanded, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<FileTreeNode> Build(List<Models.Change> changes)
|
public static List<FileTreeNode> Build(List<Models.Change> changes, bool expanded)
|
||||||
{
|
{
|
||||||
var nodes = new List<FileTreeNode>();
|
var nodes = new List<FileTreeNode>();
|
||||||
var folders = new Dictionary<string, FileTreeNode>();
|
var folders = new Dictionary<string, FileTreeNode>();
|
||||||
var expanded = changes.Count <= 50;
|
|
||||||
|
|
||||||
foreach (var c in changes)
|
foreach (var c in changes)
|
||||||
{
|
{
|
||||||
|
@ -94,11 +93,10 @@ namespace SourceGit.ViewModels
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<FileTreeNode> Build(List<Models.Object> files)
|
public static List<FileTreeNode> Build(List<Models.Object> files, bool expanded)
|
||||||
{
|
{
|
||||||
var nodes = new List<FileTreeNode>();
|
var nodes = new List<FileTreeNode>();
|
||||||
var folders = new Dictionary<string, FileTreeNode>();
|
var folders = new Dictionary<string, FileTreeNode>();
|
||||||
var expanded = files.Count <= 50;
|
|
||||||
|
|
||||||
foreach (var f in files)
|
foreach (var f in files)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,49 +35,18 @@ namespace SourceGit.ViewModels
|
||||||
private set => SetProperty(ref _visibleChanges, value);
|
private set => SetProperty(ref _visibleChanges, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> ChangeTree
|
public List<Models.Change> SelectedChanges
|
||||||
{
|
{
|
||||||
get => _changeTree;
|
get => _selectedChanges;
|
||||||
private set => SetProperty(ref _changeTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedChange
|
|
||||||
{
|
|
||||||
get => _selectedChange;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedChange, value))
|
if (SetProperty(ref _selectedChanges, value))
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value != null && value.Count == 1)
|
||||||
{
|
DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, _endPoint, value[0]), _diffContext);
|
||||||
SelectedNode = null;
|
else
|
||||||
DiffContext = null;
|
DiffContext = null;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedNode = FileTreeNode.SelectByPath(_changeTree, value.Path);
|
|
||||||
DiffContext = new DiffContext(_repo, new Models.DiffOption(StartPoint.SHA, _endPoint, value), _diffContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedNode
|
|
||||||
{
|
|
||||||
get => _selectedNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedNode, value))
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
SelectedChange = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedChange = value.Backend as Models.Change;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,17 +95,14 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var c in _changes)
|
foreach (var c in _changes)
|
||||||
{
|
{
|
||||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(c);
|
visible.Add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var tree = FileTreeNode.Build(visible);
|
var tree = FileTreeNode.Build(visible, true);
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
ChangeTree = tree;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -148,10 +114,8 @@ namespace SourceGit.ViewModels
|
||||||
_changes.Clear();
|
_changes.Clear();
|
||||||
if (_visibleChanges != null)
|
if (_visibleChanges != null)
|
||||||
_visibleChanges.Clear();
|
_visibleChanges.Clear();
|
||||||
if (_changeTree != null)
|
if (_selectedChanges != null)
|
||||||
_changeTree.Clear();
|
_selectedChanges.Clear();
|
||||||
_selectedChange = null;
|
|
||||||
_selectedNode = null;
|
|
||||||
_searchFilter = null;
|
_searchFilter = null;
|
||||||
_diffContext = null;
|
_diffContext = null;
|
||||||
}
|
}
|
||||||
|
@ -168,8 +132,12 @@ namespace SourceGit.ViewModels
|
||||||
SearchFilter = string.Empty;
|
SearchFilter = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContextMenu CreateChangeContextMenu(Models.Change change)
|
public ContextMenu CreateChangeContextMenu()
|
||||||
{
|
{
|
||||||
|
if (_selectedChanges == null || _selectedChanges.Count != 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var change = _selectedChanges[0];
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
|
|
||||||
var diffWithMerger = new MenuItem();
|
var diffWithMerger = new MenuItem();
|
||||||
|
@ -237,24 +205,18 @@ namespace SourceGit.ViewModels
|
||||||
foreach (var c in _changes)
|
foreach (var c in _changes)
|
||||||
{
|
{
|
||||||
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
|
||||||
visible.Add(c);
|
visible.Add(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VisibleChanges = visible;
|
VisibleChanges = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeTree = FileTreeNode.Build(_visibleChanges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _repo = string.Empty;
|
private string _repo = string.Empty;
|
||||||
private string _endPoint = string.Empty;
|
private string _endPoint = string.Empty;
|
||||||
private List<Models.Change> _changes = null;
|
private List<Models.Change> _changes = null;
|
||||||
private List<Models.Change> _visibleChanges = null;
|
private List<Models.Change> _visibleChanges = null;
|
||||||
private List<FileTreeNode> _changeTree = null;
|
private List<Models.Change> _selectedChanges = null;
|
||||||
private Models.Change _selectedChange = null;
|
|
||||||
private FileTreeNode _selectedNode = null;
|
|
||||||
private string _searchFilter = string.Empty;
|
private string _searchFilter = string.Empty;
|
||||||
private DiffContext _diffContext = null;
|
private DiffContext _diffContext = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,23 @@ namespace SourceGit.ViewModels
|
||||||
public bool UseAmend
|
public bool UseAmend
|
||||||
{
|
{
|
||||||
get => _useAmend;
|
get => _useAmend;
|
||||||
set => SetProperty(ref _useAmend, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _useAmend, value) && value)
|
||||||
|
{
|
||||||
|
var commits = new Commands.QueryCommits(_repo.FullPath, "-n 1", false).Result();
|
||||||
|
if (commits.Count == 0)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "No commits to amend!!!");
|
||||||
|
_useAmend = false;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CommitMessage = commits[0].FullMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Models.Change> Unstaged
|
public List<Models.Change> Unstaged
|
||||||
|
@ -73,103 +89,58 @@ namespace SourceGit.ViewModels
|
||||||
private set => SetProperty(ref _staged, value);
|
private set => SetProperty(ref _staged, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public List<Models.Change> SelectedUnstaged
|
||||||
{
|
{
|
||||||
get => _count;
|
get => _selectedUnstaged;
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedUnstagedChange
|
|
||||||
{
|
|
||||||
get => _selectedUnstagedChange;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedUnstagedChange, value) && value != null)
|
if (SetProperty(ref _selectedUnstaged, value))
|
||||||
{
|
{
|
||||||
SelectedStagedChange = null;
|
if (value == null || value.Count == 0)
|
||||||
SelectedStagedTreeNode = null;
|
|
||||||
SetDetail(value, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Change SelectedStagedChange
|
|
||||||
{
|
{
|
||||||
get => _selectedStagedChange;
|
if (_selectedStaged == null || _selectedStaged.Count == 0)
|
||||||
set
|
SetDetail(null);
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedStagedChange, value) && value != null)
|
|
||||||
{
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
SelectedUnstagedTreeNode = null;
|
|
||||||
SetDetail(value, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FileTreeNode> UnstagedTree
|
|
||||||
{
|
|
||||||
get => _unstagedTree;
|
|
||||||
private set => SetProperty(ref _unstagedTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FileTreeNode> StagedTree
|
|
||||||
{
|
|
||||||
get => _stagedTree;
|
|
||||||
private set => SetProperty(ref _stagedTree, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileTreeNode SelectedUnstagedTreeNode
|
|
||||||
{
|
|
||||||
get => _selectedUnstagedTreeNode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _selectedUnstagedTreeNode, value))
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedUnstagedChange = value.Backend as Models.Change;
|
SelectedStaged = null;
|
||||||
SelectedStagedTreeNode = null;
|
|
||||||
SelectedStagedChange = null;
|
|
||||||
|
|
||||||
if (value.IsFolder)
|
if (value.Count == 1)
|
||||||
{
|
SetDetail(value[0]);
|
||||||
SetDetail(null, true);
|
else
|
||||||
}
|
SetDetail(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileTreeNode SelectedStagedTreeNode
|
public List<Models.Change> SelectedStaged
|
||||||
{
|
{
|
||||||
get => _selectedStagedTreeNode;
|
get => _selectedStaged;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _selectedStagedTreeNode, value))
|
if (SetProperty(ref _selectedStaged, value))
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null || value.Count == 0)
|
||||||
{
|
{
|
||||||
SelectedStagedChange = null;
|
if (_selectedUnstaged == null || _selectedUnstaged.Count == 0)
|
||||||
|
SetDetail(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedStagedChange = value.Backend as Models.Change;
|
SelectedUnstaged = null;
|
||||||
SelectedUnstagedTreeNode = null;
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
|
|
||||||
if (value.IsFolder)
|
if (value.Count == 1)
|
||||||
{
|
SetDetail(value[0]);
|
||||||
SetDetail(null, false);
|
else
|
||||||
}
|
SetDetail(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Count => _count;
|
||||||
|
|
||||||
public object DetailContext
|
public object DetailContext
|
||||||
{
|
{
|
||||||
get => _detailContext;
|
get => _detailContext;
|
||||||
|
@ -194,14 +165,10 @@ namespace SourceGit.ViewModels
|
||||||
_unstaged.Clear();
|
_unstaged.Clear();
|
||||||
if (_staged != null)
|
if (_staged != null)
|
||||||
_staged.Clear();
|
_staged.Clear();
|
||||||
if (_unstagedTree != null)
|
if (_selectedUnstaged != null)
|
||||||
_unstagedTree.Clear();
|
_selectedUnstaged.Clear();
|
||||||
if (_stagedTree != null)
|
if (_selectedStaged != null)
|
||||||
_stagedTree.Clear();
|
_selectedStaged.Clear();
|
||||||
_selectedUnstagedChange = null;
|
|
||||||
_selectedStagedChange = null;
|
|
||||||
_selectedUnstagedTreeNode = null;
|
|
||||||
_selectedStagedTreeNode = null;
|
|
||||||
_detailContext = null;
|
_detailContext = null;
|
||||||
_commitMessage = string.Empty;
|
_commitMessage = string.Empty;
|
||||||
}
|
}
|
||||||
|
@ -210,20 +177,22 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
var unstaged = new List<Models.Change>();
|
var unstaged = new List<Models.Change>();
|
||||||
var staged = new List<Models.Change>();
|
var staged = new List<Models.Change>();
|
||||||
|
var selectedUnstaged = new List<Models.Change>();
|
||||||
|
var selectedStaged = new List<Models.Change>();
|
||||||
|
|
||||||
var viewFile = string.Empty;
|
var lastSelectedUnstaged = new HashSet<string>();
|
||||||
var lastSelectedIsUnstaged = false;
|
var lastSelectedStaged = new HashSet<string>();
|
||||||
if (_selectedUnstagedChange != null)
|
if (_selectedUnstaged != null)
|
||||||
{
|
{
|
||||||
viewFile = _selectedUnstagedChange.Path;
|
foreach (var c in _selectedUnstaged)
|
||||||
lastSelectedIsUnstaged = true;
|
lastSelectedUnstaged.Add(c.Path);
|
||||||
}
|
}
|
||||||
else if (_selectedStagedChange != null)
|
else if (_selectedStaged != null)
|
||||||
{
|
{
|
||||||
viewFile = _selectedStagedChange.Path;
|
foreach (var c in _selectedStaged)
|
||||||
|
lastSelectedStaged.Add(c.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewChange = null as Models.Change;
|
|
||||||
var hasConflict = false;
|
var hasConflict = false;
|
||||||
foreach (var c in changes)
|
foreach (var c in changes)
|
||||||
{
|
{
|
||||||
|
@ -233,65 +202,43 @@ namespace SourceGit.ViewModels
|
||||||
|| c.Index == Models.ChangeState.Renamed)
|
|| c.Index == Models.ChangeState.Renamed)
|
||||||
{
|
{
|
||||||
staged.Add(c);
|
staged.Add(c);
|
||||||
if (!lastSelectedIsUnstaged && c.Path == viewFile)
|
|
||||||
{
|
if (lastSelectedStaged.Contains(c.Path))
|
||||||
viewChange = c;
|
selectedStaged.Add(c);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.WorkTree != Models.ChangeState.None)
|
if (c.WorkTree != Models.ChangeState.None)
|
||||||
{
|
{
|
||||||
unstaged.Add(c);
|
unstaged.Add(c);
|
||||||
hasConflict |= c.IsConflit;
|
hasConflict |= c.IsConflit;
|
||||||
if (lastSelectedIsUnstaged && c.Path == viewFile)
|
|
||||||
{
|
if (lastSelectedUnstaged.Contains(c.Path))
|
||||||
viewChange = c;
|
selectedUnstaged.Add(c);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_count = changes.Count;
|
_count = changes.Count;
|
||||||
|
|
||||||
var unstagedTree = FileTreeNode.Build(unstaged);
|
|
||||||
var stagedTree = FileTreeNode.Build(staged);
|
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
_isLoadingData = true;
|
_isLoadingData = true;
|
||||||
Unstaged = unstaged;
|
Unstaged = unstaged;
|
||||||
Staged = staged;
|
Staged = staged;
|
||||||
UnstagedTree = unstagedTree;
|
|
||||||
StagedTree = stagedTree;
|
|
||||||
_isLoadingData = false;
|
_isLoadingData = false;
|
||||||
|
|
||||||
// Restore last selection states.
|
|
||||||
if (viewChange != null)
|
|
||||||
{
|
|
||||||
var scrollOffset = Vector.Zero;
|
var scrollOffset = Vector.Zero;
|
||||||
if (_detailContext is DiffContext old)
|
if (_detailContext is DiffContext old)
|
||||||
scrollOffset = old.SyncScrollOffset;
|
scrollOffset = old.SyncScrollOffset;
|
||||||
|
|
||||||
if (lastSelectedIsUnstaged)
|
if (selectedUnstaged.Count > 0)
|
||||||
{
|
SelectedUnstaged = selectedUnstaged;
|
||||||
SelectedUnstagedChange = viewChange;
|
else if (selectedStaged.Count > 0)
|
||||||
SelectedUnstagedTreeNode = FileTreeNode.SelectByPath(_unstagedTree, viewFile);
|
SelectedStaged = selectedStaged;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
SetDetail(null);
|
||||||
SelectedStagedChange = viewChange;
|
|
||||||
SelectedStagedTreeNode = FileTreeNode.SelectByPath(_stagedTree, viewFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_detailContext is DiffContext cur)
|
if (_detailContext is DiffContext cur)
|
||||||
cur.SyncScrollOffset = scrollOffset;
|
cur.SyncScrollOffset = scrollOffset;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedUnstagedChange = null;
|
|
||||||
SelectedUnstagedTreeNode = null;
|
|
||||||
SelectedStagedChange = null;
|
|
||||||
SelectedStagedTreeNode = null;
|
|
||||||
SetDetail(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to load merge message from MERGE_MSG
|
// Try to load merge message from MERGE_MSG
|
||||||
if (string.IsNullOrEmpty(_commitMessage))
|
if (string.IsNullOrEmpty(_commitMessage))
|
||||||
|
@ -305,30 +252,24 @@ namespace SourceGit.ViewModels
|
||||||
return hasConflict;
|
return hasConflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDetail(Models.Change change, bool isUnstaged)
|
public void OpenAssumeUnchanged()
|
||||||
{
|
{
|
||||||
if (_isLoadingData)
|
var dialog = new Views.AssumeUnchangedManager()
|
||||||
return;
|
{
|
||||||
|
DataContext = new AssumeUnchangedManager(_repo.FullPath)
|
||||||
|
};
|
||||||
|
|
||||||
if (change == null)
|
dialog.ShowDialog(App.GetTopLevel() as Window);
|
||||||
{
|
|
||||||
DetailContext = null;
|
|
||||||
}
|
}
|
||||||
else if (change.IsConflit && isUnstaged)
|
|
||||||
|
public void StageSelected()
|
||||||
{
|
{
|
||||||
DetailContext = new ConflictContext(_repo.FullPath, change);
|
StageChanges(_selectedUnstaged);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
public void StageAll()
|
||||||
{
|
{
|
||||||
if (_detailContext is DiffContext previous)
|
StageChanges(_unstaged);
|
||||||
{
|
|
||||||
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), previous);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void StageChanges(List<Models.Change> changes)
|
public async void StageChanges(List<Models.Change> changes)
|
||||||
|
@ -336,7 +277,7 @@ namespace SourceGit.ViewModels
|
||||||
if (_unstaged.Count == 0 || changes.Count == 0)
|
if (_unstaged.Count == 0 || changes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetDetail(null, true);
|
SetDetail(null);
|
||||||
IsStaging = true;
|
IsStaging = true;
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
if (changes.Count == _unstaged.Count)
|
if (changes.Count == _unstaged.Count)
|
||||||
|
@ -357,12 +298,22 @@ namespace SourceGit.ViewModels
|
||||||
IsStaging = false;
|
IsStaging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UnstageSelected()
|
||||||
|
{
|
||||||
|
UnstageChanges(_selectedStaged);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnstageAll()
|
||||||
|
{
|
||||||
|
UnstageChanges(_staged);
|
||||||
|
}
|
||||||
|
|
||||||
public async void UnstageChanges(List<Models.Change> changes)
|
public async void UnstageChanges(List<Models.Change> changes)
|
||||||
{
|
{
|
||||||
if (_staged.Count == 0 || changes.Count == 0)
|
if (_staged.Count == 0 || changes.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SetDetail(null, false);
|
SetDetail(null);
|
||||||
IsUnstaging = true;
|
IsUnstaging = true;
|
||||||
_repo.SetWatcherEnabled(false);
|
_repo.SetWatcherEnabled(false);
|
||||||
if (changes.Count == _staged.Count)
|
if (changes.Count == _staged.Count)
|
||||||
|
@ -412,113 +363,25 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void UseTheirs(List<Models.Change> changes)
|
public void Commit()
|
||||||
{
|
{
|
||||||
var files = new List<string>();
|
DoCommit(false);
|
||||||
foreach (var change in changes)
|
|
||||||
{
|
|
||||||
if (change.IsConflit)
|
|
||||||
files.Add(change.Path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_repo.SetWatcherEnabled(false);
|
public void CommitWithPush()
|
||||||
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
|
|
||||||
if (succ)
|
|
||||||
{
|
{
|
||||||
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
DoCommit(true);
|
||||||
}
|
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void UseMine(List<Models.Change> changes)
|
public ContextMenu CreateContextMenuForUnstagedChanges()
|
||||||
{
|
{
|
||||||
var files = new List<string>();
|
if (_selectedUnstaged.Count == 0)
|
||||||
foreach (var change in changes)
|
|
||||||
{
|
|
||||||
if (change.IsConflit)
|
|
||||||
files.Add(change.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
_repo.SetWatcherEnabled(false);
|
|
||||||
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
|
|
||||||
if (succ)
|
|
||||||
{
|
|
||||||
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
|
||||||
}
|
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void UseExternalMergeTool(Models.Change change)
|
|
||||||
{
|
|
||||||
var type = Preference.Instance.ExternalMergeToolType;
|
|
||||||
var exec = Preference.Instance.ExternalMergeToolPath;
|
|
||||||
|
|
||||||
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
|
|
||||||
if (tool == null)
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
|
|
||||||
|
|
||||||
_repo.SetWatcherEnabled(false);
|
|
||||||
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, change.Path));
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void DoCommit(bool autoPush)
|
|
||||||
{
|
|
||||||
if (!PopupHost.CanCreatePopup())
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_staged.Count == 0)
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_commitMessage))
|
|
||||||
{
|
|
||||||
App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PushCommitMessage();
|
|
||||||
|
|
||||||
SetDetail(null, false);
|
|
||||||
IsCommitting = true;
|
|
||||||
_repo.SetWatcherEnabled(false);
|
|
||||||
var succ = await Task.Run(() => new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec());
|
|
||||||
if (succ)
|
|
||||||
{
|
|
||||||
CommitMessage = string.Empty;
|
|
||||||
UseAmend = false;
|
|
||||||
|
|
||||||
if (autoPush)
|
|
||||||
{
|
|
||||||
PopupHost.ShowAndStartPopup(new Push(_repo, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_repo.MarkWorkingCopyDirtyManually();
|
|
||||||
_repo.SetWatcherEnabled(true);
|
|
||||||
IsCommitting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextMenu CreateContextMenuForUnstagedChanges(List<Models.Change> changes)
|
|
||||||
{
|
|
||||||
if (changes.Count == 0)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
if (changes.Count == 1)
|
if (_selectedUnstaged.Count == 1)
|
||||||
{
|
{
|
||||||
var change = changes[0];
|
var change = _selectedUnstaged[0];
|
||||||
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
||||||
|
|
||||||
var explore = new MenuItem();
|
var explore = new MenuItem();
|
||||||
|
@ -551,7 +414,7 @@ namespace SourceGit.ViewModels
|
||||||
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
||||||
useTheirs.Click += (_, e) =>
|
useTheirs.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseTheirs(changes);
|
UseTheirs(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -560,7 +423,7 @@ namespace SourceGit.ViewModels
|
||||||
useMine.Header = App.Text("FileCM.UseMine");
|
useMine.Header = App.Text("FileCM.UseMine");
|
||||||
useMine.Click += (_, e) =>
|
useMine.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseMine(changes);
|
UseMine(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -585,7 +448,7 @@ namespace SourceGit.ViewModels
|
||||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||||
stage.Click += (_, e) =>
|
stage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
StageChanges(changes);
|
StageChanges(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -594,7 +457,7 @@ namespace SourceGit.ViewModels
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, true);
|
Discard(_selectedUnstaged, true);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -605,7 +468,7 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedUnstaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -627,7 +490,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -679,7 +542,7 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
var hasConflicts = false;
|
var hasConflicts = false;
|
||||||
var hasNoneConflicts = false;
|
var hasNoneConflicts = false;
|
||||||
foreach (var change in changes)
|
foreach (var change in _selectedUnstaged)
|
||||||
{
|
{
|
||||||
if (change.IsConflit)
|
if (change.IsConflit)
|
||||||
{
|
{
|
||||||
|
@ -704,7 +567,7 @@ namespace SourceGit.ViewModels
|
||||||
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
useTheirs.Header = App.Text("FileCM.UseTheirs");
|
||||||
useTheirs.Click += (_, e) =>
|
useTheirs.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseTheirs(changes);
|
UseTheirs(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -713,7 +576,7 @@ namespace SourceGit.ViewModels
|
||||||
useMine.Header = App.Text("FileCM.UseMine");
|
useMine.Header = App.Text("FileCM.UseMine");
|
||||||
useMine.Click += (_, e) =>
|
useMine.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
UseMine(changes);
|
UseMine(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -723,31 +586,31 @@ namespace SourceGit.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
var stage = new MenuItem();
|
var stage = new MenuItem();
|
||||||
stage.Header = App.Text("FileCM.StageMulti", changes.Count);
|
stage.Header = App.Text("FileCM.StageMulti", _selectedUnstaged.Count);
|
||||||
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
|
||||||
stage.Click += (_, e) =>
|
stage.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
StageChanges(changes);
|
StageChanges(_selectedUnstaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var discard = new MenuItem();
|
var discard = new MenuItem();
|
||||||
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
|
discard.Header = App.Text("FileCM.DiscardMulti", _selectedUnstaged.Count);
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, true);
|
Discard(_selectedUnstaged, true);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var stash = new MenuItem();
|
var stash = new MenuItem();
|
||||||
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
|
stash.Header = App.Text("FileCM.StashMulti", _selectedUnstaged.Count);
|
||||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||||
stash.Click += (_, e) =>
|
stash.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedUnstaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -769,7 +632,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, true, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedUnstaged, true, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -786,15 +649,15 @@ namespace SourceGit.ViewModels
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContextMenu CreateContextMenuForStagedChanges(List<Models.Change> changes)
|
public ContextMenu CreateContextMenuForStagedChanges()
|
||||||
{
|
{
|
||||||
if (changes.Count == 0)
|
if (_selectedStaged.Count == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
if (changes.Count == 1)
|
if (_selectedStaged.Count == 1)
|
||||||
{
|
{
|
||||||
var change = changes[0];
|
var change = _selectedStaged[0];
|
||||||
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
var path = Path.GetFullPath(Path.Combine(_repo.FullPath, change.Path));
|
||||||
|
|
||||||
var explore = new MenuItem();
|
var explore = new MenuItem();
|
||||||
|
@ -822,7 +685,7 @@ namespace SourceGit.ViewModels
|
||||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||||
unstage.Click += (o, e) =>
|
unstage.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
UnstageChanges(changes);
|
UnstageChanges(_selectedStaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -831,7 +694,7 @@ namespace SourceGit.ViewModels
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, false);
|
Discard(_selectedStaged, false);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -842,7 +705,7 @@ namespace SourceGit.ViewModels
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -864,7 +727,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -894,31 +757,31 @@ namespace SourceGit.ViewModels
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var unstage = new MenuItem();
|
var unstage = new MenuItem();
|
||||||
unstage.Header = App.Text("FileCM.UnstageMulti", changes.Count);
|
unstage.Header = App.Text("FileCM.UnstageMulti", _selectedStaged.Count);
|
||||||
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
|
||||||
unstage.Click += (o, e) =>
|
unstage.Click += (o, e) =>
|
||||||
{
|
{
|
||||||
UnstageChanges(changes);
|
UnstageChanges(_selectedStaged);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var discard = new MenuItem();
|
var discard = new MenuItem();
|
||||||
discard.Header = App.Text("FileCM.DiscardMulti", changes.Count);
|
discard.Header = App.Text("FileCM.DiscardMulti", _selectedStaged.Count);
|
||||||
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
discard.Icon = App.CreateMenuIcon("Icons.Undo");
|
||||||
discard.Click += (_, e) =>
|
discard.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
Discard(changes, false);
|
Discard(_selectedStaged, false);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var stash = new MenuItem();
|
var stash = new MenuItem();
|
||||||
stash.Header = App.Text("FileCM.StashMulti", changes.Count);
|
stash.Header = App.Text("FileCM.StashMulti", _selectedStaged.Count);
|
||||||
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
stash.Icon = App.CreateMenuIcon("Icons.Stashes");
|
||||||
stash.Click += (_, e) =>
|
stash.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
{
|
{
|
||||||
PopupHost.ShowPopup(new StashChanges(_repo, changes, false));
|
PopupHost.ShowPopup(new StashChanges(_repo, _selectedStaged, false));
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
};
|
};
|
||||||
|
@ -940,7 +803,7 @@ namespace SourceGit.ViewModels
|
||||||
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
var storageFile = await topLevel.StorageProvider.SaveFilePickerAsync(options);
|
||||||
if (storageFile != null)
|
if (storageFile != null)
|
||||||
{
|
{
|
||||||
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, changes, false, storageFile.Path.LocalPath));
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.Exec(_repo.FullPath, _selectedStaged, false, storageFile.Path.LocalPath));
|
||||||
if (succ)
|
if (succ)
|
||||||
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
}
|
}
|
||||||
|
@ -993,6 +856,139 @@ namespace SourceGit.ViewModels
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetDetail(Models.Change change)
|
||||||
|
{
|
||||||
|
if (_isLoadingData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var isUnstaged = _selectedUnstaged != null && _selectedUnstaged.Count > 0;
|
||||||
|
if (change == null)
|
||||||
|
{
|
||||||
|
DetailContext = null;
|
||||||
|
}
|
||||||
|
else if (change.IsConflit && isUnstaged)
|
||||||
|
{
|
||||||
|
DetailContext = new ConflictContext(_repo.FullPath, change);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_detailContext is DiffContext previous)
|
||||||
|
{
|
||||||
|
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged), previous);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DetailContext = new DiffContext(_repo.FullPath, new Models.DiffOption(change, isUnstaged));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void UseTheirs(List<Models.Change> changes)
|
||||||
|
{
|
||||||
|
var files = new List<string>();
|
||||||
|
foreach (var change in changes)
|
||||||
|
{
|
||||||
|
if (change.IsConflit)
|
||||||
|
files.Add(change.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseTheirs(files));
|
||||||
|
if (succ)
|
||||||
|
{
|
||||||
|
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
||||||
|
}
|
||||||
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void UseMine(List<Models.Change> changes)
|
||||||
|
{
|
||||||
|
var files = new List<string>();
|
||||||
|
foreach (var change in changes)
|
||||||
|
{
|
||||||
|
if (change.IsConflit)
|
||||||
|
files.Add(change.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
var succ = await Task.Run(() => new Commands.Checkout(_repo.FullPath).UseMine(files));
|
||||||
|
if (succ)
|
||||||
|
{
|
||||||
|
await Task.Run(() => new Commands.Add(_repo.FullPath, changes).Exec());
|
||||||
|
}
|
||||||
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void UseExternalMergeTool(Models.Change change)
|
||||||
|
{
|
||||||
|
var type = Preference.Instance.ExternalMergeToolType;
|
||||||
|
var exec = Preference.Instance.ExternalMergeToolPath;
|
||||||
|
|
||||||
|
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
|
||||||
|
if (tool == null)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Invalid merge tool in preference setting!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = tool.Type != 0 ? tool.Cmd : Preference.Instance.ExternalMergeToolCmd;
|
||||||
|
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
await Task.Run(() => Commands.MergeTool.OpenForMerge(_repo.FullPath, exec, args, change.Path));
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoCommit(bool autoPush)
|
||||||
|
{
|
||||||
|
if (!PopupHost.CanCreatePopup())
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Repository has unfinished job! Please wait!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_staged.Count == 0)
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "No files added to commit!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_commitMessage))
|
||||||
|
{
|
||||||
|
App.RaiseException(_repo.FullPath, "Commit without message is NOT allowed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushCommitMessage();
|
||||||
|
|
||||||
|
SetDetail(null);
|
||||||
|
IsCommitting = true;
|
||||||
|
_repo.SetWatcherEnabled(false);
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
var succ = new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend).Exec();
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
if (succ)
|
||||||
|
{
|
||||||
|
CommitMessage = string.Empty;
|
||||||
|
UseAmend = false;
|
||||||
|
|
||||||
|
if (autoPush)
|
||||||
|
{
|
||||||
|
PopupHost.ShowAndStartPopup(new Push(_repo, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_repo.MarkWorkingCopyDirtyManually();
|
||||||
|
_repo.SetWatcherEnabled(true);
|
||||||
|
|
||||||
|
IsCommitting = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void PushCommitMessage()
|
private void PushCommitMessage()
|
||||||
{
|
{
|
||||||
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
|
var existIdx = _repo.CommitMessages.IndexOf(CommitMessage);
|
||||||
|
@ -1022,13 +1018,9 @@ namespace SourceGit.ViewModels
|
||||||
private bool _useAmend = false;
|
private bool _useAmend = false;
|
||||||
private List<Models.Change> _unstaged = null;
|
private List<Models.Change> _unstaged = null;
|
||||||
private List<Models.Change> _staged = null;
|
private List<Models.Change> _staged = null;
|
||||||
private Models.Change _selectedUnstagedChange = null;
|
private List<Models.Change> _selectedUnstaged = null;
|
||||||
private Models.Change _selectedStagedChange = null;
|
private List<Models.Change> _selectedStaged = null;
|
||||||
private int _count = 0;
|
private int _count = 0;
|
||||||
private List<FileTreeNode> _unstagedTree = null;
|
|
||||||
private List<FileTreeNode> _stagedTree = null;
|
|
||||||
private FileTreeNode _selectedUnstagedTreeNode = null;
|
|
||||||
private FileTreeNode _selectedStagedTreeNode = null;
|
|
||||||
private object _detailContext = null;
|
private object _detailContext = null;
|
||||||
private string _commitMessage = string.Empty;
|
private string _commitMessage = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
46
src/Views/ChangeCollectionView.axaml
Normal file
46
src/Views/ChangeCollectionView.axaml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
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"
|
||||||
|
xmlns:m="using:SourceGit.Models"
|
||||||
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
|
xmlns:v="using:SourceGit.Views"
|
||||||
|
xmlns:c="using:SourceGit.Converters"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="SourceGit.Views.ChangeCollectionView"
|
||||||
|
x:Name="me">
|
||||||
|
<TreeDataGrid x:Name="tree"
|
||||||
|
AutoDragDropRows="False"
|
||||||
|
ShowColumnHeaders="False"
|
||||||
|
CanUserResizeColumns="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
ScrollViewer.BringIntoViewOnFocusChange="True">
|
||||||
|
<TreeDataGrid.Resources>
|
||||||
|
<DataTemplate x:Key="TreeModeTemplate" DataType="vm:FileTreeNode">
|
||||||
|
<Grid HorizontalAlignment="Stretch" Height="24" ColumnDefinitions="Auto,*">
|
||||||
|
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
||||||
|
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="{Binding #me.IsWorkingCopyChange}" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
||||||
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="ListModeTemplate" DataType="m:Change">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="{Binding #me.IsWorkingCopyChange}" Change="{Binding}" Margin="4,0,0,0"/>
|
||||||
|
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="GridModeFileTemplate" DataType="m:Change">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="{Binding #me.IsWorkingCopyChange}" Change="{Binding}" Margin="4,0,0,0"/>
|
||||||
|
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate x:Key="GridModeDirTemplate" DataType="m:Change">
|
||||||
|
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Foreground="{DynamicResource Brush.FG2}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</TreeDataGrid.Resources>
|
||||||
|
</TreeDataGrid>
|
||||||
|
</UserControl>
|
271
src/Views/ChangeCollectionView.axaml.cs
Normal file
271
src/Views/ChangeCollectionView.axaml.cs
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Models.TreeDataGrid;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
|
namespace SourceGit.Views
|
||||||
|
{
|
||||||
|
public partial class ChangeCollectionView : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<bool> IsWorkingCopyChangeProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(IsWorkingCopy), false);
|
||||||
|
|
||||||
|
public bool IsWorkingCopy
|
||||||
|
{
|
||||||
|
get => GetValue(IsWorkingCopyChangeProperty);
|
||||||
|
set => SetValue(IsWorkingCopyChangeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> SingleSelectProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(SingleSelect), true);
|
||||||
|
|
||||||
|
public bool SingleSelect
|
||||||
|
{
|
||||||
|
get => GetValue(SingleSelectProperty);
|
||||||
|
set => SetValue(SingleSelectProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Models.ChangeViewMode> ViewModeProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, Models.ChangeViewMode>(nameof(ViewMode), Models.ChangeViewMode.Tree);
|
||||||
|
|
||||||
|
public Models.ChangeViewMode ViewMode
|
||||||
|
{
|
||||||
|
get => GetValue(ViewModeProperty);
|
||||||
|
set => SetValue(ViewModeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<List<Models.Change>> ChangesProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, List<Models.Change>>(nameof(Changes), null);
|
||||||
|
|
||||||
|
public List<Models.Change> Changes
|
||||||
|
{
|
||||||
|
get => GetValue(ChangesProperty);
|
||||||
|
set => SetValue(ChangesProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<List<Models.Change>> SelectedChangesProperty =
|
||||||
|
AvaloniaProperty.Register<ChangeCollectionView, List<Models.Change>>(nameof(SelectedChanges), null);
|
||||||
|
|
||||||
|
public List<Models.Change> SelectedChanges
|
||||||
|
{
|
||||||
|
get => GetValue(SelectedChangesProperty);
|
||||||
|
set => SetValue(SelectedChangesProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly RoutedEvent<RoutedEventArgs> ChangeDoubleTappedEvent =
|
||||||
|
RoutedEvent.Register<ChangeCollectionView, RoutedEventArgs>(nameof(ChangeDoubleTapped), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
|
||||||
|
|
||||||
|
public event EventHandler<RoutedEventArgs> ChangeDoubleTapped
|
||||||
|
{
|
||||||
|
add { AddHandler(ChangeDoubleTappedEvent, value); }
|
||||||
|
remove { RemoveHandler(ChangeDoubleTappedEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChangeCollectionView()
|
||||||
|
{
|
||||||
|
ViewModeProperty.Changed.AddClassHandler<ChangeCollectionView>((c, e) => c.UpdateSource());
|
||||||
|
ChangesProperty.Changed.AddClassHandler<ChangeCollectionView>((c, e) => c.UpdateSource());
|
||||||
|
SelectedChangesProperty.Changed.AddClassHandler<ChangeCollectionView>((c, e) => c.UpdateSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangeCollectionView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSource()
|
||||||
|
{
|
||||||
|
if (tree.Source is IDisposable disposable)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
tree.Source = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var changes = Changes;
|
||||||
|
if (changes == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var viewMode = ViewMode;
|
||||||
|
if (viewMode == Models.ChangeViewMode.Tree)
|
||||||
|
{
|
||||||
|
var filetree = ViewModels.FileTreeNode.Build(changes, true);
|
||||||
|
var source = new HierarchicalTreeDataGridSource<ViewModels.FileTreeNode>(filetree)
|
||||||
|
{
|
||||||
|
Columns =
|
||||||
|
{
|
||||||
|
new HierarchicalExpanderColumn<ViewModels.FileTreeNode>(
|
||||||
|
new TemplateColumn<ViewModels.FileTreeNode>(null, "TreeModeTemplate", null, GridLength.Auto),
|
||||||
|
x => x.Children,
|
||||||
|
x => x.Children.Count > 0,
|
||||||
|
x => x.IsExpanded),
|
||||||
|
new TextColumn<ViewModels.FileTreeNode, string>(
|
||||||
|
null,
|
||||||
|
x => string.Empty,
|
||||||
|
GridLength.Star)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<ViewModels.FileTreeNode>(source, x => x.Children);
|
||||||
|
selection.RowDoubleTapped += (_, e) => RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
source.RowSelection.SingleSelect = SingleSelect;
|
||||||
|
source.RowSelection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (!_isSelecting && s is Models.TreeDataGridSelectionModel<ViewModels.FileTreeNode> model)
|
||||||
|
{
|
||||||
|
var selection = new List<Models.Change>();
|
||||||
|
foreach (var c in model.SelectedItems)
|
||||||
|
CollectChangesInNode(selection, c);
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
SetCurrentValue(SelectedChangesProperty, selection);
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.Source = source;
|
||||||
|
}
|
||||||
|
else if (viewMode == Models.ChangeViewMode.List)
|
||||||
|
{
|
||||||
|
var source = new FlatTreeDataGridSource<Models.Change>(changes)
|
||||||
|
{
|
||||||
|
Columns = { new TemplateColumn<Models.Change>(null, "ListModeTemplate", null, GridLength.Auto) }
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<Models.Change>(source, null);
|
||||||
|
selection.RowDoubleTapped += (_, e) => RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
source.RowSelection.SingleSelect = SingleSelect;
|
||||||
|
source.RowSelection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (!_isSelecting && s is Models.TreeDataGridSelectionModel<Models.Change> model)
|
||||||
|
{
|
||||||
|
var selection = new List<Models.Change>();
|
||||||
|
foreach (var c in model.SelectedItems)
|
||||||
|
selection.Add(c);
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
SetCurrentValue(SelectedChangesProperty, selection);
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.Source = source;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var source = new FlatTreeDataGridSource<Models.Change>(changes)
|
||||||
|
{
|
||||||
|
Columns =
|
||||||
|
{
|
||||||
|
new TemplateColumn<Models.Change>(null, "GridModeFileTemplate", null, GridLength.Auto),
|
||||||
|
new TemplateColumn<Models.Change>(null, "GridModeDirTemplate", null, GridLength.Auto)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var selection = new Models.TreeDataGridSelectionModel<Models.Change>(source, null);
|
||||||
|
selection.RowDoubleTapped += (_, e) => RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
|
||||||
|
|
||||||
|
source.Selection = selection;
|
||||||
|
source.RowSelection.SingleSelect = SingleSelect;
|
||||||
|
source.RowSelection.SelectionChanged += (s, _) =>
|
||||||
|
{
|
||||||
|
if (!_isSelecting && s is Models.TreeDataGridSelectionModel<Models.Change> model)
|
||||||
|
{
|
||||||
|
var selection = new List<Models.Change>();
|
||||||
|
foreach (var c in model.SelectedItems)
|
||||||
|
selection.Add(c);
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
SetCurrentValue(SelectedChangesProperty, selection);
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.Source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSelected()
|
||||||
|
{
|
||||||
|
if (_isSelecting || tree.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isSelecting = true;
|
||||||
|
var selected = SelectedChanges;
|
||||||
|
if (tree.Source.Selection is Models.TreeDataGridSelectionModel<Models.Change> changeSelection)
|
||||||
|
{
|
||||||
|
if (selected == null || selected.Count == 0)
|
||||||
|
changeSelection.Clear();
|
||||||
|
else
|
||||||
|
changeSelection.Select(selected);
|
||||||
|
}
|
||||||
|
else if (tree.Source.Selection is Models.TreeDataGridSelectionModel<ViewModels.FileTreeNode> treeSelection)
|
||||||
|
{
|
||||||
|
if (selected == null || selected.Count == 0)
|
||||||
|
{
|
||||||
|
treeSelection.Clear();
|
||||||
|
_isSelecting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var set = new HashSet<object>();
|
||||||
|
foreach (var c in selected)
|
||||||
|
set.Add(c);
|
||||||
|
|
||||||
|
var nodes = new List<ViewModels.FileTreeNode>();
|
||||||
|
foreach (var node in tree.Source.Items)
|
||||||
|
CollectSelectedNodeByChange(nodes, node as ViewModels.FileTreeNode, set);
|
||||||
|
|
||||||
|
if (nodes.Count == 0)
|
||||||
|
{
|
||||||
|
treeSelection.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
treeSelection.Select(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_isSelecting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectChangesInNode(List<Models.Change> outs, ViewModels.FileTreeNode node)
|
||||||
|
{
|
||||||
|
if (node.IsFolder)
|
||||||
|
{
|
||||||
|
foreach (var child in node.Children)
|
||||||
|
CollectChangesInNode(outs, child);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var change = node.Backend as Models.Change;
|
||||||
|
if (change != null && !outs.Contains(change))
|
||||||
|
outs.Add(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectSelectedNodeByChange(List<ViewModels.FileTreeNode> outs, ViewModels.FileTreeNode node, HashSet<object> selected)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node.IsFolder)
|
||||||
|
{
|
||||||
|
foreach (var child in node.Children)
|
||||||
|
CollectSelectedNodeByChange(outs, child, selected);
|
||||||
|
}
|
||||||
|
else if (node.Backend != null && selected.Contains(node.Backend))
|
||||||
|
{
|
||||||
|
outs.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isSelecting = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,17 +14,17 @@
|
||||||
<MenuFlyout Placement="BottomEdgeAlignedLeft">
|
<MenuFlyout Placement="BottomEdgeAlignedLeft">
|
||||||
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.List}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.List}">
|
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.List}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.List}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Path Width="12" Height="12" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.List}"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.List}"/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Grid}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Grid}">
|
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Grid}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Grid}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Path Width="12" Height="12" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.Grid}"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.Grid}"/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Tree}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Tree}">
|
<MenuItem Header="{DynamicResource Text.ChangeDisplayMode.Tree}" Command="{Binding SwitchMode}" CommandParameter="{x:Static m:ChangeViewMode.Tree}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Path Width="12" Height="12" Fill="{DynamicResource Brush.FG2}" Data="{StaticResource Icons.Tree}"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.Tree}"/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuFlyout>
|
</MenuFlyout>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
<Grid Grid.Column="0" RowDefinitions="26,*">
|
<Grid Grid.Column="0" RowDefinitions="26,*">
|
||||||
<!-- Search & Display Mode -->
|
<!-- Search & Display Mode -->
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,24">
|
<Grid Grid.Row="0" ColumnDefinitions="*,18">
|
||||||
<TextBox Grid.Column="0"
|
<TextBox Grid.Column="0"
|
||||||
Height="26"
|
Height="26"
|
||||||
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
||||||
|
@ -39,116 +39,18 @@
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="1"
|
<v:ChangeViewModeSwitcher Grid.Column="1"
|
||||||
Width="18" Height="18"
|
Width="14" Height="14"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Changes -->
|
<!-- Changes -->
|
||||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<Grid>
|
<v:ChangeCollectionView IsWorkingCopy="False"
|
||||||
<DataGrid Background="Transparent"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
Changes="{Binding VisibleChanges}"
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||||
SelectionMode="Single"
|
ContextRequested="OnChangeContextRequested"/>
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid Background="Transparent"
|
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Single"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView ItemsSource="{Binding ChangeTree}"
|
|
||||||
SelectedItem="{Binding SelectedChangeNode, Mode=TwoWay}"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
ContextRequested="OnTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
|
@ -9,37 +10,15 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.IsVisible && datagrid.SelectedItem != null)
|
if (DataContext is ViewModels.CommitDetail vm)
|
||||||
{
|
{
|
||||||
datagrid.ScrollIntoView(datagrid.SelectedItem, null);
|
var selected = (sender as ChangeCollectionView)?.SelectedChanges;
|
||||||
}
|
if (selected != null && selected.Count == 1)
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataGridContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
var menu = vm.CreateChangeContextMenu(selected[0]);
|
||||||
{
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
|
||||||
var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is TreeView view && view.SelectedItem != null)
|
|
||||||
{
|
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
|
||||||
var node = view.SelectedItem as ViewModels.FileTreeNode;
|
|
||||||
if (node != null && !node.IsFolder)
|
|
||||||
{
|
|
||||||
var menu = detail.CreateChangeContextMenu(node.Backend as Models.Change);
|
|
||||||
view.OpenContextMenu(menu);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,40 +22,20 @@
|
||||||
<v:CommitBaseInfo Grid.Row="0" Content="{Binding Commit}"/>
|
<v:CommitBaseInfo Grid.Row="0" Content="{Binding Commit}"/>
|
||||||
|
|
||||||
<!-- Change List -->
|
<!-- Change List -->
|
||||||
<DataGrid Grid.Row="1"
|
<v:ChangeCollectionView Grid.Row="1"
|
||||||
Background="Transparent"
|
Margin="72,0,8,0"
|
||||||
ItemsSource="{Binding Changes}"
|
IsWorkingCopy="False"
|
||||||
SelectionMode="Single"
|
ViewMode="List"
|
||||||
CanUserReorderColumns="False"
|
Changes="{Binding Changes}"
|
||||||
CanUserResizeColumns="False"
|
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
Margin="80,0,8,0"
|
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
ContextRequested="OnChangeListContextRequested"
|
ContextRequested="OnChangeListContextRequested"
|
||||||
DoubleTapped="OnChangeListDoubleTapped">
|
ChangeDoubleTapped="OnChangeDoubleTapped">
|
||||||
<DataGrid.Columns>
|
<v:ChangeCollectionView.Styles>
|
||||||
<DataGridTemplateColumn Header="ICON">
|
<Style Selector="TreeDataGrid">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||||
<DataTemplate>
|
</Style>
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
</v:ChangeCollectionView.Styles>
|
||||||
</DataTemplate>
|
</v:ChangeCollectionView>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="8,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
|
@ -10,30 +10,30 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnChangeListDoubleTapped(object sender, TappedEventArgs e)
|
|
||||||
{
|
|
||||||
if (DataContext is ViewModels.CommitDetail detail)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
detail.ActivePageIndex = 1;
|
|
||||||
detail.SelectedChange = datagrid.SelectedItem as Models.Change;
|
|
||||||
}
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChangeListContextRequested(object sender, ContextRequestedEventArgs e)
|
private void OnChangeListContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is ViewModels.CommitDetail detail)
|
if (DataContext is ViewModels.CommitDetail vm && sender is ChangeCollectionView view)
|
||||||
{
|
{
|
||||||
var datagrid = sender as DataGrid;
|
var selected = view.SelectedChanges;
|
||||||
if (datagrid.SelectedItem == null)
|
if (selected != null && selected.Count == 1)
|
||||||
{
|
{
|
||||||
|
var menu = vm.CreateChangeContextMenu(selected[0]);
|
||||||
|
view.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var menu = detail.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
private void OnChangeDoubleTapped(object sender, RoutedEventArgs e)
|
||||||
datagrid.OpenContextMenu(menu);
|
{
|
||||||
|
if (DataContext is ViewModels.CommitDetail vm && sender is ChangeCollectionView view)
|
||||||
|
{
|
||||||
|
var selected = view.SelectedChanges;
|
||||||
|
if (selected != null && selected.Count == 1)
|
||||||
|
{
|
||||||
|
vm.ActivePageIndex = 1;
|
||||||
|
vm.SelectedChanges = new() { selected[0] };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
|
|
||||||
<Grid Grid.Column="0" RowDefinitions="26,*">
|
<Grid Grid.Column="0" RowDefinitions="26,*">
|
||||||
<!-- Search & Display Mode -->
|
<!-- Search & Display Mode -->
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,24">
|
<Grid Grid.Row="0" ColumnDefinitions="*,18">
|
||||||
<TextBox Grid.Column="0"
|
<TextBox Grid.Column="0"
|
||||||
Height="26"
|
Height="26"
|
||||||
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}"
|
||||||
|
@ -94,116 +94,18 @@
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="1"
|
<v:ChangeViewModeSwitcher Grid.Column="1"
|
||||||
Width="18" Height="18"
|
Width="14" Height="14"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Changes -->
|
<!-- Changes -->
|
||||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<Grid>
|
<v:ChangeCollectionView IsWorkingCopy="False"
|
||||||
<DataGrid Background="Transparent"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode}"
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
Changes="{Binding VisibleChanges}"
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
SelectedChanges="{Binding SelectedChanges, Mode=TwoWay}"
|
||||||
SelectionMode="Single"
|
ContextRequested="OnChangeContextRequested"/>
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid Background="Transparent"
|
|
||||||
ItemsSource="{Binding VisibleChanges}"
|
|
||||||
SelectedItem="{Binding SelectedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Single"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
SelectionChanged="OnDataGridSelectionChanged"
|
|
||||||
ContextRequested="OnDataGridContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView ItemsSource="{Binding ChangeTree}"
|
|
||||||
SelectedItem="{Binding SelectedNode, Mode=TwoWay}"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
ContextRequested="OnTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=CommitChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -10,50 +10,21 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDataGridSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void OnChangeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGrid datagrid && datagrid.IsVisible)
|
if (DataContext is ViewModels.RevisionCompare vm && sender is ChangeCollectionView view)
|
||||||
{
|
{
|
||||||
datagrid.ScrollIntoView(datagrid.SelectedItem, null);
|
var menu = vm.CreateChangeContextMenu();
|
||||||
}
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataGridContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is DataGrid datagrid && datagrid.SelectedItem != null)
|
|
||||||
{
|
|
||||||
var compare = DataContext as ViewModels.RevisionCompare;
|
|
||||||
var menu = compare.CreateChangeContextMenu(datagrid.SelectedItem as Models.Change);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is TreeView view && view.SelectedItem != null)
|
|
||||||
{
|
|
||||||
var compare = DataContext as ViewModels.RevisionCompare;
|
|
||||||
var node = view.SelectedItem as ViewModels.FileTreeNode;
|
|
||||||
if (node != null && !node.IsFolder)
|
|
||||||
{
|
|
||||||
var menu = compare.CreateChangeContextMenu(node.Backend as Models.Change);
|
|
||||||
view.OpenContextMenu(menu);
|
view.OpenContextMenu(menu);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPressedSHA(object sender, PointerPressedEventArgs e)
|
private void OnPressedSHA(object sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is TextBlock block)
|
if (DataContext is ViewModels.RevisionCompare vm && sender is TextBlock block)
|
||||||
{
|
vm.NavigateTo(block.Text);
|
||||||
var compare = DataContext as ViewModels.RevisionCompare;
|
|
||||||
compare.NavigateTo(block.Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,29 +41,22 @@
|
||||||
|
|
||||||
<!-- File Tree -->
|
<!-- File Tree -->
|
||||||
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Row="1" Margin="0,4,0,0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<TreeView Grid.Row="5"
|
<TreeDataGrid AutoDragDropRows="False"
|
||||||
ItemsSource="{Binding RevisionFilesTree}"
|
ShowColumnHeaders="False"
|
||||||
SelectedItem="{Binding SelectedRevisionFileNode, Mode=TwoWay}"
|
CanUserResizeColumns="False"
|
||||||
AutoScrollToSelectedItem="True"
|
CanUserSortColumns="False"
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
Source="{Binding RevisionFiles}"
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
ContextRequested="OnFileContextRequested">
|
||||||
ContextRequested="OnTreeViewContextRequested">
|
<TreeDataGrid.Resources>
|
||||||
<TreeView.Styles>
|
<DataTemplate x:Key="FileTreeNodeExpanderTemplate" DataType="vm:FileTreeNode">
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
<Grid HorizontalAlignment="Stretch" Height="24" ColumnDefinitions="Auto,*">
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
||||||
<Path Grid.Column="0" Width="14" Height="14" IsVisible="{Binding !IsFolder}" Data="{StaticResource Icons.File}" VerticalAlignment="Center"/>
|
<Path Grid.Column="0" Width="14" Height="14" IsVisible="{Binding !IsFolder}" Data="{StaticResource Icons.File}" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TreeDataTemplate>
|
</DataTemplate>
|
||||||
</TreeView.ItemTemplate>
|
</TreeDataGrid.Resources>
|
||||||
</TreeView>
|
</TreeDataGrid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
|
@ -213,14 +213,16 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
private void OnFileContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
if (DataContext is ViewModels.CommitDetail vm && sender is TreeDataGrid tree)
|
||||||
var node = detail.SelectedRevisionFileNode;
|
|
||||||
if (!node.IsFolder)
|
|
||||||
{
|
{
|
||||||
var menu = detail.CreateRevisionFileContextMenu(node.Backend as Models.Object);
|
var selected = tree.RowSelection.SelectedItem as ViewModels.FileTreeNode;
|
||||||
(sender as Control)?.OpenContextMenu(menu);
|
if (selected != null && !selected.IsFolder && selected.Backend is Models.Object obj)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateRevisionFileContextMenu(obj);
|
||||||
|
tree.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<!-- Unstaged Toolbar -->
|
<!-- Unstaged Toolbar -->
|
||||||
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto,Auto,Auto">
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="0" Width="14" Height="14" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Mode=TwoWay}"/>
|
<v:ChangeViewModeSwitcher Grid.Column="0" Width="12" Height="12" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Mode=TwoWay}"/>
|
||||||
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Unstaged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Unstaged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Unstaged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Unstaged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
||||||
<Path Grid.Column="3" Classes="rotating" Width="14" Height="14" Data="{StaticResource Icons.Loading}" Margin="8,0,0,0" IsVisible="{Binding IsStaging}"/>
|
<Path Grid.Column="3" Classes="rotating" Width="14" Height="14" Data="{StaticResource Icons.Loading}" Margin="8,0,0,0" IsVisible="{Binding IsStaging}"/>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.ViewAssumeUnchaged}"
|
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.ViewAssumeUnchaged}"
|
||||||
Click="ViewAssumeUnchanged">
|
Command="{Binding OpenAssumeUnchanged}">
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.File.Ignore}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.File.Ignore}"/>
|
||||||
</Button>
|
</Button>
|
||||||
<ToggleButton Grid.Column="6"
|
<ToggleButton Grid.Column="6"
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
Click="StageSelected">
|
Command="{Binding StageSelected}">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{DynamicResource Text.WorkingCopy.Unstaged.Stage}" VerticalAlignment="Center"/>
|
<TextBlock Text="{DynamicResource Text.WorkingCopy.Unstaged.Stage}" VerticalAlignment="Center"/>
|
||||||
|
@ -56,130 +56,33 @@
|
||||||
Classes="icon_button"
|
Classes="icon_button"
|
||||||
Width="26" Height="14"
|
Width="26" Height="14"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.StageAll}" Click="StageAll">
|
ToolTip.Tip="{DynamicResource Text.WorkingCopy.Unstaged.StageAll}"
|
||||||
|
Command="{Binding StageAll}">
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleDown}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleDown}"/>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Unstaged Changes -->
|
<!-- Unstaged Changes -->
|
||||||
<Grid Grid.Row="1" Background="{DynamicResource Brush.Contents}">
|
<v:ChangeCollectionView Grid.Row="1"
|
||||||
<DataGrid x:Name="unstagedList"
|
IsWorkingCopy="True"
|
||||||
Background="Transparent"
|
SingleSelect="False"
|
||||||
ItemsSource="{Binding Unstaged}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
SelectedItem="{Binding SelectedUnstagedChange, Mode=TwoWay}"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode}"
|
||||||
SelectionMode="Extended"
|
Changes="{Binding Unstaged}"
|
||||||
CanUserReorderColumns="False"
|
SelectedChanges="{Binding SelectedUnstaged, Mode=TwoWay}"
|
||||||
CanUserResizeColumns="False"
|
ContextRequested="OnUnstagedContextRequested"
|
||||||
CanUserSortColumns="False"
|
ChangeDoubleTapped="OnUnstagedChangeDoubleTapped"
|
||||||
IsReadOnly="True"
|
KeyDown="OnUnstagedKeyDown"/>
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnUnstagedListKeyDown"
|
|
||||||
ContextRequested="OnUnstagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="True" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid x:Name="unstagedGrid"
|
|
||||||
Background="Transparent"
|
|
||||||
ItemsSource="{Binding Unstaged}"
|
|
||||||
SelectedItem="{Binding SelectedUnstagedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnUnstagedListKeyDown"
|
|
||||||
ContextRequested="OnUnstagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="True" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView x:Name="unstagedTree"
|
|
||||||
ItemsSource="{Binding UnstagedTree}"
|
|
||||||
SelectedItem="{Binding SelectedUnstagedTreeNode, Mode=TwoWay}"
|
|
||||||
SelectionMode="Multiple"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnUnstagedTreeViewKeyDown"
|
|
||||||
ContextRequested="OnUnstagedTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=UnstagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="True" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Staged Toolbar -->
|
<!-- Staged Toolbar -->
|
||||||
<Border Grid.Row="2" BorderThickness="0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
<Border Grid.Row="2" BorderThickness="0,1" BorderBrush="{DynamicResource Brush.Border0}">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto">
|
<Grid ColumnDefinitions="Auto,Auto,Auto,Auto,*,Auto,Auto">
|
||||||
<v:ChangeViewModeSwitcher Grid.Column="0" Width="14" Height="14" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}"/>
|
<v:ChangeViewModeSwitcher Grid.Column="0" Width="12" Height="12" Margin="8,0,0,0" ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}"/>
|
||||||
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Staged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
<TextBlock Grid.Column="1" Text="{DynamicResource Text.WorkingCopy.Staged}" Foreground="{DynamicResource Brush.FG2}" FontWeight="Bold" Margin="8,0,0,0"/>
|
||||||
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Staged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
<TextBlock Grid.Column="2" FontWeight="Bold" Foreground="{DynamicResource Brush.FG2}" Text="{Binding Staged, Converter={x:Static c:ListConverters.ToCount}}"/>
|
||||||
<Path Grid.Column="3" Classes="rotating" Width="14" Height="14" Data="{StaticResource Icons.Loading}" Margin="8,0,0,0" IsVisible="{Binding IsUnstaging}"/>
|
<Path Grid.Column="3" Classes="rotating" Width="14" Height="14" Data="{StaticResource Icons.Loading}" Margin="8,0,0,0" IsVisible="{Binding IsUnstaging}"/>
|
||||||
<Button Grid.Column="5" Classes="icon_button" Width="26" Height="14" Padding="0" Click="UnstageSelected">
|
<Button Grid.Column="5" Classes="icon_button" Width="26" Height="14" Padding="0" Command="{Binding UnstageSelected}">
|
||||||
<ToolTip.Tip>
|
<ToolTip.Tip>
|
||||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{DynamicResource Text.WorkingCopy.Staged.Unstage}" VerticalAlignment="Center"/>
|
<TextBlock Text="{DynamicResource Text.WorkingCopy.Staged.Unstage}" VerticalAlignment="Center"/>
|
||||||
|
@ -188,121 +91,23 @@
|
||||||
</ToolTip.Tip>
|
</ToolTip.Tip>
|
||||||
<Path Width="14" Height="14" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
<Path Width="14" Height="14" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Grid.Column="6" Classes="icon_button" Width="26" Height="14" Padding="0" ToolTip.Tip="{DynamicResource Text.WorkingCopy.Staged.UnstageAll}" Click="UnstageAll">
|
<Button Grid.Column="6" Classes="icon_button" Width="26" Height="14" Padding="0" ToolTip.Tip="{DynamicResource Text.WorkingCopy.Staged.UnstageAll}" Command="{Binding UnstageAll}">
|
||||||
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleUp}"/>
|
<Path Width="14" Height="14" Data="{StaticResource Icons.DoubleUp}"/>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Staged Changes -->
|
<!-- Staged Changes -->
|
||||||
<Grid Grid.Row="3" Background="{DynamicResource Brush.Contents}">
|
<v:ChangeCollectionView Grid.Row="3"
|
||||||
<DataGrid x:Name="stagedList"
|
IsWorkingCopy="False"
|
||||||
Background="Transparent"
|
SingleSelect="False"
|
||||||
ItemsSource="{Binding Staged}"
|
Background="{DynamicResource Brush.Contents}"
|
||||||
SelectedItem="{Binding SelectedStagedChange, Mode=TwoWay}"
|
ViewMode="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode}"
|
||||||
SelectionMode="Extended"
|
Changes="{Binding Staged}"
|
||||||
CanUserReorderColumns="False"
|
SelectedChanges="{Binding SelectedStaged, Mode=TwoWay}"
|
||||||
CanUserResizeColumns="False"
|
ContextRequested="OnStagedContextRequested"
|
||||||
CanUserSortColumns="False"
|
ChangeDoubleTapped="OnStagedChangeDoubleTapped"
|
||||||
IsReadOnly="True"
|
KeyDown="OnStagedKeyDown"/>
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnStagedListKeyDown"
|
|
||||||
ContextRequested="OnStagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsList}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="*" Header="PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<DataGrid x:Name="stagedGrid"
|
|
||||||
Background="Transparent"
|
|
||||||
ItemsSource="{Binding Staged}"
|
|
||||||
SelectedItem="{Binding SelectedStagedChange, Mode=TwoWay}"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
CanUserReorderColumns="False"
|
|
||||||
CanUserResizeColumns="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
IsReadOnly="True"
|
|
||||||
HeadersVisibility="None"
|
|
||||||
Focusable="False"
|
|
||||||
RowHeight="26"
|
|
||||||
HorizontalScrollBarVisibility="Auto"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnStagedListKeyDown"
|
|
||||||
ContextRequested="OnStagedListContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsGrid}}">
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTemplateColumn Header="ICON">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<v:ChangeStatusIcon Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FILE_NAME">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureFileName}}" Margin="4,0,0,0"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="FOLDER_PATH">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Classes="monospace" Text="{Binding Path, Converter={x:Static c:PathConverters.PureDirectoryName}}" Margin="4,0,0,0" Foreground="{DynamicResource Brush.FG2}"/>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
|
|
||||||
<TreeView x:Name="stagedTree"
|
|
||||||
ItemsSource="{Binding StagedTree}"
|
|
||||||
SelectedItem="{Binding SelectedStagedTreeNode, Mode=TwoWay}"
|
|
||||||
SelectionMode="Multiple"
|
|
||||||
AutoScrollToSelectedItem="True"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
|
||||||
KeyDown="OnStagedTreeViewKeyDown"
|
|
||||||
ContextRequested="OnStagedTreeViewContextRequested"
|
|
||||||
IsVisible="{Binding Source={x:Static vm:Preference.Instance}, Path=StagedChangeViewMode, Converter={x:Static c:ChangeViewModeConverters.IsTree}}">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeViewItem" x:DataType="vm:FileTreeNode">
|
|
||||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}" x:DataType="{x:Type vm:FileTreeNode}">
|
|
||||||
<Grid Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<v:ChangeStatusIcon Grid.Column="0" Width="14" Height="14" IsWorkingCopyChange="False" Change="{Binding Backend}" IsVisible="{Binding !IsFolder}"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<GridSplitter Grid.Column="1"
|
<GridSplitter Grid.Column="1"
|
||||||
|
@ -384,8 +189,7 @@
|
||||||
Margin="12,0,0,0"
|
Margin="12,0,0,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
IsChecked="{Binding UseAmend, Mode=TwoWay}"
|
IsChecked="{Binding UseAmend, Mode=TwoWay}"
|
||||||
Content="{DynamicResource Text.WorkingCopy.Amend}"
|
Content="{DynamicResource Text.WorkingCopy.Amend}"/>
|
||||||
Checked="StartAmend"/>
|
|
||||||
|
|
||||||
<Path Grid.Column="3"
|
<Path Grid.Column="3"
|
||||||
Classes="rotating"
|
Classes="rotating"
|
||||||
|
@ -399,7 +203,7 @@
|
||||||
Height="28"
|
Height="28"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
Padding="8,0"
|
Padding="8,0"
|
||||||
Click="Commit"/>
|
Command="{Binding Commit}"/>
|
||||||
|
|
||||||
<Button Grid.Column="5"
|
<Button Grid.Column="5"
|
||||||
Classes="flat"
|
Classes="flat"
|
||||||
|
@ -407,7 +211,7 @@
|
||||||
Height="28"
|
Height="28"
|
||||||
Margin="8,0,0,0"
|
Margin="8,0,0,0"
|
||||||
Padding="8,0"
|
Padding="8,0"
|
||||||
Click="CommitWithPush">
|
Command="{Binding CommitWithPush}">
|
||||||
<Button.IsVisible>
|
<Button.IsVisible>
|
||||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||||
<Binding Path="$parent[v:Repository].DataContext.(vm:Repository).CanCommitWithPush"/>
|
<Binding Path="$parent[v:Repository].DataContext.(vm:Repository).CanCommitWithPush"/>
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.VisualTree;
|
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
|
@ -14,313 +11,6 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ViewAssumeUnchanged(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var repoPage = this.FindAncestorOfType<Repository>();
|
|
||||||
if (repoPage != null)
|
|
||||||
{
|
|
||||||
var repo = (repoPage.DataContext as ViewModels.Repository).FullPath;
|
|
||||||
var window = new AssumeUnchangedManager();
|
|
||||||
window.DataContext = new ViewModels.AssumeUnchangedManager(repo);
|
|
||||||
window.ShowDialog((Window)TopLevel.GetTopLevel(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StageSelected(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
switch (ViewModels.Preference.Instance.UnstagedChangeViewMode)
|
|
||||||
{
|
|
||||||
case Models.ChangeViewMode.List:
|
|
||||||
foreach (var item in unstagedList.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Models.ChangeViewMode.Grid:
|
|
||||||
foreach (var item in unstagedGrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
foreach (var item in unstagedTree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.StageChanges(selected);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StageAll(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vm.StageChanges(vm.Unstaged);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnstageSelected(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
switch (ViewModels.Preference.Instance.StagedChangeViewMode)
|
|
||||||
{
|
|
||||||
case Models.ChangeViewMode.List:
|
|
||||||
foreach (var item in stagedList.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Models.ChangeViewMode.Grid:
|
|
||||||
foreach (var item in stagedGrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
foreach (var item in stagedTree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.UnstageChanges(selected);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnstageAll(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
if (vm == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vm.UnstageChanges(vm.Staged);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedListKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.StageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedTreeViewKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.StageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedListKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.UnstageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedTreeViewKeyDown(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && e.Key == Key.Space && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.UnstageChanges(selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedListContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForUnstagedChanges(selected);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUnstagedTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForUnstagedChanges(selected);
|
|
||||||
tree.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedListContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var datagrid = sender as DataGrid;
|
|
||||||
if (datagrid.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in datagrid.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is Models.Change change)
|
|
||||||
selected.Add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForStagedChanges(selected);
|
|
||||||
datagrid.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStagedTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
var tree = sender as TreeView;
|
|
||||||
if (tree.SelectedItems.Count > 0 && DataContext is ViewModels.WorkingCopy vm)
|
|
||||||
{
|
|
||||||
List<Models.Change> selected = new List<Models.Change>();
|
|
||||||
foreach (var item in tree.SelectedItems)
|
|
||||||
{
|
|
||||||
if (item is ViewModels.FileTreeNode node)
|
|
||||||
CollectChangesFromNode(selected, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
var menu = vm.CreateContextMenuForStagedChanges(selected);
|
|
||||||
tree.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartAmend(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var repoPage = this.FindAncestorOfType<Repository>();
|
|
||||||
if (repoPage != null)
|
|
||||||
{
|
|
||||||
var repo = (repoPage.DataContext as ViewModels.Repository).FullPath;
|
|
||||||
var commits = new Commands.QueryCommits(repo, "-n 1", false).Result();
|
|
||||||
if (commits.Count == 0)
|
|
||||||
{
|
|
||||||
App.RaiseException(repo, "No commits to amend!!!");
|
|
||||||
|
|
||||||
var chkBox = sender as CheckBox;
|
|
||||||
chkBox.IsChecked = false;
|
|
||||||
e.Handled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
vm.CommitMessage = commits[0].FullMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Commit(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
vm.DoCommit(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CommitWithPush(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var vm = DataContext as ViewModels.WorkingCopy;
|
|
||||||
vm.DoCommit(true);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CollectChangesFromNode(List<Models.Change> outs, ViewModels.FileTreeNode node)
|
|
||||||
{
|
|
||||||
if (node.IsFolder)
|
|
||||||
{
|
|
||||||
foreach (var child in node.Children)
|
|
||||||
CollectChangesFromNode(outs, child);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var change = node.Backend as Models.Change;
|
|
||||||
if (change != null && !outs.Contains(change))
|
|
||||||
outs.Add(change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
|
private void OnOpenCommitMessagePicker(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is Button button && DataContext is ViewModels.WorkingCopy vm)
|
if (sender is Button button && DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
@ -331,5 +21,61 @@ namespace SourceGit.Views
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnUnstagedContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateContextMenuForUnstagedChanges();
|
||||||
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStagedContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateContextMenuForStagedChanges();
|
||||||
|
(sender as Control)?.OpenContextMenu(menu);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnstagedChangeDoubleTapped(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
vm.StageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStagedChangeDoubleTapped(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm)
|
||||||
|
{
|
||||||
|
vm.UnstageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnstagedKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm && e.Key == Key.Space)
|
||||||
|
{
|
||||||
|
vm.StageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStagedKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.WorkingCopy vm && e.Key == Key.Space)
|
||||||
|
{
|
||||||
|
vm.UnstageSelected();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue