feature: supports to show submodules as tree or list (#1307)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-05-16 11:31:53 +08:00
parent d299469613
commit ed1351b1f7
No known key found for this signature in database
10 changed files with 302 additions and 152 deletions

View file

@ -617,6 +617,7 @@
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Message</x:String> <x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">Message</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String> <x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Current Branch</x:String> <x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">Current Branch</x:String>
<x:String x:Key="Text.Repository.ShowSubmodulesAsTree" xml:space="preserve">Show Submodules as Tree</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Show Tags as Tree</x:String> <x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">Show Tags as Tree</x:String>
<x:String x:Key="Text.Repository.Skip" xml:space="preserve">SKIP</x:String> <x:String x:Key="Text.Repository.Skip" xml:space="preserve">SKIP</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Statistics</x:String> <x:String x:Key="Text.Repository.Statistics" xml:space="preserve">Statistics</x:String>

View file

@ -621,6 +621,7 @@
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交信息</x:String> <x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交信息</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交指纹</x:String> <x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交指纹</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">仅在当前分支中查找</x:String> <x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">仅在当前分支中查找</x:String>
<x:String x:Key="Text.Repository.ShowSubmodulesAsTree" xml:space="preserve">以树型结构展示</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">以树型结构展示</x:String> <x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">以树型结构展示</x:String>
<x:String x:Key="Text.Repository.Skip" xml:space="preserve">跳过此提交</x:String> <x:String x:Key="Text.Repository.Skip" xml:space="preserve">跳过此提交</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交统计</x:String> <x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交统计</x:String>

View file

@ -621,6 +621,7 @@
<x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交訊息</x:String> <x:String x:Key="Text.Repository.Search.ByMessage" xml:space="preserve">提交訊息</x:String>
<x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交編號</x:String> <x:String x:Key="Text.Repository.Search.BySHA" xml:space="preserve">提交編號</x:String>
<x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">僅搜尋目前分支</x:String> <x:String x:Key="Text.Repository.Search.InCurrentBranch" xml:space="preserve">僅搜尋目前分支</x:String>
<x:String x:Key="Text.Repository.ShowSubmodulesAsTree" xml:space="preserve">以樹型結構展示</x:String>
<x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">以樹型結構展示</x:String> <x:String x:Key="Text.Repository.ShowTagsAsTree" xml:space="preserve">以樹型結構展示</x:String>
<x:String x:Key="Text.Repository.Skip" xml:space="preserve">跳過此提交</x:String> <x:String x:Key="Text.Repository.Skip" xml:space="preserve">跳過此提交</x:String>
<x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交統計</x:String> <x:String x:Key="Text.Repository.Statistics" xml:space="preserve">提交統計</x:String>

View file

@ -1235,7 +1235,7 @@
<Setter Property="Opacity" Value="1"/> <Setter Property="Opacity" Value="1"/>
</Style> </Style>
<Style Selector="ToggleButton.tag_display_mode"> <Style Selector="ToggleButton.show_as_tree">
<Setter Property="Margin" Value="0" /> <Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>
<Setter Property="Template"> <Setter Property="Template">

View file

@ -188,6 +188,12 @@ namespace SourceGit.ViewModels
set => SetProperty(ref _showTagsInGraph, value); set => SetProperty(ref _showTagsInGraph, value);
} }
public bool ShowSubmodulesAsTree
{
get;
set;
} = false;
public bool UseTwoColumnsLayoutInHistories public bool UseTwoColumnsLayoutInHistories
{ {
get => _useTwoColumnsLayoutInHistories; get => _useTwoColumnsLayoutInHistories;

View file

@ -210,7 +210,21 @@ namespace SourceGit.ViewModels
private set => SetProperty(ref _submodules, value); private set => SetProperty(ref _submodules, value);
} }
public SubmoduleCollection VisibleSubmodules public bool ShowSubmodulesAsTree
{
get => Preferences.Instance.ShowSubmodulesAsTree;
set
{
if (value != Preferences.Instance.ShowSubmodulesAsTree)
{
Preferences.Instance.ShowSubmodulesAsTree = value;
VisibleSubmodules = BuildVisibleSubmodules();
OnPropertyChanged();
}
}
}
public object VisibleSubmodules
{ {
get => _visibleSubmodules; get => _visibleSubmodules;
private set => SetProperty(ref _visibleSubmodules, value); private set => SetProperty(ref _visibleSubmodules, value);
@ -536,7 +550,7 @@ namespace SourceGit.ViewModels
_tags.Clear(); _tags.Clear();
_visibleTags.Clear(); _visibleTags.Clear();
_submodules.Clear(); _submodules.Clear();
_visibleSubmodules.Clear(); _visibleSubmodules = null;
_searchedCommits.Clear(); _searchedCommits.Clear();
_selectedSearchedCommit = null; _selectedSearchedCommit = null;
@ -2512,7 +2526,7 @@ namespace SourceGit.ViewModels
return visible; return visible;
} }
private SubmoduleCollection BuildVisibleSubmodules() private object BuildVisibleSubmodules()
{ {
var visible = new List<Models.Submodule>(); var visible = new List<Models.Submodule>();
if (string.IsNullOrEmpty(_filter)) if (string.IsNullOrEmpty(_filter))
@ -2528,7 +2542,10 @@ namespace SourceGit.ViewModels
} }
} }
return SubmoduleCollection.Build(visible, _visibleSubmodules); if (Preferences.Instance.ShowSubmodulesAsTree)
return SubmoduleCollectionAsTree.Build(visible, _visibleSubmodules as SubmoduleCollectionAsTree);
else
return new SubmoduleCollectionAsList() { Submodules = visible };
} }
private void RefreshHistoriesFilters(bool refresh) private void RefreshHistoriesFilters(bool refresh)
@ -2760,7 +2777,7 @@ namespace SourceGit.ViewModels
private List<Models.Tag> _tags = new List<Models.Tag>(); private List<Models.Tag> _tags = new List<Models.Tag>();
private List<Models.Tag> _visibleTags = new List<Models.Tag>(); private List<Models.Tag> _visibleTags = new List<Models.Tag>();
private List<Models.Submodule> _submodules = new List<Models.Submodule>(); private List<Models.Submodule> _submodules = new List<Models.Submodule>();
private SubmoduleCollection _visibleSubmodules = new SubmoduleCollection(); private object _visibleSubmodules = null;
private bool _isAutoFetching = false; private bool _isAutoFetching = false;
private Timer _autoFetchTimer = null; private Timer _autoFetchTimer = null;

View file

@ -120,7 +120,7 @@ namespace SourceGit.ViewModels
private bool _isExpanded = false; private bool _isExpanded = false;
} }
public class SubmoduleCollection public class SubmoduleCollectionAsTree
{ {
public List<SubmoduleTreeNode> Tree public List<SubmoduleTreeNode> Tree
{ {
@ -134,16 +134,19 @@ namespace SourceGit.ViewModels
set; set;
} = []; } = [];
public static SubmoduleCollection Build(List<Models.Submodule> submodules, SubmoduleCollection old) public static SubmoduleCollectionAsTree Build(List<Models.Submodule> submodules, SubmoduleCollectionAsTree old)
{ {
var oldExpanded = new HashSet<string>(); var oldExpanded = new HashSet<string>();
foreach (var row in old.Rows) if (old != null)
{ {
if (row.IsFolder && row.IsExpanded) foreach (var row in old.Rows)
oldExpanded.Add(row.FullPath); {
if (row.IsFolder && row.IsExpanded)
oldExpanded.Add(row.FullPath);
}
} }
var collection = new SubmoduleCollection(); var collection = new SubmoduleCollectionAsTree();
collection.Tree = SubmoduleTreeNode.Build(submodules, oldExpanded); collection.Tree = SubmoduleTreeNode.Build(submodules, oldExpanded);
var rows = new List<SubmoduleTreeNode>(); var rows = new List<SubmoduleTreeNode>();
@ -203,4 +206,13 @@ namespace SourceGit.ViewModels
} }
} }
} }
public class SubmoduleCollectionAsList
{
public List<Models.Submodule> Submodules
{
get;
set;
} = [];
}
} }

View file

@ -269,7 +269,7 @@
<Run Text="{Binding Tags, Converter={x:Static c:ListConverters.ToCount}}"/> <Run Text="{Binding Tags, Converter={x:Static c:ListConverters.ToCount}}"/>
</TextBlock> </TextBlock>
<ToggleButton Grid.Column="2" <ToggleButton Grid.Column="2"
Classes="tag_display_mode" Classes="show_as_tree"
Width="14" Width="14"
IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowTagsAsTree, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preferences.Instance}, Path=ShowTagsAsTree, Mode=TwoWay}"
ToolTip.Tip="{DynamicResource Text.Repository.ShowTagsAsTree}"/> ToolTip.Tip="{DynamicResource Text.Repository.ShowTagsAsTree}"/>
@ -305,13 +305,19 @@
<!-- Submodules --> <!-- Submodules -->
<ToggleButton Grid.Row="6" Classes="group_expander" IsChecked="{Binding IsSubmoduleGroupExpanded, Mode=TwoWay}"> <ToggleButton Grid.Row="6" Classes="group_expander" IsChecked="{Binding IsSubmoduleGroupExpanded, Mode=TwoWay}">
<Grid ColumnDefinitions="16,*,Auto,Auto"> <Grid ColumnDefinitions="16,*,Auto,Auto,Auto">
<Path Grid.Column="0" Width="10" Height="10" Margin="2,0,0,0" HorizontalAlignment="Left" Data="{StaticResource Icons.Submodules}" Fill="{DynamicResource Brush.FG2}"/> <Path Grid.Column="0" Width="10" Height="10" Margin="2,0,0,0" HorizontalAlignment="Left" Data="{StaticResource Icons.Submodules}" Fill="{DynamicResource Brush.FG2}"/>
<TextBlock Grid.Column="1" Classes="group_header_label" Margin="0"> <TextBlock Grid.Column="1" Classes="group_header_label" Margin="0">
<Run Text="{DynamicResource Text.Repository.Submodules}"/> <Run Text="{DynamicResource Text.Repository.Submodules}"/>
<Run Text="{Binding Submodules, Converter={x:Static c:ListConverters.ToCount}}"/> <Run Text="{Binding Submodules, Converter={x:Static c:ListConverters.ToCount}}"/>
</TextBlock> </TextBlock>
<Button Grid.Column="2" <ToggleButton Grid.Column="2"
Classes="show_as_tree"
Width="14"
IsChecked="{Binding ShowSubmodulesAsTree, Mode=TwoWay}"
IsVisible="{Binding Submodules, Converter={x:Static c:ListConverters.IsNotNullOrEmpty}}"
ToolTip.Tip="{DynamicResource Text.Repository.ShowSubmodulesAsTree}"/>
<Button Grid.Column="3"
Classes="icon_button" Classes="icon_button"
Width="14" Width="14"
Margin="8,0" Margin="8,0"
@ -320,7 +326,7 @@
ToolTip.Tip="{DynamicResource Text.Repository.Submodules.Update}"> ToolTip.Tip="{DynamicResource Text.Repository.Submodules.Update}">
<Path Width="12" Height="12" Data="{StaticResource Icons.Loading}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.Loading}"/>
</Button> </Button>
<Button Grid.Column="3" <Button Grid.Column="4"
Classes="icon_button" Classes="icon_button"
Width="14" Width="14"
Margin="0,0,8,0" Margin="0,0,8,0"
@ -334,7 +340,7 @@
x:Name="SubmoduleList" x:Name="SubmoduleList"
Height="0" Height="0"
Margin="8,0,4,0" Margin="8,0,4,0"
Submodules="{Binding VisibleSubmodules}" Content="{Binding VisibleSubmodules}"
RowsChanged="OnSubmodulesRowsChanged" RowsChanged="OnSubmodulesRowsChanged"
Focusable="False" Focusable="False"
IsVisible="{Binding IsSubmoduleGroupExpanded, Mode=OneWay}"/> IsVisible="{Binding IsSubmoduleGroupExpanded, Mode=OneWay}"/>

View file

@ -7,119 +7,217 @@
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
xmlns:c="using:SourceGit.Converters" xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.SubmodulesView" x:Class="SourceGit.Views.SubmodulesView">
x:Name="ThisControl">
<UserControl.DataTemplates> <UserControl.DataTemplates>
<DataTemplate DataType="m:Submodule"> <DataTemplate DataType="vm:SubmoduleCollectionAsTree">
<StackPanel Orientation="Vertical"> <ListBox Classes="repo_left_content_list" ItemsSource="{Binding Rows}" SelectionMode="Single">
<StackPanel Orientation="Horizontal"> <ListBox.Styles>
<Path Width="10" Height="10" Data="{StaticResource Icons.Submodule}"/> <Style Selector="ListBoxItem">
<TextBlock FontWeight="Bold" Margin="4,0,0,0" Text="{Binding Path}"/> <Setter Property="CornerRadius" Value="4"/>
</StackPanel> </Style>
</ListBox.Styles>
<Grid RowDefinitions="24,24" ColumnDefinitions="Auto,Auto" Margin="0,8,0,0"> <ListBox.DataTemplates>
<TextBlock Grid.Row="0" Grid.Column="0" <DataTemplate DataType="m:Submodule">
Classes="info_label" <StackPanel Orientation="Vertical">
HorizontalAlignment="Left" VerticalAlignment="Center" <StackPanel Orientation="Horizontal">
Text="{DynamicResource Text.CommitDetail.Info.SHA}"/> <Path Width="10" Height="10" Data="{StaticResource Icons.Submodule}"/>
<StackPanel Grid.Row="0" Grid.Column="1" <TextBlock FontWeight="Bold" Margin="4,0,0,0" Text="{Binding Path}"/>
Orientation="Horizontal" </StackPanel>
Margin="8,0,0,0">
<TextBlock Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
VerticalAlignment="Center"/>
<Path Margin="6,0,0,0" <Grid RowDefinitions="24,24" ColumnDefinitions="Auto,Auto" Margin="0,8,0,0">
HorizontalAlignment="Left" VerticalAlignment="Center" <TextBlock Grid.Row="0" Grid.Column="0"
Width="12" Height="12" Classes="info_label"
Data="{StaticResource Icons.Check}" HorizontalAlignment="Left" VerticalAlignment="Center"
Fill="Green" Text="{DynamicResource Text.CommitDetail.Info.SHA}"/>
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Normal}}"/> <StackPanel Grid.Row="0" Grid.Column="1"
<Border Height="16" Orientation="Horizontal"
Margin="6,0,0,0" Padding="4,0" Margin="8,0,0,0">
HorizontalAlignment="Left" VerticalAlignment="Center" <TextBlock Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
Background="DarkOrange" VerticalAlignment="Center"/>
CornerRadius="4"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.NotEqual}, ConverterParameter={x:Static m:SubmoduleStatus.Normal}}"> <Path Margin="6,0,0,0"
<Grid> HorizontalAlignment="Left" VerticalAlignment="Center"
<TextBlock VerticalAlignment="Center" Width="12" Height="12"
Text="{DynamicResource Text.Submodule.Status.NotInited}" Data="{StaticResource Icons.Check}"
Foreground="White" Fill="Green"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.NotInited}}"/> IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Normal}}"/>
<TextBlock VerticalAlignment="Center" <Border Height="16"
Text="{DynamicResource Text.Submodule.Status.Modified}" Margin="6,0,0,0" Padding="4,0"
Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Center"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Modified}}"/> Background="DarkOrange"
<TextBlock VerticalAlignment="Center" CornerRadius="4"
Text="{DynamicResource Text.Submodule.Status.RevisionChanged}" IsVisible="{Binding Status, Converter={x:Static ObjectConverters.NotEqual}, ConverterParameter={x:Static m:SubmoduleStatus.Normal}}">
Foreground="White" <Grid>
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.RevisionChanged}}"/> <TextBlock VerticalAlignment="Center"
<TextBlock VerticalAlignment="Center" Text="{DynamicResource Text.Submodule.Status.NotInited}"
Text="{DynamicResource Text.Submodule.Status.Unmerged}" Foreground="White"
Foreground="White" IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.NotInited}}"/>
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Unmerged}}"/> <TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.Modified}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Modified}}"/>
<TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.RevisionChanged}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.RevisionChanged}}"/>
<TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.Unmerged}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Unmerged}}"/>
</Grid>
</Border>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
Classes="info_label"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.URL}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Margin="8,0,0,0"
Text="{Binding URL}"
Foreground="{DynamicResource Brush.Link}"
VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.DataTemplates>
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:SubmoduleTreeNode">
<Border Height="24"
Background="Transparent"
DoubleTapped="OnItemDoubleTapped"
ContextRequested="OnItemContextRequested"
ToolTip.Tip="{Binding Module}"
ToolTip.Placement="Right">
<Grid ColumnDefinitions="16,Auto,*,Auto,Auto"
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
VerticalAlignment="Center">
<v:SubmoduleTreeNodeToggleButton Grid.Column="0"
Classes="tree_expander"
Focusable="False"
HorizontalAlignment="Center"
IsChecked="{Binding IsExpanded, Mode=OneWay}"
IsVisible="{Binding IsFolder}"/>
<v:SubmoduleTreeNodeIcon Grid.Column="1"
IsExpanded="{Binding IsExpanded, Mode=OneWay}"/>
<TextBlock Grid.Column="2"
Classes="primary"
Margin="8,0,0,0"
TextTrimming="CharacterEllipsis">
<Run Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}"/>
<Run Text="{Binding ChildCounter}" Foreground="{DynamicResource Brush.FG2}"/>
</TextBlock>
<Path Grid.Column="3"
Width="8" Height="8"
Margin="0,0,12,0"
Fill="Goldenrod"
Data="{StaticResource Icons.Modified}"
IsVisible="{Binding IsDirty}"/>
</Grid> </Grid>
</Border> </Border>
</StackPanel> </DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
<TextBlock Grid.Row="1" Grid.Column="0" <DataTemplate DataType="vm:SubmoduleCollectionAsList">
Classes="info_label" <ListBox Classes="repo_left_content_list" ItemsSource="{Binding Submodules}" SelectionMode="Single">
HorizontalAlignment="Left" VerticalAlignment="Center" <ListBox.Styles>
Text="{DynamicResource Text.Submodule.URL}"/> <Style Selector="ListBoxItem">
<TextBlock Grid.Row="1" Grid.Column="1" <Setter Property="CornerRadius" Value="4"/>
Margin="8,0,0,0" </Style>
Text="{Binding URL}" </ListBox.Styles>
Foreground="{DynamicResource Brush.Link}"
VerticalAlignment="Center"/> <ListBox.ItemTemplate>
</Grid> <DataTemplate DataType="m:Submodule">
</StackPanel> <Border Height="24"
Background="Transparent"
DoubleTapped="OnItemDoubleTapped"
ContextRequested="OnItemContextRequested"
ToolTip.Placement="Right">
<ToolTip.Tip>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Path Width="10" Height="10" Data="{StaticResource Icons.Submodule}"/>
<TextBlock FontWeight="Bold" Margin="4,0,0,0" Text="{Binding Path}"/>
</StackPanel>
<Grid RowDefinitions="24,24" ColumnDefinitions="Auto,Auto" Margin="0,8,0,0">
<TextBlock Grid.Row="0" Grid.Column="0"
Classes="info_label"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{DynamicResource Text.CommitDetail.Info.SHA}"/>
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Horizontal"
Margin="8,0,0,0">
<TextBlock Text="{Binding SHA, Converter={x:Static c:StringConverters.ToShortSHA}}"
VerticalAlignment="Center"/>
<Path Margin="6,0,0,0"
HorizontalAlignment="Left" VerticalAlignment="Center"
Width="12" Height="12"
Data="{StaticResource Icons.Check}"
Fill="Green"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Normal}}"/>
<Border Height="16"
Margin="6,0,0,0" Padding="4,0"
HorizontalAlignment="Left" VerticalAlignment="Center"
Background="DarkOrange"
CornerRadius="4"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.NotEqual}, ConverterParameter={x:Static m:SubmoduleStatus.Normal}}">
<Grid>
<TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.NotInited}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.NotInited}}"/>
<TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.Modified}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Modified}}"/>
<TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.RevisionChanged}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.RevisionChanged}}"/>
<TextBlock VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.Status.Unmerged}"
Foreground="White"
IsVisible="{Binding Status, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:SubmoduleStatus.Unmerged}}"/>
</Grid>
</Border>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
Classes="info_label"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{DynamicResource Text.Submodule.URL}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Margin="8,0,0,0"
Text="{Binding URL}"
Foreground="{DynamicResource Brush.Link}"
VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</ToolTip.Tip>
<Grid ColumnDefinitions="16,*,Auto" Margin="8,0,0,0" VerticalAlignment="Center">
<Path Grid.Column="0" Width="10" Height="10" Margin="8,0" Data="{StaticResource Icons.Submodule}"/>
<TextBlock Grid.Column="1" Text="{Binding Path}" ClipToBounds="True" Classes="primary" TextTrimming="CharacterEllipsis"/>
<Path Grid.Column="2"
Width="8" Height="8"
Margin="8,0,12,0"
Fill="Goldenrod"
Data="{StaticResource Icons.Modified}"
IsVisible="{Binding IsDirty}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate> </DataTemplate>
</UserControl.DataTemplates> </UserControl.DataTemplates>
<ListBox Classes="repo_left_content_list" ItemsSource="{Binding #ThisControl.Submodules.Rows}" SelectionMode="Single">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="CornerRadius" Value="4"/>
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:SubmoduleTreeNode">
<Border Height="24"
Background="Transparent"
DoubleTapped="OnDoubleTappedNode"
ContextRequested="OnRowContextRequested"
ToolTip.Tip="{Binding Module}"
ToolTip.Placement="Right">
<Grid ColumnDefinitions="16,Auto,*,Auto,Auto"
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
VerticalAlignment="Center">
<v:SubmoduleTreeNodeToggleButton Grid.Column="0"
Classes="tree_expander"
Focusable="False"
HorizontalAlignment="Center"
IsChecked="{Binding IsExpanded, Mode=OneWay}"
IsVisible="{Binding IsFolder}"/>
<v:SubmoduleTreeNodeIcon Grid.Column="1"
IsExpanded="{Binding IsExpanded, Mode=OneWay}"/>
<TextBlock Grid.Column="2"
Classes="primary"
Margin="8,0,0,0"
TextTrimming="CharacterEllipsis">
<Run Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}"/>
<Run Text="{Binding ChildCounter}" Foreground="{DynamicResource Brush.FG2}"/>
</TextBlock>
<Path Grid.Column="3"
Width="8" Height="8"
Margin="0,0,12,0"
Fill="Goldenrod"
Data="{StaticResource Icons.Modified}"
IsVisible="{Binding IsDirty}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl> </UserControl>

