Merge branch 'release/v8.10'

This commit is contained in:
leo 2024-04-29 11:01:46 +08:00
commit d60a33af1a
45 changed files with 487 additions and 287 deletions

View file

@ -28,12 +28,20 @@ Opensource Git GUI client.
> **Linux** only tested on **Ubuntu 22.04** on **X11**.
## How to use
## How to Use
**To use this tool, you need to install Git(>=2.23.0) first.**
You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [Github Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits.
This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs.
| OS | PATH |
| --- | --- |
| Windows | `C:\Users\USER_NAME\AppData\Roaming\SourceGit` |
| Linux | `/home/USER_NAME/.config/SourceGit` |
| macOS | `/Users/USER_NAME/.config/SourceGit` |
For **Windows** users:
* **MSYS Git is NOT supported**. Please use official [Git for Windows](https://git-scm.com/download/win) instead.
@ -64,7 +72,8 @@ This app supports open repository in external tools listed in the table below.
| JetBrains Fleet | YES | YES | YES | FLEET_PATH |
| Sublime Text | YES | YES | YES | SUBLIME_TEXT_PATH |
You can set the given environment variable for special tool if it can NOT be found by this app automatically.
> * You can set the given environment variable for special tool if it can NOT be found by this app automatically.
> * Installing `JetBrains Toolbox` will help this app to find other JetBrains tools installed on your device.
## Screenshots

View file

@ -1 +1 @@
8.9
8.10

View file

@ -4,6 +4,7 @@ namespace SourceGit
{
[JsonSourceGenerationOptions(WriteIndented = true, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = true)]
[JsonSerializable(typeof(Models.Version))]
[JsonSerializable(typeof(Models.JetBrainsState))]
[JsonSerializable(typeof(ViewModels.Preference))]
internal partial class JsonCodeGen : JsonSerializerContext { }
}

View file

@ -164,7 +164,7 @@ namespace SourceGit
try
{
// Fetch lastest release information.
var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) };
var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) };
var data = await client.GetStringAsync("https://sourcegit-scm.github.io/data/version.json");
// Parse json into Models.Version.

View file

@ -0,0 +1,20 @@
namespace SourceGit.Commands
{
public class IsConflictResolved : Command
{
public IsConflictResolved(string repo, Models.Change change)
{
var opt = new Models.DiffOption(change, true);
WorkingDirectory = repo;
Context = repo;
Args = $"diff -a --ignore-cr-at-eol --check {opt}";
}
public bool Result()
{
var rs = ReadToEnd();
return rs.IsSuccess;
}
}
}

View file

