mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-23 20:47:25 -08:00
feature<DiffViewer>: add combined diff view mode; optimize line number using virtualizing ItemsControl instead of TextBox
This commit is contained in:
parent
788013817d
commit
b97d5e608d
5 changed files with 457 additions and 254 deletions
|
@ -110,6 +110,10 @@ namespace SourceGit.Git {
|
|||
/// Use list instead of tree in change view.
|
||||
/// </summary>
|
||||
public bool UIUseListInChanges { get; set; }
|
||||
/// <summary>
|
||||
/// Use one side diff instead of two sides.
|
||||
/// </summary>
|
||||
public bool UIUseOneSideDiff { get; set; }
|
||||
#endregion
|
||||
|
||||
#region SETTING_REPOS
|
||||
|
|
|
@ -22,12 +22,14 @@
|
|||
|
||||
<Geometry x:Key="Icon.MoveUp">M868 545.5L536.1 163c-12.7-14.7-35.5-14.7-48.3 0L156 545.5c-4.5 5.2-0.8 13.2 6 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z</Geometry>
|
||||
<Geometry x:Key="Icon.MoveDown">M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861c12.7 14.7 35.5 14.7 48.3 0L868 478.5c4.5-5.2 0.8-13.2-6-13.2z</Geometry>
|
||||
|
||||
<Geometry x:Key="Icon.Down">M509.44 546.304l270.848-270.912 90.56 90.56-347.52 349.056-0.832-0.768-13.056 13.056-362.624-361.28 91.136-91.264z</Geometry>
|
||||
<Geometry x:Key="Icon.DoubleDown">M256 224l1e-8 115.2L512 544l255.99999999-204.8 1e-8-115.2-256 204.80000001L256 224zM512 684.8l-256-204.8L256 595.2 512 800 768 595.2l0-115.2L512 684.8z</Geometry>
|
||||
<Geometry x:Key="Icon.Up">M169.5 831l342.8-341.9L855.1 831l105.3-105.3-448.1-448.1L64.2 725.7 169.5 831z</Geometry>
|
||||
<Geometry x:Key="Icon.DoubleUp">M768 800V684.8L512 480 256 684.8V800l256-204.8L768 800zM512 339.2L768 544V428.8L512 224 256 428.8V544l256-204.8z</Geometry>
|
||||
|
||||
<Geometry x:Key="Icon.SplitHorizontal">M73.152 1024H1024V658.282667h-73.152v292.565333H73.173333V658.282667H0V1024h73.152zM0 0v365.717333h73.152V73.152H950.826667V0H0z m950.848 365.717333V0H1024v365.717333h-73.152zM0 548.565333v-73.130666h1024v73.130666H0z</Geometry>
|
||||
<Geometry x:Key="Icon.SplitVertical">M0 73.152V1024h365.717333v-73.152H73.152V73.173333h292.565333V0H0v73.152zM1024 0H658.282667v73.152h292.565333V950.826667H1024V0zM658.282667 950.848H1024V1024H658.282667v-73.152zM475.434667 0h73.130666v1024h-73.130666V0z</Geometry>
|
||||
|
||||
<Geometry x:Key="Icon.Preference">M64.2 180.3h418.2v120.6H64.2zM64.2 461.7h358.5v120.6H64.2zM64.2 723.1h418.2v120.6H64.2zM601.9 180.3h358.5v120.6H601.9zM482.4 119.9h179.2v241.3H482.4zM303.2 401.4h179.2v241.3H303.2zM482.4 662.8h179.2v241.3H482.4zM540.3 461.7h420.1v120.6H540.3zM601.9 723.1h358.5v120.6H601.9z</Geometry>
|
||||
<Geometry x:Key="Icon.Setting">M887 576.8v-129.4L796.6 418c-4.6-14-10.2-27.4-16.8-40.4l43.2-84.8-91.6-91.6-84.8 43.2c-13-6.6-26.6-12.2-40.4-16.8l-29.4-90.4h-129.4L418 227.6c-13.8 4.6-27.4 10.2-40.4 16.8l-84.8-43.2-91.6 91.6 43.2 84.8c-6.6 13-12.2 26.6-16.8 40.4l-90.4 29.4v129.4l90.4 29.4c4.6 13.8 10.2 27.4 16.8 40.4l-43.2 84.8 91.6 91.6 84.8-43.2c13 6.6 26.6 12.2 40.4 16.8l29.4 90.4h129.4l29.4-90.4c14-4.6 27.4-10.2 40.4-16.8l84.8 43.2 91.6-91.6-43.2-84.8c6.6-13 12.2-26.6 16.8-40.4l90.4-29.4zM512 662c-82.8 0-150-67.2-150-150s67.2-150 150-150 150 67.2 150 150-67.2 150-150 150z</Geometry>
|
||||
<Geometry x:Key="Icon.Info">M 38,19C 48.4934,19 57,27.5066 57,38C 57,48.4934 48.4934,57 38,57C 27.5066,57 19,48.4934 19,38C 19,27.5066 27.5066,19 38,19 Z M 33.25,33.25L 33.25,36.4167L 36.4166,36.4167L 36.4166,47.5L 33.25,47.5L 33.25,50.6667L 44.3333,50.6667L 44.3333,47.5L 41.1666,47.5L 41.1666,36.4167L 41.1666,33.25L 33.25,33.25 Z M 38.7917,25.3333C 37.48,25.3333 36.4167,26.3967 36.4167,27.7083C 36.4167,29.02 37.48,30.0833 38.7917,30.0833C 40.1033,30.0833 41.1667,29.02 41.1667,27.7083C 41.1667,26.3967 40.1033,25.3333 38.7917,25.3333 Z</Geometry>
|
||||
|
|
|
@ -108,4 +108,34 @@
|
|||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Style.ToggleButton.SplitDirection" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Grid Background="Transparent">
|
||||
<Path
|
||||
x:Name="Icon"
|
||||
Height="12"
|
||||
Width="12"
|
||||
Style="{DynamicResource Style.Icon}"
|
||||
Fill="Transparent"
|
||||
Stroke="{DynamicResource Brush.FG}"
|
||||
StrokeThickness=".4"
|
||||
Data="{DynamicResource Icon.SplitHorizontal}"/>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="Icon" Property="Data" Value="{DynamicResource Icon.SplitVertical}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
|
@ -3,6 +3,7 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:sourcegit="clr-namespace:SourceGit"
|
||||
mc:Ignorable="d"
|
||||
FontFamily="Consolas">
|
||||
<Border BorderThickness="1" BorderBrush="{StaticResource Brush.Border2}">
|
||||
|
@ -32,7 +33,7 @@
|
|||
<Path x:Name="loading" Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.Loading}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" x:Name="diffNavigation" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<StackPanel Grid.Column="2" x:Name="textChangeOptions" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Width="26" Click="Go2Next" ToolTip="Next Difference" Background="Transparent">
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.MoveDown}"/>
|
||||
</Button>
|
||||
|
@ -40,43 +41,68 @@
|
|||
<Button Click="Go2Prev" ToolTip="Previous Difference" Background="Transparent">
|
||||
<Path Width="10" Style="{StaticResource Style.Icon}" Data="{StaticResource Icon.MoveUp}"/>
|
||||
</Button>
|
||||
|
||||
<ToggleButton
|
||||
Margin="8,0,0,0"
|
||||
Style="{StaticResource Style.ToggleButton.SplitDirection}"
|
||||
ToolTip="Toggle One-Side/Two-Sides"
|
||||
IsChecked="{Binding Source={x:Static sourcegit:App.Preference}, Path=UIUseOneSideDiff, Mode=TwoWay}"
|
||||
Checked="ChangeDiffMode" Unchecked="ChangeDiffMode"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Grid x:Name="textChange" Grid.Row="1" ClipToBounds="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" MinWidth="100"/>
|
||||
<ColumnDefinition Width="2"/>
|
||||
<ColumnDefinition Width="*" MinWidth="100"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition x:Name="twoSideLeft"/>
|
||||
<ColumnDefinition x:Name="twoSideSplittter" Width="1"/>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*" MinWidth="100"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
<ItemsControl
|
||||
Grid.Column="1"
|
||||
x:Name="leftLineNumber"
|
||||
Grid.Column="0"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
IsReadOnly="True"
|
||||
Padding="2,0"
|
||||
Margin="0"
|
||||
FontSize="13"
|
||||
HorizontalContentAlignment="Right"
|
||||
VerticalAlignment="Stretch"/>
|
||||
VirtualizingPanel.ScrollUnit="Item"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
<ItemsControl.Template>
|
||||
<ControlTemplate TargetType="{x:Type ItemsControl}">
|
||||
<ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled">
|
||||
<ItemsPresenter/>
|
||||
</ScrollViewer>
|
||||
</ControlTemplate>
|
||||
</ItemsControl.Template>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding .}" Padding="0" Margin="0" FontFamily="Consolas" FontSize="13" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<Rectangle Grid.Column="3" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<RichTextBox
|
||||
x:Name="leftText"
|
||||
Grid.Column="2"
|
||||
Grid.Column="4"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
|
@ -90,49 +116,53 @@
|
|||
RenderOptions.ClearTypeHint="Enabled"
|
||||
ScrollViewer.ScrollChanged="OnViewerScroll"
|
||||
PreviewMouseWheel="OnViewerMouseWheel"
|
||||
SizeChanged="LeftSizeChanged"
|
||||
SizeChanged="OnSizeChanged"
|
||||
SelectionChanged="OnViewerSelectionChanged"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<RichTextBox.Document>
|
||||
<FlowDocument PageWidth="0"/>
|
||||
</RichTextBox.Document>
|
||||
<RichTextBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="ApplicationCommands.Copy"/>
|
||||
</ContextMenu>
|
||||
</RichTextBox.ContextMenu>
|
||||
</RichTextBox>
|
||||
</Grid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource Brush.Border2}"/>
|
||||
<Rectangle Grid.Column="5" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<Grid Grid.Column="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
<ItemsControl
|
||||
Grid.Column="7"
|
||||
x:Name="rightLineNumber"
|
||||
Grid.Column="0"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Padding="2,0"
|
||||
Margin="0"
|
||||
FontSize="13"
|
||||
HorizontalContentAlignment="Right"
|
||||
VerticalAlignment="Stretch"/>
|
||||
VirtualizingPanel.ScrollUnit="Item"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<Rectangle Grid.Column="1" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
<ItemsControl.Template>
|
||||
<ControlTemplate TargetType="{x:Type ItemsControl}">
|
||||
<ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled">
|
||||
<ItemsPresenter/>
|
||||
</ScrollViewer>
|
||||
</ControlTemplate>
|
||||
</ItemsControl.Template>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding .}" Padding="0" Margin="0" FontFamily="Consolas" FontSize="13" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{StaticResource Brush.FG}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<Rectangle Grid.Column="9" Width="1" Fill="{StaticResource Brush.Border2}"/>
|
||||
|
||||
<RichTextBox
|
||||
x:Name="rightText"
|
||||
Grid.Column="2"
|
||||
Grid.Column="10"
|
||||
AcceptsReturn="True"
|
||||
AcceptsTab="True"
|
||||
IsReadOnly="True"
|
||||
|
@ -146,21 +176,15 @@
|
|||
RenderOptions.ClearTypeHint="Enabled"
|
||||
ScrollViewer.ScrollChanged="OnViewerScroll"
|
||||
PreviewMouseWheel="OnViewerMouseWheel"
|
||||
SizeChanged="RightSizeChanged"
|
||||
SizeChanged="OnSizeChanged"
|
||||
SelectionChanged="OnViewerSelectionChanged"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<RichTextBox.Document>
|
||||
<FlowDocument PageWidth="0"/>
|
||||
</RichTextBox.Document>
|
||||
<RichTextBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="ApplicationCommands.Copy"/>
|
||||
</ContextMenu>
|
||||
</RichTextBox.ContextMenu>
|
||||
</RichTextBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Border x:Name="sizeChange" Grid.Row="1" ClipToBounds="True" Background="{StaticResource Brush.BG3}" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -15,6 +16,7 @@ namespace SourceGit.UI {
|
|||
/// </summary>
|
||||
public partial class DiffViewer : UserControl {
|
||||
private double minWidth = 0;
|
||||
private Git.Diff.TextChange textChangeData = null;
|
||||
|
||||
/// <summary>
|
||||
/// Diff options.
|
||||
|
@ -49,6 +51,8 @@ namespace SourceGit.UI {
|
|||
public void Diff(Git.Repository repo, Option opts) {
|
||||
SetTitle(opts.Path, opts.OrgPath);
|
||||
|
||||
textChangeData = null;
|
||||
|
||||
loading.Visibility = Visibility.Visible;
|
||||
mask.Visibility = Visibility.Collapsed;
|
||||
textChange.Visibility = Visibility.Collapsed;
|
||||
|
@ -80,7 +84,8 @@ namespace SourceGit.UI {
|
|||
if (rs.IsBinary) {
|
||||
SetBinaryChange(Git.Diff.GetSizeChange(repo, opts.RevisionRange, opts.Path, opts.OrgPath));
|
||||
} else if (rs.Blocks.Count > 0) {
|
||||
SetTextChange(rs);
|
||||
textChangeData = rs;
|
||||
SetTextChange();
|
||||
} else {
|
||||
SetSame();
|
||||
}
|
||||
|
@ -107,28 +112,41 @@ namespace SourceGit.UI {
|
|||
/// Show diff content.
|
||||
/// </summary>
|
||||
/// <param name="rs"></param>
|
||||
private void SetTextChange(Git.Diff.TextChange rs) {
|
||||
private void SetTextChange() {
|
||||
if (textChangeData == null) return;
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
textChange.Visibility = Visibility.Visible;
|
||||
diffNavigation.Visibility = Visibility.Visible;
|
||||
textChangeOptions.Visibility = Visibility.Visible;
|
||||
|
||||
if (App.Preference.UIUseOneSideDiff) {
|
||||
twoSideLeft.Width = new GridLength(0);
|
||||
twoSideLeft.MinWidth = 0;
|
||||
twoSideSplittter.Width = new GridLength(0);
|
||||
} else {
|
||||
twoSideLeft.Width = new GridLength(1, GridUnitType.Star);
|
||||
twoSideLeft.MinWidth = 100;
|
||||
twoSideSplittter.Width = new GridLength(2);
|
||||
}
|
||||
|
||||
minWidth = Math.Max(leftText.ActualWidth, rightText.ActualWidth) - 16;
|
||||
|
||||
leftLineNumber.Text = "";
|
||||
rightLineNumber.Text = "";
|
||||
leftLineNumber.ItemsSource = null;
|
||||
rightLineNumber.ItemsSource = null;
|
||||
|
||||
leftText.Document.Blocks.Clear();
|
||||
rightText.Document.Blocks.Clear();
|
||||
|
||||
var leftLineNumberBuilder = new StringBuilder();
|
||||
var rightLineNumberBuilder = new StringBuilder();
|
||||
var lLineNumbers = new List<string>();
|
||||
var rLineNumbers = new List<string>();
|
||||
|
||||
foreach (var b in rs.Blocks) ShowBlock(b, leftLineNumberBuilder, rightLineNumberBuilder);
|
||||
foreach (var b in textChangeData.Blocks) ShowBlock(b, lLineNumbers, rLineNumbers);
|
||||
|
||||
leftText.Document.PageWidth = minWidth + 16;
|
||||
if (!App.Preference.UIUseOneSideDiff) leftText.Document.PageWidth = minWidth + 16;
|
||||
rightText.Document.PageWidth = minWidth + 16;
|
||||
leftLineNumber.Text = leftLineNumberBuilder.ToString();
|
||||
rightLineNumber.Text = rightLineNumberBuilder.ToString();
|
||||
leftLineNumber.ItemsSource = lLineNumbers;
|
||||
rightLineNumber.ItemsSource = rLineNumbers;
|
||||
leftText.ScrollToHome();
|
||||
});
|
||||
}
|
||||
|
@ -141,7 +159,7 @@ namespace SourceGit.UI {
|
|||
Dispatcher.Invoke(() => {
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
sizeChange.Visibility = Visibility.Visible;
|
||||
diffNavigation.Visibility = Visibility.Collapsed;
|
||||
textChangeOptions.Visibility = Visibility.Collapsed;
|
||||
txtSizeChangeTitle.Content = "BINARY DIFF";
|
||||
txtNewSize.Content = $"{bc.Size} Bytes";
|
||||
txtOldSize.Content = $"{bc.PreSize} Bytes";
|
||||
|
@ -159,7 +177,7 @@ namespace SourceGit.UI {
|
|||
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
sizeChange.Visibility = Visibility.Visible;
|
||||
diffNavigation.Visibility = Visibility.Collapsed;
|
||||
textChangeOptions.Visibility = Visibility.Collapsed;
|
||||
txtSizeChangeTitle.Content = "LFS OBJECT CHANGE";
|
||||
txtNewSize.Content = $"{newSize} Bytes";
|
||||
txtOldSize.Content = $"{oldSize} Bytes";
|
||||
|
@ -173,44 +191,34 @@ namespace SourceGit.UI {
|
|||
Dispatcher.Invoke(() => {
|
||||
loading.Visibility = Visibility.Collapsed;
|
||||
noChange.Visibility = Visibility.Visible;
|
||||
diffNavigation.Visibility = Visibility.Collapsed;
|
||||
textChangeOptions.Visibility = Visibility.Collapsed;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make paragraph.
|
||||
/// Make paragraph for two-sides diff
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
private void ShowBlock(Git.Diff.Block b, StringBuilder leftNumber, StringBuilder rightNumber) {
|
||||
/// <param name="leftNumber"></param>
|
||||
/// <param name="rightNumber"></param>
|
||||
private void ShowBlock(Git.Diff.Block b, List<string> leftNumber, List<string> rightNumber) {
|
||||
bool useOneSide = App.Preference.UIUseOneSideDiff;
|
||||
if (useOneSide && b.Mode == Git.Diff.LineMode.Empty) return;
|
||||
|
||||
var content = b.Builder.ToString();
|
||||
|
||||
// Make paragraph element
|
||||
Paragraph p = new Paragraph(new Run(content));
|
||||
p.Margin = new Thickness(0);
|
||||
p.Padding = new Thickness();
|
||||
p.Padding = new Thickness(0);
|
||||
p.LineHeight = 1;
|
||||
p.Background = Brushes.Transparent;
|
||||
p.Foreground = FindResource("Brush.FG") as SolidColorBrush;
|
||||
p.FontStyle = FontStyles.Normal;
|
||||
p.Background = GetBlockBackground(b);
|
||||
p.Foreground = b.Mode == Git.Diff.LineMode.Indicator ? Brushes.Gray : FindResource("Brush.FG") as SolidColorBrush;
|
||||
p.FontStyle = b.Mode == Git.Diff.LineMode.Indicator ? FontStyles.Italic : FontStyles.Normal;
|
||||
p.DataContext = b;
|
||||
p.ContextMenuOpening += OnParagraphContextMenuOpening;
|
||||
|
||||
switch (b.Mode) {
|
||||
case Git.Diff.LineMode.Normal:
|
||||
break;
|
||||
case Git.Diff.LineMode.Indicator:
|
||||
p.Foreground = Brushes.Gray;
|
||||
p.FontStyle = FontStyles.Italic;
|
||||
break;
|
||||
case Git.Diff.LineMode.Empty:
|
||||
p.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
|
||||
break;
|
||||
case Git.Diff.LineMode.Added:
|
||||
p.Background = new SolidColorBrush(Color.FromArgb(60, 0, 255, 0));
|
||||
break;
|
||||
case Git.Diff.LineMode.Deleted:
|
||||
p.Background = new SolidColorBrush(Color.FromArgb(60, 255, 0, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate with
|
||||
var formatter = new FormattedText(
|
||||
content,
|
||||
CultureInfo.CurrentUICulture,
|
||||
|
@ -220,24 +228,50 @@ namespace SourceGit.UI {
|
|||
Brushes.Black,
|
||||
new NumberSubstitution(),
|
||||
TextFormattingMode.Ideal);
|
||||
|
||||
if (minWidth < formatter.Width) minWidth = formatter.Width;
|
||||
|
||||
// Line numbers
|
||||
switch (b.Side) {
|
||||
case Git.Diff.Side.Left:
|
||||
leftText.Document.Blocks.Add(p);
|
||||
for (int i = 0; i < b.Count; i++) {
|
||||
if (b.CanShowNumber) leftNumber.AppendLine($"{i + b.LeftStart}");
|
||||
else leftNumber.AppendLine();
|
||||
if (b.CanShowNumber) leftNumber.Add($"{i + b.LeftStart}");
|
||||
else leftNumber.Add("");
|
||||
|
||||
if (useOneSide) rightNumber.Add("");
|
||||
}
|
||||
break;
|
||||
case Git.Diff.Side.Right:
|
||||
rightText.Document.Blocks.Add(p);
|
||||
for (int i = 0; i < b.Count; i++) {
|
||||
if (b.CanShowNumber) rightNumber.AppendLine($"{i + b.RightStart}");
|
||||
else rightNumber.AppendLine();
|
||||
if (b.CanShowNumber) rightNumber.Add($"{i + b.RightStart}");
|
||||
else rightNumber.Add("");
|
||||
|
||||
if (useOneSide) leftNumber.Add("");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (int i = 0; i < b.Count; i++) {
|
||||
if (b.CanShowNumber) {
|
||||
leftNumber.Add($"{i + b.LeftStart}");
|
||||
rightNumber.Add($"{i + b.RightStart}");
|
||||
} else {
|
||||
leftNumber.Add("");
|
||||
rightNumber.Add("");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Add this paragraph to document.
|
||||
if (App.Preference.UIUseOneSideDiff) {
|
||||
rightText.Document.Blocks.Add(p);
|
||||
} else {
|
||||
switch (b.Side) {
|
||||
case Git.Diff.Side.Left:
|
||||
leftText.Document.Blocks.Add(p);
|
||||
break;
|
||||
case Git.Diff.Side.Right:
|
||||
rightText.Document.Blocks.Add(p);
|
||||
break;
|
||||
default:
|
||||
leftText.Document.Blocks.Add(p);
|
||||
|
||||
|
@ -249,23 +283,140 @@ namespace SourceGit.UI {
|
|||
cp.Foreground = p.Foreground;
|
||||
cp.FontStyle = p.FontStyle;
|
||||
cp.DataContext = b;
|
||||
rightText.Document.Blocks.Add(cp);
|
||||
cp.ContextMenuOpening += OnParagraphContextMenuOpening;
|
||||
|
||||
for (int i = 0; i < b.Count; i++) {
|
||||
if (b.Mode != Git.Diff.LineMode.Indicator) {
|
||||
leftNumber.AppendLine($"{i + b.LeftStart}");
|
||||
rightNumber.AppendLine($"{i + b.RightStart}");
|
||||
} else {
|
||||
leftNumber.AppendLine();
|
||||
rightNumber.AppendLine();
|
||||
}
|
||||
}
|
||||
rightText.Document.Blocks.Add(cp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get background color of block.
|
||||
/// </summary>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
private Brush GetBlockBackground(Git.Diff.Block b) {
|
||||
Border border = new Border();
|
||||
border.BorderThickness = new Thickness(0);
|
||||
border.BorderBrush = Brushes.LightBlue;
|
||||
border.Height = b.Count * 16 - 1;
|
||||
border.Width = minWidth - 1;
|
||||
|
||||
switch (b.Mode) {
|
||||
case Git.Diff.LineMode.Empty:
|
||||
border.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
|
||||
break;
|
||||
case Git.Diff.LineMode.Added:
|
||||
border.Background = new SolidColorBrush(Color.FromArgb(60, 0, 255, 0));
|
||||
break;
|
||||
case Git.Diff.LineMode.Deleted:
|
||||
border.Background = new SolidColorBrush(Color.FromArgb(60, 255, 0, 0));
|
||||
break;
|
||||
default:
|
||||
border.Background = Brushes.Transparent;
|
||||
break;
|
||||
}
|
||||
|
||||
VisualBrush highlight = new VisualBrush();
|
||||
highlight.TileMode = TileMode.None;
|
||||
highlight.Stretch = Stretch.Fill;
|
||||
highlight.Visual = border;
|
||||
return highlight;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
/// <summary>
|
||||
/// Context menu for text-change paragraph
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnParagraphContextMenuOpening(object sender, ContextMenuEventArgs ev) {
|
||||
var paragraph = sender as Paragraph;
|
||||
|
||||
var doc = (paragraph.Parent as FlowDocument);
|
||||
if (doc != null) {
|
||||
var textBox = doc.Parent as RichTextBox;
|
||||
if (textBox != null && !textBox.Selection.IsEmpty) {
|
||||
var copyItem = new MenuItem();
|
||||
copyItem.Header = "Copy";
|
||||
copyItem.Click += (o, e) => {
|
||||
Clipboard.SetText(textBox.Selection.Text);
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
var copyMenu = new ContextMenu();
|
||||
copyMenu.Items.Add(copyItem);
|
||||
copyMenu.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var block = paragraph.DataContext as Git.Diff.Block;
|
||||
if (block.Mode == Git.Diff.LineMode.Empty || block.Mode == Git.Diff.LineMode.Indicator) {
|
||||
ev.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var highlight = paragraph.Background as VisualBrush;
|
||||
if (highlight != null) {
|
||||
(highlight.Visual as Border).BorderThickness = new Thickness(.5);
|
||||
}
|
||||
|
||||
paragraph.ContextMenu = new ContextMenu();
|
||||
paragraph.ContextMenu.Closed += (o, e) => {
|
||||
if (paragraph.ContextMenu == (o as ContextMenu)) {
|
||||
if (highlight != null) {
|
||||
(highlight.Visual as Border).BorderThickness = new Thickness(0);
|
||||
}
|
||||
paragraph.ContextMenu = null;
|
||||
}
|
||||
};
|
||||
|
||||
var copy = new MenuItem();
|
||||
copy.Header = "Copy";
|
||||
copy.Click += (o, e) => {
|
||||
Clipboard.SetText(block.Builder.ToString());
|
||||
e.Handled = true;
|
||||
};
|
||||
paragraph.ContextMenu.Items.Add(copy);
|
||||
|
||||
paragraph.ContextMenu.IsOpen = true;
|
||||
ev.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fix document size.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
var text = sender as RichTextBox;
|
||||
if (text.Document.PageWidth < text.ActualWidth) {
|
||||
text.Document.PageWidth = text.ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll using mouse wheel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnViewerMouseWheel(object sender, MouseWheelEventArgs e) {
|
||||
var text = sender as RichTextBox;
|
||||
if (text == null) return;
|
||||
|
||||
if (e.Delta > 0) {
|
||||
text.LineUp();
|
||||
} else {
|
||||
text.LineDown();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync scroll both sides.
|
||||
/// </summary>
|
||||
|
@ -294,46 +445,6 @@ namespace SourceGit.UI {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll using mouse wheel.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void OnViewerMouseWheel(object sender, MouseWheelEventArgs e) {
|
||||
var text = sender as RichTextBox;
|
||||
if (text == null) return;
|
||||
|
||||
if (e.Delta > 0) {
|
||||
text.LineUp();
|
||||
} else {
|
||||
text.LineDown();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fix document size for left side.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LeftSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
if (leftText.Document.PageWidth < leftText.ActualWidth) {
|
||||
leftText.Document.PageWidth = leftText.ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fix document size for right side.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RightSizeChanged(object sender, SizeChangedEventArgs e) {
|
||||
if (rightText.Document.PageWidth < rightText.ActualWidth) {
|
||||
rightText.Document.PageWidth = rightText.ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Auto scroll when selection changed.
|
||||
/// </summary>
|
||||
|
@ -366,9 +477,20 @@ namespace SourceGit.UI {
|
|||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Go2Next(object sender, RoutedEventArgs e) {
|
||||
Paragraph next = null;
|
||||
double minTop = 0;
|
||||
|
||||
if (App.Preference.UIUseOneSideDiff) {
|
||||
foreach (var p in rightText.Document.Blocks) {
|
||||
var rect = p.ContentStart.GetCharacterRect(LogicalDirection.Forward);
|
||||
var block = p.DataContext as Git.Diff.Block;
|
||||
if (rect.Top > 17 && (block.IsLeftDelete || block.IsRightAdded)) {
|
||||
minTop = rect.Top;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Paragraph next = null;
|
||||
|
||||
foreach (var p in leftText.Document.Blocks) {
|
||||
var rect = p.ContentStart.GetCharacterRect(LogicalDirection.Forward);
|
||||
var block = p.DataContext as Git.Diff.Block;
|
||||
|
@ -391,8 +513,9 @@ namespace SourceGit.UI {
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next != null) {
|
||||
if (minTop > 0) {
|
||||
rightText.ScrollToVerticalOffset(rightText.VerticalOffset + minTop - 16);
|
||||
}
|
||||
}
|
||||
|
@ -403,8 +526,22 @@ namespace SourceGit.UI {
|
|||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Go2Prev(object sender, RoutedEventArgs e) {
|
||||
double maxTop = double.MaxValue;
|
||||
|
||||
if (App.Preference.UIUseOneSideDiff) {
|
||||
var p = rightText.Document.Blocks.LastBlock as Paragraph;
|
||||
do {
|
||||
var rect = p.ContentStart.GetCharacterRect(LogicalDirection.Forward);
|
||||
var block = p.DataContext as Git.Diff.Block;
|
||||
if (rect.Top < 15 && (block.IsLeftDelete || block.IsRightAdded)) {
|
||||
maxTop = rect.Top;
|
||||
break;
|
||||
}
|
||||
|
||||
p = p.PreviousBlock as Paragraph;
|
||||
} while (p != null);
|
||||
} else {
|
||||
Paragraph next = null;
|
||||
double maxTop = 0;
|
||||
|
||||
var p = leftText.Document.Blocks.LastBlock as Paragraph;
|
||||
do {
|
||||
|
@ -424,21 +561,27 @@ namespace SourceGit.UI {
|
|||
var rect = p.ContentStart.GetCharacterRect(LogicalDirection.Forward);
|
||||
var block = p.DataContext as Git.Diff.Block;
|
||||
if (rect.Top < 15 && block.IsRightAdded) {
|
||||
if (next == null || maxTop < rect.Top) {
|
||||
next = p;
|
||||
maxTop = rect.Top;
|
||||
}
|
||||
|
||||
if (next == null || maxTop < rect.Top) maxTop = rect.Top;
|
||||
break;
|
||||
}
|
||||
|
||||
p = p.PreviousBlock as Paragraph;
|
||||
} while (p != null);
|
||||
}
|
||||
|
||||
if (next != null) {
|
||||
if (maxTop != double.MaxValue) {
|
||||
rightText.ScrollToVerticalOffset(rightText.VerticalOffset + maxTop - 16);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chang diff mode.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ChangeDiffMode(object sender, RoutedEventArgs e) {
|
||||
SetTextChange();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue