ux: tree toggle button

This commit is contained in:
leo 2024-07-12 11:01:02 +08:00
parent 67d0167278
commit 3c770e2525
No known key found for this signature in database
3 changed files with 104 additions and 70 deletions

View file

@ -1002,7 +1002,7 @@
<Setter Property="VerticalAlignment" Value="Stretch"/> <Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Grid ColumnDefinitions="Auto,*"> <Grid ColumnDefinitions="Auto,*" Background="Transparent">
<Path Grid.Column="0" <Path Grid.Column="0"
x:Name="PART_IndicatorIcon" x:Name="PART_IndicatorIcon"
Width="10" Width="10"
@ -1072,6 +1072,7 @@
<Setter Property="Margin" Value="0" /> <Setter Property="Margin" Value="0" />
<Setter Property="Width" Value="8" /> <Setter Property="Width" Value="8" />
<Setter Property="Height" Value="8" /> <Setter Property="Height" Value="8" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Border Background="Transparent" <Border Background="Transparent"
@ -1224,8 +1225,7 @@
Classes="tree_expander" Classes="tree_expander"
Focusable="False" Focusable="False"
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"/>
IsHitTestVisible="False" />
</Panel> </Panel>
<ContentPresenter Name="PART_HeaderPresenter" <ContentPresenter Name="PART_HeaderPresenter"
Grid.Column="1" Grid.Column="1"

View file

@ -53,51 +53,55 @@
<DataTemplate x:DataType="vm:BranchTreeNode"> <DataTemplate x:DataType="vm:BranchTreeNode">
<Grid Height="24" <Grid Height="24"
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}" Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
ColumnDefinitions="16,20,*,Auto,Auto" ColumnDefinitions="16,*"
Background="Transparent"
DoubleTapped="OnDoubleTappedBranchNode"
ToolTip.Tip="{Binding Tooltip}"> ToolTip.Tip="{Binding Tooltip}">
<!-- Tree Expander --> <!-- Tree Expander -->
<ToggleButton Grid.Column="0" <v:BranchTreeNodeToggleButton Grid.Column="0"
Classes="tree_expander" Classes="tree_expander"
Focusable="False" Focusable="False"
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsChecked="{Binding IsExpanded}" IsChecked="{Binding IsExpanded, Mode=OneWay}"
IsHitTestVisible="False" IsVisible="{Binding !IsBranch}"/>
IsVisible="{Binding !IsBranch}"/>
<!-- Icon --> <!-- Content Area (allows double-click) -->
<v:BranchTreeNodeIcon Grid.Column="1" <Grid Grid.Column="1"
Node="{Binding}" Background="Transparent"
IsExpanded="{Binding IsExpanded}"/> ColumnDefinitions="20,*,Auto,Auto"
DoubleTapped="OnDoubleTappedBranchNode">
<!-- Icon -->
<v:BranchTreeNodeIcon Grid.Column="0"
Node="{Binding}"
IsExpanded="{Binding IsExpanded}"/>
<!-- Name --> <!-- Name -->
<TextBlock Grid.Column="2" <TextBlock Grid.Column="1"
Text="{Binding Name}" Text="{Binding Name}"
Classes="monospace" Classes="monospace"
FontWeight="{Binding NameFontWeight}"/> FontWeight="{Binding NameFontWeight}"/>
<!-- Tracking status --> <!-- Tracking status -->
<Border Grid.Column="3" <Border Grid.Column="2"
Margin="8,0" Margin="8,0"
Height="18" Height="18"
CornerRadius="9" CornerRadius="9"
VerticalAlignment="Center" VerticalAlignment="Center"
Background="{DynamicResource Brush.Badge}" Background="{DynamicResource Brush.Badge}"
IsVisible="{Binding IsUpstreamTrackStatusVisible}"> IsVisible="{Binding IsUpstreamTrackStatusVisible}">
<TextBlock Classes="monospace" FontSize="10" HorizontalAlignment="Center" Margin="9,0" Text="{Binding UpstreamTrackStatus}" Foreground="{DynamicResource Brush.BadgeFG}"/> <TextBlock Classes="monospace" FontSize="10" HorizontalAlignment="Center" Margin="9,0" Text="{Binding UpstreamTrackStatus}" Foreground="{DynamicResource Brush.BadgeFG}"/>
</Border> </Border>
<!-- Filter Toggle Button --> <!-- Filter Toggle Button -->
<ToggleButton Grid.Column="4" <ToggleButton Grid.Column="3"
Classes="filter" Classes="filter"
Margin="0,0,8,0" Margin="0,0,8,0"
Background="Transparent" Background="Transparent"
IsCheckedChanged="OnToggleFilter" IsCheckedChanged="OnToggleFilter"
IsVisible="{Binding IsBranch}" IsVisible="{Binding IsBranch}"
IsChecked="{Binding IsFiltered}" IsChecked="{Binding IsFiltered}"
ToolTip.Tip="{DynamicResource Text.Filter}"/> ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>