@ -388,11 +388,11 @@ namespace SourceGit.Models
}
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
private static partial Regex indicatorRegex();
private static partial Regex REG_INDICATOR();
private bool ProcessIndicatorForPatch(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool tailed)
{
var match = indicatorRegex().Match(indicator.Content);
var match = REG_INDICATOR().Match(indicator.Content);
var oldStart = int.Parse(match.Groups[1].Value);
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
var oldCount = 0;
@ -461,7 +461,7 @@ namespace SourceGit.Models
private bool ProcessIndicatorForPatchSingleSide(StringBuilder builder, TextDiffLine indicator, int idx, int start, int end, int ignoreRemoves, int ignoreAdds, bool revert, bool isOldSide, bool tailed)
{
var match = indicatorRegex().Match(indicator.Content);
var match = REG_INDICATOR().Match(indicator.Content);
var oldStart = int.Parse(match.Groups[1].Value);
var newStart = int.Parse(match.Groups[2].Value) + ignoreRemoves - ignoreAdds;
var oldCount = 0;

View file

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
@ -19,8 +21,24 @@ namespace SourceGit.Models
{
get
{
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute));
return new Bitmap(icon);
if (_isFirstTimeGetIcon)
{
_isFirstTimeGetIcon = false;
if (!string.IsNullOrWhiteSpace(Icon))
{
try
{
var icon = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{Icon}.png", UriKind.RelativeOrAbsolute));
_iconImage = new Bitmap(icon);
}
catch
{
}
}
}
return _iconImage;
}
}
@ -34,6 +52,41 @@ namespace SourceGit.Models
UseShellExecute = false,
});
}
private bool _isFirstTimeGetIcon = true;
private Bitmap _iconImage = null;
}
public class JetBrainsState
{
[JsonPropertyName("version")]
public int Version { get; set; } = 0;
[JsonPropertyName("appVersion")]
public string AppVersion { get; set; } = string.Empty;
[JsonPropertyName("tools")]
public List<JetBrainsTool> Tools { get; set; } = new List<JetBrainsTool>();
}
public class JetBrainsTool
{
[JsonPropertyName("channelId")]
public string ChannelId { get; set; }
[JsonPropertyName("toolId")]
public string ToolId { get; set; }
[JsonPropertyName("productCode")]
public string ProductCode { get; set; }
[JsonPropertyName("tag")]
public string Tag { get; set; }
[JsonPropertyName("displayName")]
public string DisplayName { get; set; }
[JsonPropertyName("displayVersion")]
public string DisplayVersion { get; set; }
[JsonPropertyName("buildNumber")]
public string BuildNumber { get; set; }
[JsonPropertyName("installLocation")]
public string InstallLocation { get; set; }
[JsonPropertyName("launchCommand")]
public string LaunchCommand { get; set; }
}
public class ExternalToolsFinder
@ -44,26 +97,6 @@ namespace SourceGit.Models
private set;
} = new List<ExternalTool>();
public void VSCode(Func<string> platform_finder)
{
TryAdd("Visual Studio Code", "vscode", "\"{0}\"", "VSCODE_PATH", platform_finder);
}
public void VSCodeInsiders(Func<string> platform_finder)
{
TryAdd("Visual Studio Code - Insiders", "vscode_insiders", "\"{0}\"", "VSCODE_INSIDERS_PATH", platform_finder);
}
public void Fleet(Func<string> platform_finder)
{
TryAdd("JetBrains Fleet", "fleet", "\"{0}\"", "FLEET_PATH", platform_finder);
}
public void SublimeText(Func<string> platform_finder)
{
TryAdd("Sublime Text", "sublime_text", "\"{0}\"", "SUBLIME_TEXT_PATH", platform_finder);
}
public void TryAdd(string name, string icon, string args, string env, Func<string> finder)
{
var path = Environment.GetEnvironmentVariable(env);
@ -79,8 +112,52 @@ namespace SourceGit.Models
Name = name,
Icon = icon,
OpenCmdArgs = args,
Executable = path,
Executable = path
});
}
public void VSCode(Func<string> platform_finder)
{
TryAdd("Visual Studio Code", "vscode", "\"{0}\"", "VSCODE_PATH", platform_finder);
}
public void VSCodeInsiders(Func<string> platform_finder)
{
TryAdd("Visual Studio Code - Insiders", "vscode_insiders", "\"{0}\"", "VSCODE_INSIDERS_PATH", platform_finder);
}
public void Fleet(Func<string> platform_finder)
{
TryAdd("Fleet", "fleet", "\"{0}\"", "FLEET_PATH", platform_finder);
}
public void SublimeText(Func<string> platform_finder)
{
TryAdd("Sublime Text", "sublime_text", "\"{0}\"", "SUBLIME_TEXT_PATH", platform_finder);
}
public void FindJetBrainsFromToolbox(Func<string> platform_finder)
{
var exclude = new List<string> { "fleet", "dotmemory", "dottrace", "resharper-u", "androidstudio" };
var supported_icons = new List<string> { "CL", "DB", "DL", "DS", "GO", "IC", "IU", "JB", "PC", "PS", "PY", "QA", "QD", "RD", "RM", "RR", "WRS", "WS" };
var state = Path.Combine(platform_finder(), "state.json");
if (File.Exists(state))
{
var stateData = JsonSerializer.Deserialize(File.ReadAllText(state), JsonCodeGen.Default.JetBrainsState);
foreach (var tool in stateData.Tools)
{
if (exclude.Contains(tool.ToolId.ToLowerInvariant()))
continue;
Founded.Add(new ExternalTool
{
Name = $"{tool.DisplayName} {tool.DisplayVersion}",
Icon = supported_icons.Contains(tool.ProductCode) ? $"JetBrains/{tool.ProductCode}" : $"JetBrains/JB",
OpenCmdArgs = "\"{0}\"",
Executable = Path.Combine(tool.InstallLocation, tool.LaunchCommand),
});
}
}
}
}
}

View file

@ -57,7 +57,8 @@ namespace SourceGit.Native
var finder = new Models.ExternalToolsFinder();
finder.VSCode(() => FindExecutable("code"));
finder.VSCodeInsiders(() => FindExecutable("code-insiders"));
finder.Fleet(FindJetBrainFleet);
finder.Fleet(FindJetBrainsFleet);
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox");
finder.SublimeText(() => FindExecutable("subl"));
return finder.Founded;
}
@ -174,7 +175,7 @@ namespace SourceGit.Native
return null;
}
private string FindJetBrainFleet()
private string FindJetBrainsFleet()
{
var path = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/JetBrains/Toolbox/apps/fleet/bin/Fleet";
return File.Exists(path) ? path : FindExecutable("fleet");

View file

@ -33,6 +33,7 @@ namespace SourceGit.Native
finder.VSCode(() => "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code");
finder.VSCodeInsiders(() => "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code");
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Applications/Fleet.app/Contents/MacOS/Fleet");
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Library/Application Support/JetBrains/Toolbox");
finder.SublimeText(() => "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl");
return finder.Founded;
}

View file

@ -111,6 +111,7 @@ namespace SourceGit.Native
finder.VSCode(FindVSCode);
finder.VSCodeInsiders(FindVSCodeInsiders);
finder.Fleet(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\Programs\\Fleet\\Fleet.exe");
finder.FindJetBrainsFromToolbox(() => $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\\JetBrains\\Toolbox");
finder.SublimeText(FindSublimeText);
return finder.Founded;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -211,6 +211,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">Go to next page</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">Create new page</x:String>
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">REPOSITORY</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">Force to reload this repository</x:String>
<x:String x:Key="Text.Hotkeys.Repo.StageOrUnstageSelected" xml:space="preserve">Stage/Unstage selected changes</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ToggleSearch" xml:space="preserve">Toggle commit search</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ViewChanges" xml:space="preserve">Switch to 'Changes'</x:String>
@ -426,11 +427,13 @@
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">Sort</x:String>
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">Changes</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">Amend</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">You can stage this file now.</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">COMMIT</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">COMMIT &amp; PUSH</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageTip" xml:space="preserve">Enter commit message</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">CONFLICTS DETECTED</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">FILE CONFLICTS ARE RESOLVED</x:String>
<x:String x:Key="Text.WorkingCopy.HasCommitHistories" xml:space="preserve">RECENT INPUT MESSAGES</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">INCLUDE UNTRACKED FILES</x:String>
<x:String x:Key="Text.WorkingCopy.MessageHistories" xml:space="preserve">MESSAGE HISTORIES</x:String>

View file

@ -211,6 +211,7 @@
<x:String x:Key="Text.Hotkeys.Global.GotoNextTab" xml:space="preserve">切换到下一个页面</x:String>
<x:String x:Key="Text.Hotkeys.Global.NewTab" xml:space="preserve">新建页面</x:String>
<x:String x:Key="Text.Hotkeys.Repo" xml:space="preserve">仓库页面快捷键</x:String>
<x:String x:Key="Text.Hotkeys.Repo.Refresh" xml:space="preserve">重新加载仓库状态</x:String>
<x:String x:Key="Text.Hotkeys.Repo.StageOrUnstageSelected" xml:space="preserve">将选中的变更暂存或从暂存列表中移除</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ToggleSearch" xml:space="preserve">打开/关闭历史搜索</x:String>
<x:String x:Key="Text.Hotkeys.Repo.ViewChanges" xml:space="preserve">显示本地更改</x:String>
@ -426,11 +427,13 @@
<x:String x:Key="Text.Welcome.Sort" xml:space="preserve">排序</x:String>
<x:String x:Key="Text.WorkingCopy" xml:space="preserve">本地更改</x:String>
<x:String x:Key="Text.WorkingCopy.Amend" xml:space="preserve">修补(--amend)</x:String>
<x:String x:Key="Text.WorkingCopy.CanStageTip" xml:space="preserve">现在您已可将其加入暂存区中</x:String>
<x:String x:Key="Text.WorkingCopy.Commit" xml:space="preserve">提交</x:String>
<x:String x:Key="Text.WorkingCopy.CommitAndPush" xml:space="preserve">提交并推送</x:String>
<x:String x:Key="Text.WorkingCopy.CommitMessageTip" xml:space="preserve">填写提交信息</x:String>
<x:String x:Key="Text.WorkingCopy.CommitTip" xml:space="preserve">CTRL + Enter</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts" xml:space="preserve">检测到冲突</x:String>
<x:String x:Key="Text.WorkingCopy.Conflicts.Resolved" xml:space="preserve">文件冲突已解决</x:String>
<x:String x:Key="Text.WorkingCopy.HasCommitHistories" xml:space="preserve">最近输入的提交信息</x:String>
<x:String x:Key="Text.WorkingCopy.IncludeUntracked" xml:space="preserve">显示未跟踪文件</x:String>
<x:String x:Key="Text.WorkingCopy.MessageHistories" xml:space="preserve">历史提交信息</x:String>

View file

@ -88,9 +88,9 @@
<Setter.Value>
<MenuFlyout Placement="Bottom">
<MenuItem Header="{DynamicResource Text.Copy}"
Command="{Binding $parent[SelectableTextBlock].Copy}"
IsEnabled="{Binding $parent[SelectableTextBlock].CanCopy}"
InputGesture="{x:Static TextBox.CopyGesture}">
Command="{Binding $parent[SelectableTextBlock].Copy}"
IsEnabled="{Binding $parent[SelectableTextBlock].CanCopy}"
InputGesture="{x:Static TextBox.CopyGesture}">
<MenuItem.Icon>
<Path Width="11" Height="11" Data="{StaticResource Icons.Copy}" VerticalAlignment="Center"/>
</MenuItem.Icon>
@ -254,7 +254,7 @@
<ControlTemplate>
<Grid>
<Border x:Name="PART_Border"
Margin="2"
Margin="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
@ -487,21 +487,21 @@
<ControlTemplate>
<Grid>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
<Border Background="{DynamicResource Brush.FG2}"
Width="4"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
IsHitTestVisible="False"
IsVisible="{TemplateBinding IsSelected}"/>
Width="4"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
IsHitTestVisible="False"
IsVisible="{TemplateBinding IsSelected}"/>
</Grid>
</ControlTemplate>
</Setter>
@ -529,16 +529,16 @@
</Border>
<Border BorderThickness="0"
Margin="8,4"
MaxWidth="{TemplateBinding MaxWidth}"
MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
Margin="8,4"
MaxWidth="{TemplateBinding MaxWidth}"
MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
KeyboardNavigation.TabNavigation="Continue"
Grid.IsSharedSizeScope="True" />
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
KeyboardNavigation.TabNavigation="Continue"
Grid.IsSharedSizeScope="True" />
</ScrollViewer>
</Border>
</Grid>
@ -557,20 +557,20 @@
</Border>
<Border Name="LayoutRoot"
Margin="4"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0"
Padding="4,2"
CornerRadius="{TemplateBinding CornerRadius}">
Margin="4"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0"
Padding="4,2"
CornerRadius="{TemplateBinding CornerRadius}">
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
Theme="{StaticResource FluentMenuScrollViewer}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
Theme="{StaticResource FluentMenuScrollViewer}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
KeyboardNavigation.TabNavigation="Continue"
Grid.IsSharedSizeScope="True" />
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
KeyboardNavigation.TabNavigation="Continue"
Grid.IsSharedSizeScope="True" />
</ScrollViewer>
</Border>
</Grid>
@ -581,18 +581,18 @@
<Style Selector="MenuItem">
<Style.Resources>
<ControlTheme x:Key="{x:Type MenuItem}" TargetType="MenuItem">
<Setter Property="Height" Value="26"/>
<Setter Property="Height" Value="28"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}" />
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Border Name="PART_LayoutRoot"
Padding="0,0,4,0"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3">
Padding="0,0,4,0"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIcon" />
@ -603,44 +603,44 @@
<Border Grid.Column="0" Width="28">
<ContentControl x:Name="PART_IconPresenter"
Content="{TemplateBinding Icon}"
IsVisible="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
Content="{TemplateBinding Icon}"
IsVisible="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ContentPresenter Grid.Column="1"
Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
RecognizesAccessKey="True"/>
Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
RecognizesAccessKey="True"/>
<TextBlock x:Name="PART_InputGestureText"
Grid.Column="2"
Classes="CaptionTextBlockStyle"
Margin="{DynamicResource MenuInputGestureTextMargin}"
Text="{TemplateBinding InputGesture, Converter={StaticResource KeyGestureConverter}}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
FontSize="11"/>
Grid.Column="2"
Classes="CaptionTextBlockStyle"
Margin="{DynamicResource MenuInputGestureTextMargin}"
Text="{TemplateBinding InputGesture, Converter={StaticResource KeyGestureConverter}}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
FontSize="11"/>
<Path Name="PART_ChevronPath"
Width="6"
Data="M 0 0 L 0 7 L 4 3.5 Z"
Fill="{DynamicResource MenuFlyoutSubItemChevron}"
Margin="8,0,0,0"
VerticalAlignment="Center"
Grid.Column="3" />
Width="6"
Data="M573 512 215 881c-20 20-20 51 0 61l61 61c20 20 51 20 61 0l461-461c10-10 10-20 10-31s0-20-10-31L338 20C317 0 287 0 276 20L215 82c-20 20-20 51 0 61L573 512z"
Fill="{DynamicResource Brush.FG2}"
Margin="8,0,0,0"
VerticalAlignment="Center"
Grid.Column="3" />
</Grid>
</Border>
<Popup Name="PART_Popup"
WindowManagerAddShadowHint="False"
Placement="RightEdgeAlignedTop"
IsLightDismissEnabled="False"
HorizontalOffset="-4"
VerticalOffset="-4"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
WindowManagerAddShadowHint="False"
Placement="RightEdgeAlignedTop"
IsLightDismissEnabled="False"
HorizontalOffset="-4"
VerticalOffset="-4"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}">
<Grid>
<Border Background="{DynamicResource Brush.Popup}" Margin="4" CornerRadius="{DynamicResource OverlayCornerRadius}">
<Border.Effect>
@ -649,16 +649,16 @@
</Border>
<Border BorderThickness="0"
Margin="8,4"
MaxWidth="{DynamicResource FlyoutThemeMaxWidth}"
MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}"
HorizontalAlignment="Stretch"
CornerRadius="{DynamicResource OverlayCornerRadius}">
Margin="8,4"
MaxWidth="{DynamicResource FlyoutThemeMaxWidth}"
MinHeight="{DynamicResource MenuFlyoutThemeMinHeight}"
HorizontalAlignment="Stretch"
CornerRadius="{DynamicResource OverlayCornerRadius}">
<ScrollViewer Theme="{StaticResource FluentMenuScrollViewer}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
Grid.IsSharedSizeScope="True" />
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{DynamicResource MenuFlyoutScrollerMargin}"
Grid.IsSharedSizeScope="True" />
</ScrollViewer>
</Border>
</Grid>
@ -724,7 +724,7 @@
<SolidColorBrush x:Key="ComboBoxDropDownBackground" Color="{DynamicResource Color.Contents}"/>
<SolidColorBrush x:Key="ComboBoxDropDownBorderBrush" Color="{DynamicResource Color.Accent1}"/>
</Style.Resources>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border1}"/>
@ -761,12 +761,12 @@
<Path x:Name="Icon" Height="12" Width="12" Data="{DynamicResource Icons.Check}" Fill="{DynamicResource Brush.Accent1}" IsVisible="False" Margin="0,2,0,0"/>
</Border>
<ContentPresenter x:Name="ContentPresenter"
<ContentPresenter x:Name="ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Grid.Column="1"
VerticalAlignment="Center"
Margin="8,0,0,0"
Grid.Column="1"
VerticalAlignment="Center"
Margin="8,0,0,0"
TextWrapping="Wrap"/>
</Grid>
</ControlTemplate>
@ -834,26 +834,26 @@
<ControlTemplate>
<Grid ColumnDefinitions="Auto,*">
<Path Grid.Column="0"
x:Name="PART_IndicatorIcon"
Margin="4,0,0,0"
Width="10"
Data="{StaticResource Icons.Down}"
Fill="{DynamicResource Brush.FG2}"
VerticalAlignment="Center"
RenderTransformOrigin="50%,50%"
RenderTransform="rotate(270deg)"/>
x:Name="PART_IndicatorIcon"
Margin="4,0,0,0"
Width="10"
Data="{StaticResource Icons.Down}"
Fill="{DynamicResource Brush.FG2}"
VerticalAlignment="Center"
RenderTransformOrigin="50%,50%"
RenderTransform="rotate(270deg)"/>
<ContentPresenter Grid.Column="1"
x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="0"
RecognizesAccessKey="True"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="0"
RecognizesAccessKey="True"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</Grid>
</ControlTemplate>
</Setter>
@ -880,13 +880,13 @@
<Setter Property="Template">
<ControlTemplate>
<Path Grid.Column="0"
x:Name="PART_IndicatorIcon"
Width="12"
Data="{StaticResource Icons.Filter}"
Fill="Transparent"
StrokeThickness="1"
Stroke="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
x:Name="PART_IndicatorIcon"
Width="12"
Data="{StaticResource Icons.Filter}"
Fill="Transparent"
StrokeThickness="1"
Stroke="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</ControlTemplate>
</Setter>
</Style>
@ -906,15 +906,15 @@
<Setter Property="Template">
<ControlTemplate>
<Border Background="Transparent"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Path x:Name="ChevronPath"
Data="M 4 0 L 8 4 L 4 8 Z"
Fill="{DynamicResource TreeViewItemForeground}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
Data="M 4 0 L 8 4 L 4 8 Z"
Fill="{DynamicResource TreeViewItemForeground}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter>
@ -933,11 +933,11 @@
<ControlTemplate>
<Border Background="Transparent">
<Path Grid.Column="0"
x:Name="PART_IndicatorIcon"
x:Name="PART_IndicatorIcon"
Width="14" Height="14"
Stretch="Uniform"
Data="{StaticResource Icons.LayoutVertical}"
Fill="{DynamicResource Brush.FG1}"
Data="{StaticResource Icons.LayoutVertical}"
Fill="{DynamicResource Brush.FG1}"
Opacity=".8"/>
</Border>
</ControlTemplate>
@ -949,7 +949,7 @@
<Style Selector="ToggleButton.layout_direction:pointerover /template/ Path#PART_IndicatorIcon">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="ToggleButton.line_path">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
@ -966,7 +966,7 @@
<Style Selector="ToggleButton.line_path:checked Path">
<Setter Property="Fill" Value="{DynamicResource Brush.Accent1}"/>
</Style>
<Style Selector="ToggleButton.toggle_untracked">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
@ -977,12 +977,12 @@
<ControlTemplate>
<Border Background="Transparent">
<Path Grid.Column="0"
x:Name="PART_IndicatorIcon"
x:Name="PART_IndicatorIcon"
Margin="{TemplateBinding Padding}"
Stretch="Fill"
Width="14" Height="10"
Data="{StaticResource Icons.EyeClose}"
Fill="{DynamicResource Brush.FG1}"
Data="{StaticResource Icons.EyeClose}"
Fill="{DynamicResource Brush.FG1}"
Opacity=".8"/>
</Border>
</ControlTemplate>
@ -1040,36 +1040,36 @@
<ControlTemplate>
<StackPanel>
<Border Name="PART_LayoutRoot"
Classes="TreeViewItemLayoutRoot"
Focusable="True"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
MinHeight="{TemplateBinding MinHeight}"
TemplatedControl.IsTemplateFocusTarget="True">
Classes="TreeViewItemLayoutRoot"
Focusable="True"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
MinHeight="{TemplateBinding MinHeight}"
TemplatedControl.IsTemplateFocusTarget="True">
<Grid Name="PART_Header" ColumnDefinitions="16,*" Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
<Panel Name="PART_ExpandCollapseChevronContainer">
<ToggleButton Name="PART_ExpandCollapseChevron"
Classes="tree_expander"
Focusable="False"
HorizontalAlignment="Center"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
Classes="tree_expander"
Focusable="False"
HorizontalAlignment="Center"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
</Panel>
<ContentPresenter Name="PART_HeaderPresenter"
Grid.Column="1"
Focusable="False"
Background="Transparent"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="{TemplateBinding Padding}" />
Grid.Column="1"
Focusable="False"
Background="Transparent"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Margin="{TemplateBinding Padding}" />
</Grid>
</Border>
<ItemsPresenter Name="PART_ItemsPresenter"
IsVisible="{TemplateBinding IsExpanded}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
IsVisible="{TemplateBinding IsExpanded}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</StackPanel>
</ControlTemplate>
</Setter>

View file

@ -23,6 +23,7 @@
<ItemGroup>
<AvaloniaResource Include="App.ico" />
<AvaloniaResource Include="Resources/ExternalToolIcons/*" />
<AvaloniaResource Include="Resources/ExternalToolIcons/JetBrains/*" />
<AvaloniaResource Include="Resources/Fonts/*" />
<AvaloniaResource Include="Resources/ShellIcons/*" />
</ItemGroup>

View file

@ -22,9 +22,9 @@ namespace SourceGit.ViewModels
Cmd = cmd;
}
public void Abort()
public bool Abort()
{
new Commands.Command()
return new Commands.Command()
{
WorkingDirectory = Repository,
Context = Repository,

View file

@ -34,7 +34,7 @@ namespace SourceGit.ViewModels
{
foreach (var b in rename._repo.Branches)
{
if (b != rename.Target && b.Name == name)
if (b.IsLocal && b != rename.Target && b.Name == name)
{
return new ValidationResult("A branch with same name already exists!!!");
}

View file

@ -233,17 +233,7 @@ namespace SourceGit.ViewModels
_inProgressContext = null;
_hasUnsolvedConflicts = false;
Task.Run(() =>
{
RefreshBranches();
RefreshTags();
RefreshCommits();
});
Task.Run(RefreshSubmodules);
Task.Run(RefreshWorkingCopyChanges);
Task.Run(RefreshStashes);
Task.Run(RefreshGitFlow);
RefreshAll();
}
public void Close()
@ -277,6 +267,21 @@ namespace SourceGit.ViewModels
_searchedCommits.Clear();
}
public void RefreshAll()
{
Task.Run(() =>
{
RefreshBranches();
RefreshTags();
RefreshCommits();
});
Task.Run(RefreshSubmodules);
Task.Run(RefreshWorkingCopyChanges);
Task.Run(RefreshStashes);
Task.Run(RefreshGitFlow);
}
public void OpenInFileManager()
{
Native.OS.OpenInFileManager(_fullpath);
@ -506,7 +511,11 @@ namespace SourceGit.ViewModels
if (_inProgressContext != null)
{
SetWatcherEnabled(false);
await Task.Run(_inProgressContext.Abort);
var succ = await Task.Run(_inProgressContext.Abort);
if (succ && _workingCopy != null)
{
_workingCopy.CommitMessage = string.Empty;
}
SetWatcherEnabled(true);
}
else

View file

@ -1,6 +1,7 @@
using System;
using Avalonia.Collections;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
@ -79,7 +80,7 @@ namespace SourceGit.ViewModels
SearchFilter = string.Empty;
}
public void AddFolder()
public void AddRootNode()
{
if (PopupHost.CanCreatePopup())
PopupHost.ShowPopup(new CreateGroup(null));
@ -90,6 +91,72 @@ namespace SourceGit.ViewModels
Preference.MoveNode(from, to);
}
public ContextMenu CreateContextMenu(RepositoryNode node)
{
var menu = new ContextMenu();
var hasRepo = Preference.FindRepository(node.Id) != null;
var edit = new MenuItem();
edit.Header = App.Text("Welcome.Edit");
edit.Icon = App.CreateMenuIcon("Icons.Edit");
edit.IsEnabled = !node.IsRepository || hasRepo;
edit.Click += (_, e) =>
{
node.Edit();
e.Handled = true;
};
menu.Items.Add(edit);
if (node.IsRepository)
{
var explore = new MenuItem();
explore.Header = App.Text("Repository.Explore");
explore.Icon = App.CreateMenuIcon("Icons.Folder.Open");
explore.IsEnabled = hasRepo;
explore.Click += (_, e) =>
{
node.OpenInFileManager();
e.Handled = true;
};
menu.Items.Add(explore);
var terminal = new MenuItem();
terminal.Header = App.Text("Repository.Terminal");
terminal.Icon = App.CreateMenuIcon("Icons.Terminal");
terminal.IsEnabled = hasRepo;
terminal.Click += (_, e) =>
{
node.OpenTerminal();
e.Handled = true;
};
menu.Items.Add(terminal);
}
else
{
var addSubFolder = new MenuItem();
addSubFolder.Header = App.Text("Welcome.AddSubFolder");
addSubFolder.Icon = App.CreateMenuIcon("Icons.Folder.Add");
addSubFolder.Click += (_, e) =>
{
node.AddSubFolder();
e.Handled = true;
};
menu.Items.Add(addSubFolder);
}
var delete = new MenuItem();
delete.Header = App.Text("Welcome.Delete");
delete.Icon = App.CreateMenuIcon("Icons.Clear");
delete.Click += (_, e) =>
{
node.Delete();
e.Handled = true;
};
menu.Items.Add(delete);
return menu;
}
private void Referesh()
{
if (string.IsNullOrWhiteSpace(_searchFilter))

View file

@ -12,9 +12,27 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class ConflictContext
public class ConflictContext : ObservableObject
{
public Models.Change Change { get; set; }
public bool IsResolved
{
get => _isResolved;
set => SetProperty(ref _isResolved, value);
}
public ConflictContext(string repo, Models.Change change)
{
Task.Run(() =>
{
var result = new Commands.IsConflictResolved(repo, change).Result();
Dispatcher.UIThread.Post(() =>
{
IsResolved = result;
});
});
}
private bool _isResolved = false;
}
public class WorkingCopy : ObservableObject
@ -274,6 +292,14 @@ namespace SourceGit.ViewModels
SelectedStagedTreeNode = null;
SetDetail(null, false);
}
// Try to load merge message from MERGE_MSG
if (string.IsNullOrEmpty(_commitMessage))
{
var mergeMsgFile = Path.Combine(_repo.GitDir, "MERGE_MSG");
if (File.Exists(mergeMsgFile))
CommitMessage = File.ReadAllText(mergeMsgFile);
}
});
return hasConflict;
@ -288,9 +314,9 @@ namespace SourceGit.ViewModels
{
DetailContext = null;
}
else if (change.IsConflit)
else if (change.IsConflit && isUnstaged)
{
DetailContext = new ConflictContext() { Change = change };
DetailContext = new ConflictContext(_repo.FullPath, change);
}
else
{
@ -424,26 +450,23 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(true);
}
public async void UseExternalMergeTool()
public async void UseExternalMergeTool(Models.Change change)
{
if (_detailContext is ConflictContext ctx)
var type = Preference.Instance.ExternalMergeToolType;
var exec = Preference.Instance.ExternalMergeToolPath;
var tool = Models.ExternalMerger.Supported.Find(x => x.Type == type);
if (tool == null)
{
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, ctx.Change.Path));
_repo.SetWatcherEnabled(true);
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)
@ -546,7 +569,7 @@ namespace SourceGit.ViewModels
openMerger.Header = App.Text("FileCM.OpenWithExternalMerger");
openMerger.Click += (_, e) =>
{
UseExternalMergeTool();
UseExternalMergeTool(change);
e.Handled = true;
};

View file

@ -99,7 +99,7 @@
<!-- Messages -->
<TextBlock Grid.Row="3" Grid.Column="0" Classes="info_label" Text="{DynamicResource Text.CommitDetail.Info.Message}" VerticalAlignment="Top" Margin="0,4,0,0" />
<ScrollViewer Grid.Row="3" Grid.Column="1" Margin="12,5,0,0" MaxHeight="64" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ScrollViewer Grid.Row="3" Grid.Column="1" Margin="12,5,8,0" MaxHeight="64" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<SelectableTextBlock Text="{Binding FullMessage}" FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" TextWrapping="Wrap"/>
</ScrollViewer>
</Grid>

View file

@ -87,7 +87,7 @@
FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Increase}}"
Margin="0,8"/>
<Grid RowDefinitions="20,20,20,20,20" ColumnDefinitions="80,*">
<Grid RowDefinitions="20,20,20,20,20,20" ColumnDefinitions="80,*">
<TextBlock Grid.Row="0" Grid.Column="0" Classes="monospace bold" Text="Ctrl+F"/>
<TextBlock Grid.Row="0" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.ToggleSearch}" />
@ -102,6 +102,9 @@
<TextBlock Grid.Row="4" Grid.Column="0" Classes="monospace bold" Text="SPACE"/>
<TextBlock Grid.Row="4" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.StageOrUnstageSelected}" />
<TextBlock Grid.Row="5" Grid.Column="0" Classes="monospace bold" Text="F5"/>
<TextBlock Grid.Row="5" Grid.Column="1" Margin="16,0,0,0" Text="{DynamicResource Text.Hotkeys.Repo.Refresh}" />
</Grid>
<TextBlock Text="{DynamicResource Text.Hotkeys.TextEditor}"

View file

@ -146,6 +146,15 @@ namespace SourceGit.Views
e.Handled = true;
return;
}
else if (e.Key == Key.F5)
{
if (vm.ActivePage.Data is ViewModels.Repository repo)
{
repo.RefreshAll();
e.Handled = true;
return;
}
}
base.OnKeyDown(e);
}

View file

@ -7,6 +7,7 @@ using System.Text;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.VisualTree;
@ -657,6 +658,7 @@ namespace SourceGit.Views
UpdateTextMate();
TextArea.PointerWheelChanged += OnTextAreaPointerWheelChanged;
TextArea.TextView.ContextRequested += OnTextViewContextRequested;
}
@ -676,21 +678,20 @@ namespace SourceGit.Views
_textMate = null;
}
TextArea.PointerWheelChanged -= OnTextAreaPointerWheelChanged;
TextArea.TextView.ContextRequested -= OnTextViewContextRequested;
GC.Collect();
}
private void OnTextAreaPointerWheelChanged(object sender, PointerWheelEventArgs e)
{
if (!TextArea.IsFocused) Focus();
}
private void OnTextViewScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (_syncScrollingByOthers)
{
_syncScrollingByOthers = false;
}
else
{
SetCurrentValue(SyncScrollOffsetProperty, _scrollViewer.Offset);
}
if (TextArea.IsFocused) SetCurrentValue(SyncScrollOffsetProperty, _scrollViewer.Offset);
}
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
@ -754,24 +755,8 @@ namespace SourceGit.Views
}
else if (change.Property == SyncScrollOffsetProperty)
{
if (_scrollViewer == null)
return;
var curOffset = _scrollViewer.Offset;
if (!curOffset.Equals(SyncScrollOffset))
{
_syncScrollingByOthers = true;
if (curOffset.X != SyncScrollOffset.X)
{
var offset = new Vector(Math.Min(_scrollViewer.ScrollBarMaximum.X, SyncScrollOffset.X), SyncScrollOffset.Y);
_scrollViewer.Offset = offset;
}
else
{
_scrollViewer.Offset = SyncScrollOffset;
}
}
if (!TextArea.IsFocused && _scrollViewer != null)
_scrollViewer.Offset = SyncScrollOffset;
}
else if (change.Property == UseSyntaxHighlightingProperty)
{
@ -813,7 +798,6 @@ namespace SourceGit.Views
private TextMate.Installation _textMate;
private readonly LineStyleTransformer _lineStyleTransformer = null;
private ScrollViewer _scrollViewer = null;
private bool _syncScrollingByOthers = false;
}
public partial class TextDiffView : UserControl

View file

@ -57,7 +57,7 @@
LostFocus="OnTreeViewLostFocus">
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Text.Welcome.AddRootFolder}" Command="{Binding AddFolder}">
<MenuItem Header="{DynamicResource Text.Welcome.AddRootFolder}" Command="{Binding AddRootNode}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Add}"/>
</MenuItem.Icon>
@ -70,37 +70,6 @@
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="IsVisible" Value="{Binding IsVisible}"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{DynamicResource Text.Welcome.Edit}" Command="{Binding Edit}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Edit}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Welcome.AddSubFolder}" Command="{Binding AddSubFolder}" IsVisible="{Binding !IsRepository}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Add}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Repository.Explore}" Command="{Binding OpenInFileManager}" IsVisible="{Binding IsRepository}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Folder.Open}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Repository.Terminal}" Command="{Binding OpenTerminal}" IsVisible="{Binding IsRepository}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Terminal}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource Text.Welcome.Delete}" Command="{Binding Delete}">
<MenuItem.Icon>
<Path Width="12" Height="12" Data="{DynamicResource Icons.Clear}"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.Styles>
@ -109,11 +78,12 @@
<Grid Height="30"
ColumnDefinitions="Auto,Auto,*"
Background="Transparent"
Loaded="SetupTreeNodeDragAndDrop"
ContextRequested="OnTreeNodeContextRequested"
PointerPressed="OnPointerPressedTreeNode"
PointerMoved="OnPointerMovedOverTreeNode"
PointerReleased="OnPointerReleasedOnTreeNode"
DoubleTapped="OnDoubleTappedTreeNode"
Loaded="SetupTreeNodeDragAndDrop"
ClipToBounds="True">
<Path Grid.Column="0" Width="12" Height="12" Margin="0,0,8,0"
Fill="{Binding Bookmark, Converter={x:Static c:BookmarkConverters.ToBrush}}"