View file

@ -89,15 +89,6 @@ namespace SourceGit.Views
public partial class SubmodulesView : UserControl public partial class SubmodulesView : UserControl
{ {
public static readonly StyledProperty<ViewModels.SubmoduleCollection> SubmodulesProperty =
AvaloniaProperty.Register<SubmodulesView, ViewModels.SubmoduleCollection>(nameof(Submodules));
public ViewModels.SubmoduleCollection Submodules
{
get => GetValue(SubmodulesProperty);
set => SetValue(SubmodulesProperty, value);
}
public static readonly RoutedEvent<RoutedEventArgs> RowsChangedEvent = public static readonly RoutedEvent<RoutedEventArgs> RowsChangedEvent =
RoutedEvent.Register<TagsView, RoutedEventArgs>(nameof(RowsChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); RoutedEvent.Register<TagsView, RoutedEventArgs>(nameof(RowsChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
@ -120,11 +111,10 @@ namespace SourceGit.Views
public void ToggleNodeIsExpanded(ViewModels.SubmoduleTreeNode node) public void ToggleNodeIsExpanded(ViewModels.SubmoduleTreeNode node)
{ {
var submodules = Submodules; if (Content is ViewModels.SubmoduleCollectionAsTree tree)
if (submodules != null)
{ {
submodules.ToggleExpand(node); tree.ToggleExpand(node);
Rows = submodules.Rows.Count; Rows = tree.Rows.Count;
RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); RaiseEvent(new RoutedEventArgs(RowsChangedEvent));
} }
} }
@ -133,9 +123,15 @@ namespace SourceGit.Views
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == SubmodulesProperty) if (change.Property == ContentProperty)
{ {
Rows = Submodules?.Rows.Count ?? 0; if (Content is ViewModels.SubmoduleCollectionAsTree tree)
Rows = tree.Rows.Count;
else if (Content is ViewModels.SubmoduleCollectionAsList list)
Rows = list.Submodules.Count;
else
Rows = 0;
RaiseEvent(new RoutedEventArgs(RowsChangedEvent)); RaiseEvent(new RoutedEventArgs(RowsChangedEvent));
} }
else if (change.Property == IsVisibleProperty) else if (change.Property == IsVisibleProperty)
@ -144,28 +140,40 @@ namespace SourceGit.Views
} }
} }
private void OnDoubleTappedNode(object sender, TappedEventArgs e) private void OnItemDoubleTapped(object sender, TappedEventArgs e)
{ {
if (sender is Control { DataContext: ViewModels.SubmoduleTreeNode node } && if (sender is Control control && DataContext is ViewModels.Repository repo)
DataContext is ViewModels.Repository repo)
{ {
if (node.IsFolder) if (control.DataContext is ViewModels.SubmoduleTreeNode node)
ToggleNodeIsExpanded(node); {
else if (node.Module.Status != Models.SubmoduleStatus.NotInited) if (node.IsFolder)
repo.OpenSubmodule(node.Module.Path); ToggleNodeIsExpanded(node);
else if (node.Module.Status != Models.SubmoduleStatus.NotInited)
repo.OpenSubmodule(node.Module.Path);
}
else if (control.DataContext is Models.Submodule m && m.Status != Models.SubmoduleStatus.NotInited)
{
repo.OpenSubmodule(m.Path);
}
} }
e.Handled = true; e.Handled = true;
} }
private void OnRowContextRequested(object sender, ContextRequestedEventArgs e) private void OnItemContextRequested(object sender, ContextRequestedEventArgs e)
{ {
if (sender is Control { DataContext: ViewModels.SubmoduleTreeNode node } control && if (sender is Control control && DataContext is ViewModels.Repository repo)
node.Module != null &&
DataContext is ViewModels.Repository repo)
{ {
var menu = repo.CreateContextMenuForSubmodule(node.Module); if (control.DataContext is ViewModels.SubmoduleTreeNode node && node.Module != null)
menu?.Open(control); {
var menu = repo.CreateContextMenuForSubmodule(node.Module);
menu?.Open(control);
}
else if (control.DataContext is Models.Submodule m)
{
var menu = repo.CreateContextMenuForSubmodule(m);
menu?.Open(control);
}
} }
e.Handled = true; e.Handled = true;