Skip to content

Commit 38e2e0f

Browse files
committed
refactor: rewrite the welcome page since the original TreeView has many limitations (#391)
1 parent af6d2cc commit 38e2e0f

File tree

12 files changed

+223
-239
lines changed

12 files changed

+223
-239
lines changed

src/Resources/Styles.axaml

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,72 +1397,6 @@
13971397
<Setter Property="IsVisible" Value="False"/>
13981398
</Style>
13991399

1400-
<Style Selector="TreeViewItem">
1401-
<Style.Resources>
1402-
<x:Double x:Key="TreeViewItemIndent">16</x:Double>
1403-
</Style.Resources>
1404-
1405-
<Setter Property="BorderThickness" Value="0"/>
1406-
<Setter Property="MinHeight" Value="24" />
1407-
<Setter Property="Template">
1408-
<ControlTemplate>
1409-
<StackPanel>
1410-
<Border Name="PART_LayoutRoot"
1411-
Background="Transparent"
1412-
BorderThickness="0"
1413-
CornerRadius="0"
1414-
MinHeight="{TemplateBinding MinHeight}"
1415-
TemplatedControl.IsTemplateFocusTarget="True">
1416-
<Grid>
1417-
<Border Name="PART_Background" CornerRadius="{TemplateBinding CornerRadius}" Background="Transparent"/>
1418-
1419-
<Grid Name="PART_Header" ColumnDefinitions="16,*" Margin="{TemplateBinding Level, Mode=OneWay, Converter={StaticResource TreeViewItemLeftMarginConverter}}">
1420-
<Panel Name="PART_ExpandCollapseChevronContainer">
1421-
<ToggleButton Name="PART_ExpandCollapseChevron"
1422-
Classes="tree_expander"
1423-
Focusable="False"
1424-
HorizontalAlignment="Center"
1425-
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"/>
1426-
</Panel>
1427-
<ContentPresenter Name="PART_HeaderPresenter"
1428-
Grid.Column="1"
1429-
Focusable="False"
1430-
Background="Transparent"
1431-
Content="{TemplateBinding Header}"
1432-
ContentTemplate="{TemplateBinding HeaderTemplate}"
1433-
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
1434-
VerticalAlignment="{TemplateBinding VerticalAlignment}"
1435-
Margin="{TemplateBinding Padding}" />
1436-
</Grid>
1437-
</Grid>
1438-
</Border>
1439-
<ItemsPresenter Name="PART_ItemsPresenter"
1440-
IsVisible="{TemplateBinding IsExpanded}"
1441-
ItemsPanel="{TemplateBinding ItemsPanel}" />
1442-
</StackPanel>
1443-
</ControlTemplate>
1444-
</Setter>
1445-
1446-
<Style Selector="^ /template/ Border#PART_LayoutRoot:pointerover">
1447-
<Setter Property="Background" Value="Transparent" />
1448-
</Style>
1449-
<Style Selector="^ /template/ Border#PART_LayoutRoot:pointerover Border#PART_Background">
1450-
<Setter Property="Background" Value="{DynamicResource Brush.AccentHovered}" />
1451-
</Style>
1452-
1453-
<Style Selector="^:selected /template/ Border#PART_LayoutRoot">
1454-
<Setter Property="Background" Value="Transparent" />
1455-
</Style>
1456-
<Style Selector="^:selected /template/ Border#PART_LayoutRoot Border#PART_Background">
1457-
<Setter Property="Background" Value="{DynamicResource Brush.Accent}" />
1458-
<Setter Property="Opacity" Value=".4"/>
1459-
</Style>
1460-
<Style Selector="^:selected /template/ Border#PART_LayoutRoot:pointerover Border#PART_Background">
1461-
<Setter Property="Background" Value="{DynamicResource Brush.Accent}" />
1462-
<Setter Property="Opacity" Value=".65"/>
1463-
</Style>
1464-
</Style>
1465-
14661400
<Style Selector="NumericUpDown">
14671401
<Style Selector="^ /template/ ButtonSpinner#PART_Spinner">
14681402
<Setter Property="MinHeight" Value="0"/>

src/ViewModels/Clone.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ public override Task<bool> Sure()
141141
}
142142
}
143143

144+
Welcome.Instance.Refresh();
144145
launcher.OpenRepositoryInTab(node, page);
145146
});
146147

src/ViewModels/CreateGroup.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public override Task<bool> Sure()
2929
IsExpanded = false,
3030
}, _parent);
3131

32+
Welcome.Instance.Refresh();
3233
return null;
3334
}
3435

src/ViewModels/DeleteRepositoryNode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public DeleteRepositoryNode(RepositoryNode node)
1919
public override Task<bool> Sure()
2020
{
2121
Preference.Instance.RemoveNode(_node);
22+
Welcome.Instance.Refresh();
2223
return null;
2324
}
2425

src/ViewModels/EditRepositoryNode.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ public override Task<bool> Sure()
4848
_node.Bookmark = _bookmark;
4949

5050
if (needSort)
51+
{
5152
Preference.Instance.SortByRenamedNode(_node);
53+
Welcome.Instance.Refresh();
54+
}
5255

5356
return null;
5457
}

src/ViewModels/Init.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public override Task<bool> Sure()
3232
{
3333
var normalizedPath = _targetPath.Replace("\\", "/");
3434
Preference.Instance.FindOrAddNodeByRepositoryPath(normalizedPath, _parentNode, true);
35+
Welcome.Instance.Refresh();
3536
});
3637

3738
return true;

src/ViewModels/Launcher.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public Launcher(string startupRepo)
4848

4949
var normalized = root.Replace("\\", "/");
5050
var node = pref.FindOrAddNodeByRepositoryPath(normalized, null, false);
51+
Welcome.Instance.Refresh();
5152
OpenRepositoryInTab(node, null);
5253
}
5354
else if (pref.RestoreTabs)

src/ViewModels/RepositoryNode.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ public bool IsVisible
4949
set => SetProperty(ref _isVisible, value);
5050
}
5151

52+
[JsonIgnore]
53+
public int Depth
54+
{
55+
get;
56+
set;
57+
} = 0;
58+
5259
public AvaloniaList<RepositoryNode> SubNodes
5360
{
5461
get => _subNodes;

src/ViewModels/Welcome.cs

Lines changed: 76 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ public class Welcome : ObservableObject
1212
{
1313
public static Welcome Instance => _instance;
1414

15-
public AvaloniaList<RepositoryNode> RepositoryNodes
15+
public AvaloniaList<RepositoryNode> Rows
1616
{
17-
get => Preference.Instance.RepositoryNodes;
18-
}
17+
get;
18+
private set;
19+
} = [];
1920

2021
public string SearchFilter
2122
{
@@ -27,6 +28,60 @@ public string SearchFilter
2728
}
2829
}
2930

31+
public Welcome()
32+
{
33+
Refresh();
34+
}
35+
36+
public void Refresh()
37+
{
38+
if (string.IsNullOrWhiteSpace(_searchFilter))
39+
{
40+
foreach (var node in Preference.Instance.RepositoryNodes)
41+
ResetVisibility(node);
42+
}
43+
else
44+
{
45+
foreach (var node in Preference.Instance.RepositoryNodes)
46+
SetVisibilityBySearch(node);
47+
}
48+
49+
var rows = new List<RepositoryNode>();
50+
MakeTreeRows(rows, Preference.Instance.RepositoryNodes);
51+
Rows.Clear();
52+
Rows.AddRange(rows);
53+
}
54+
55+
public void ToggleNodeIsExpanded(RepositoryNode node)
56+
{
57+
node.IsExpanded = !node.IsExpanded;
58+
59+
var depth = node.Depth;
60+
var idx = Rows.IndexOf(node);
61+
if (idx == -1)
62+
return;
63+
64+
if (node.IsExpanded)
65+
{
66+
var subrows = new List<RepositoryNode>();
67+
MakeTreeRows(subrows, node.SubNodes, depth + 1);
68+
Rows.InsertRange(idx + 1, subrows);
69+
}
70+
else
71+
{
72+
var removeCount = 0;
73+
for (int i = idx + 1; i < Rows.Count; i++)
74+
{
75+
var row = Rows[i];
76+
if (row.Depth <= depth)
77+
break;
78+
79+
removeCount++;
80+
}
81+
Rows.RemoveRange(idx + 1, removeCount);
82+
}
83+
}
84+
3085
public void InitRepository(string path, RepositoryNode parent)
3186
{
3287
if (!Preference.Instance.IsGitConfigured())
@@ -36,9 +91,7 @@ public void InitRepository(string path, RepositoryNode parent)
3691
}
3792