View file

@ -87,6 +87,23 @@ namespace SourceGit.Views
} }
} }
public class BranchTreeNodeToggleButton : ToggleButton
{
protected override Type StyleKeyOverride => typeof(ToggleButton);
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
DataContext is ViewModels.BranchTreeNode { IsBranch: false } node)
{
var tree = this.FindAncestorOfType<BranchTree>();
tree.ToggleNodeIsExpanded(node);
}
e.Handled = true;
}
}
public partial class BranchTree : UserControl public partial class BranchTree : UserControl
{ {
public static readonly StyledProperty<List<ViewModels.BranchTreeNode>> NodesProperty = public static readonly StyledProperty<List<ViewModels.BranchTreeNode>> NodesProperty =
@ -132,6 +149,42 @@ namespace SourceGit.Views
BranchesPresenter.SelectedItem = null; BranchesPresenter.SelectedItem = null;
} }
public void ToggleNodeIsExpanded(ViewModels.BranchTreeNode node)
{
_disableSelectionChangingEvent = true;
node.IsExpanded = !node.IsExpanded;
var rows = Rows;
var depth = node.Depth;
var idx = rows.IndexOf(node);
if (idx == -1)
return;
if (node.IsExpanded)
{
var subtree = new List<ViewModels.BranchTreeNode>();
MakeRows(subtree, node.Children, depth + 1);
rows.InsertRange(idx + 1, subtree);
}
else
{
var removeCount = 0;
for (int i = idx + 1; i < rows.Count; i++)
{
var row = rows[i];
if (row.Depth <= depth)
break;
row.IsSelected = false;
removeCount++;
}
rows.RemoveRange(idx + 1, removeCount);
}
RaiseEvent(new RoutedEventArgs(RowsChangedEvent));
_disableSelectionChangingEvent = false;
}
protected override void OnSizeChanged(SizeChangedEventArgs e) protected override void OnSizeChanged(SizeChangedEventArgs e)
{ {
base.OnSizeChanged(e); base.OnSizeChanged(e);
@ -165,6 +218,9 @@ namespace SourceGit.Views
private void OnNodesSelectionChanged(object _, SelectionChangedEventArgs e) private void OnNodesSelectionChanged(object _, SelectionChangedEventArgs e)
{ {
if (_disableSelectionChangingEvent)
return;
var repo = DataContext as ViewModels.Repository; var repo = DataContext as ViewModels.Repository;
if (repo?.Settings == null) if (repo?.Settings == null)
return; return;
@ -273,35 +329,7 @@ namespace SourceGit.Views
} }
else else
{ {
node.IsExpanded = !node.IsExpanded; ToggleNodeIsExpanded(node);
var rows = Rows;
var depth = node.Depth;
var idx = rows.IndexOf(node);
if (idx == -1)
return;
if (node.IsExpanded)
{
var subtree = new List<ViewModels.BranchTreeNode>();
MakeRows(subtree, node.Children, depth + 1);
rows.InsertRange(idx + 1, subtree);
}
else
{
var removeCount = 0;
for (int i = idx + 1; i < rows.Count; i++)
{
var row = rows[i];
if (row.Depth <= depth)
break;
removeCount++;
}
rows.RemoveRange(idx + 1, removeCount);
}
RaiseEvent(new RoutedEventArgs(RowsChangedEvent));
} }
} }
} }
@ -342,6 +370,8 @@ namespace SourceGit.Views
foreach (var sub in node.Children) foreach (var sub in node.Children)
CollectBranchesInNode(outs, sub); CollectBranchesInNode(outs, sub);
} }
private bool _disableSelectionChangingEvent = false;
} }
} }