View file

@ -37,6 +37,16 @@ namespace SourceGit.Views
}
}
private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e)
{
if (sender is Grid grid && DataContext is ViewModels.Welcome vm)
{
var menu = vm.CreateContextMenu(grid.DataContext as ViewModels.RepositoryNode);
menu?.Open(grid);
e.Handled = true;
}
}
private void OnPointerPressedTreeNode(object sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(sender as Visual).Properties.IsLeftButtonPressed)

View file

@ -319,11 +319,19 @@
<ContentControl.DataTemplates>
<DataTemplate DataType="vm:ConflictContext">
<Border Background="{DynamicResource Brush.Window}" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Width="64" Height="64" Data="{StaticResource Icons.Conflict}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
<TextBlock Text="{DynamicResource Text.WorkingCopy.ResolveTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Vertical" IsVisible="{Binding !IsResolved}">
<Path Width="64" Height="64" Data="{StaticResource Icons.Conflict}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
<TextBlock Text="{DynamicResource Text.WorkingCopy.ResolveTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Vertical" IsVisible="{Binding IsResolved}">
<Path Width="64" Height="64" Data="{StaticResource Icons.Check}" Fill="Green"/>
<TextBlock Margin="0,16,0,8" FontSize="20" FontWeight="Bold" Text="{DynamicResource Text.WorkingCopy.Conflicts.Resolved}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
<TextBlock Text="{DynamicResource Text.WorkingCopy.CanStageTip}" Foreground="{DynamicResource Brush.FG2}" HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>