3893
if (PopupHost.CanCreatePopup())
39-
{
4094
PopupHost.ShowPopup(new Init(path, parent));
41-
}
4295
}
4396

4497
public void Clone()
@@ -75,30 +128,7 @@ public void AddRootNode()
75128
public void MoveNode(RepositoryNode from, RepositoryNode to)
76129
{
77130
Preference.Instance.MoveNode(from, to);
78-
}
79-
80-
public RepositoryNode GetPrevVisible(RepositoryNode node)
81-
{
82-
var visibleRows = new List<RepositoryNode>();
83-
CollectVisibleRows(visibleRows, RepositoryNodes);
84-
85-
var idx = visibleRows.IndexOf(node);
86-
if (idx <= 1)
87-
return null;
88-
89-
return visibleRows[idx - 1];
90-
}
91-
92-
public RepositoryNode GetNextVisible(RepositoryNode node)
93-
{
94-
var visibleRows = new List<RepositoryNode>();
95-
CollectVisibleRows(visibleRows, RepositoryNodes);
96-
97-
var idx = visibleRows.IndexOf(node);
98-
if (idx < 0 || idx >= visibleRows.Count - 1)
99-
return null;
100-
101-
return visibleRows[idx + 1];
131+
Refresh();
102132
}
103133

104134
public ContextMenu CreateContextMenu(RepositoryNode node)
@@ -178,20 +208,6 @@ public ContextMenu CreateContextMenu(RepositoryNode node)
178208
return menu;
179209
}
180210

181-
private void Refresh()
182-
{
183-
if (string.IsNullOrWhiteSpace(_searchFilter))
184-
{
185-
foreach (var node in RepositoryNodes)
186-
ResetVisibility(node);
187-
}
188-
else
189-
{
190-
foreach (var node in RepositoryNodes)
191-
SetVisibilityBySearch(node);
192-
}
193-
}
194-
195211
private void ResetVisibility(RepositoryNode node)
196212
{
197213
node.IsVisible = true;
@@ -226,6 +242,23 @@ private void SetVisibilityBySearch(RepositoryNode node)
226242
}
227243
}
228244

245+
private void MakeTreeRows(List<RepositoryNode> rows, AvaloniaList<RepositoryNode> nodes, int depth = 0)
246+
{
247+
foreach (var node in nodes)
248+
{
249+
if (!node.IsVisible)
250+
continue;
251+
252+
node.Depth = depth;
253+
rows.Add(node);
254+
255+
if (node.IsRepository || !node.IsExpanded)
256+
continue;
257+
258+
MakeTreeRows(rows, node.SubNodes, depth+1);
259+
}
260+
}
261+
229262
private void OpenAllInNode(Launcher launcher, RepositoryNode node)
230263
{
231264
foreach (var subNode in node.SubNodes)
@@ -237,20 +270,6 @@ private void OpenAllInNode(Launcher launcher, RepositoryNode node)
237270
}
238271
}
239272

240-
private void CollectVisibleRows(List<RepositoryNode> visible, AvaloniaList<RepositoryNode> collection)
241-
{
242-
foreach (var node in collection)
243-
{
244-
if (node.IsVisible)
245-
{
246-
visible.Add(node);
247-
248-
if (!node.IsRepository)
249-
CollectVisibleRows(visible, node.SubNodes);
250-
}
251-
}
252-
}
253-
254273
private static Welcome _instance = new Welcome();
255274
private string _searchFilter = string.Empty;
256275
}

0 commit comments

Comments
 (0)