Skip to content

Commit c02570e

Browse files
committed
refactor: unify the behaviour to open the built-in hex-editor
Signed-off-by: leo <longshuang@msn.cn>
1 parent fcf6441 commit c02570e

9 files changed

Lines changed: 179 additions & 225 deletions

src/ViewModels/BinaryFileViewer.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Threading.Tasks;
2+
using CommunityToolkit.Mvvm.ComponentModel;
3+
4+
namespace SourceGit.ViewModels
5+
{
6+
public class BinaryFileViewer : ObservableObject
7+
{
8+
public string File
9+
{
10+
get => _file;
11+
}
12+
13+
public bool IsLoading
14+
{
15+
get => _isLoading;
16+
private set => SetProperty(ref _isLoading, value);
17+
}
18+
19+
public BinaryFile Content
20+
{
21+
get => _content;
22+
private set => SetProperty(ref _content, value);
23+
}
24+
25+
public BinaryFileViewer(string repo, string file, string revision)
26+
{
27+
_repo = repo;
28+
_file = file;
29+
_revision = revision;
30+
}
31+
32+
public async Task LoadAsync()
33+
{
34+
Content = await BinaryFile.LoadAsync(_repo, _file, _revision)
35+
.ConfigureAwait(false);
36+
IsLoading = false;
37+
}
38+
39+
public void Cleanup()
40+
{
41+
_repo = null;
42+
_file = null;
43+
_revision = null;
44+
45+
_content?.Dispose();
46+
_content = null;
47+
}
48+
49+
private bool _isLoading = true;
50+
private string _repo = null;
51+
private string _file = null;
52+
private string _revision = null;
53+
private BinaryFile _content = null;
54+
}
55+
}

src/ViewModels/BinaryFileViewerStandalone.cs

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/Views/BinaryFileViewer.axaml

Lines changed: 83 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,55 @@
1-
<UserControl xmlns="https://github.com/avaloniaui"
2-
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3-
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4-
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5-
xmlns:m="using:SourceGit.Models"
6-
xmlns:vm="using:SourceGit.ViewModels"
7-
xmlns:v="using:SourceGit.Views"
8-
xmlns:c="using:SourceGit.Converters"
9-
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
10-
x:Class="SourceGit.Views.BinaryFileViewer">
11-
<UserControl.DataTemplates>
12-
<DataTemplate DataType="m:RevisionBinaryFile">
13-
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
14-
<Path Width="64" Height="64" Data="{StaticResource Icons.Binary}" Fill="{DynamicResource Brush.FG2}"/>
15-
<TextBlock Margin="0,16,0,0" Text="{DynamicResource Text.File.Binary}" FontSize="18" FontWeight="Bold" HorizontalAlignment="Center" Foreground="{DynamicResource Brush.FG2}"/>
16-
<StackPanel Margin="0,8,0,0" Orientation="Horizontal" HorizontalAlignment="Center">
17-
<TextBlock Text="{Binding Size, Converter={x:Static c:LongConverters.ToFileSize}}" Foreground="{DynamicResource Brush.FG2}"/>
18-
</StackPanel>
19-
<Button Classes="flat"
20-
Margin="0,16,0,0"
21-
HorizontalAlignment="Center"
22-
Content="{DynamicResource Text.File.HexViewer}"
23-
Click="OnOpenHexViewer"/>
24-
</StackPanel>
25-
</DataTemplate>
26-
27-
<DataTemplate DataType="m:Null">
28-
<v:LoadingIcon Width="64" Height="64"
29-
HorizontalAlignment="Center" VerticalAlignment="Center"/>
30-
</DataTemplate>
1+
<v:ChromelessWindow xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:vm="using:SourceGit.ViewModels"
6+
xmlns:v="using:SourceGit.Views"
7+
mc:Ignorable="d" d:DesignWidth="520" d:DesignHeight="230"
8+
x:Class="SourceGit.Views.BinaryFileViewer"
9+
x:Name="ThisControl"
10+
x:DataType="vm:BinaryFileViewer"
11+
Icon="/App.ico"
12+
Title="{DynamicResource Text.File.HexViewer}"
13+
Width="800" Height="600"
14+
WindowStartupLocation="CenterOwner">
15+
<Grid RowDefinitions="Auto,32,*">
16+
<!-- TitleBar -->
17+
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
18+
<!-- Bottom border -->
19+
<Border Background="{DynamicResource Brush.TitleBar}"
20+
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border2}"
21+
DoubleTapped="MaximizeOrRestoreWindow"
22+
PointerPressed="BeginMoveWindow"/>
23+
24+
<Path Width="12" Height="12"
25+
Margin="10,0,0,0"
26+
HorizontalAlignment="Left"
27+
Data="{StaticResource Icons.Binary}"
28+
IsVisible="{OnPlatform True, macOS=False}"/>
29+
30+
<TextBlock Classes="bold"
31+
Text="{DynamicResource Text.File.HexViewer}"
32+
HorizontalAlignment="Center" VerticalAlignment="Center"
33+
IsHitTestVisible="False"/>
34+
35+
<!-- Caption Buttons (Windows/Linux) -->
36+
<v:CaptionButtons HorizontalAlignment="Right" IsVisible="{OnPlatform True, macOS=False}"/>
37+
</Grid>
38+
39+
<!-- File -->
40+
<Border Grid.Row="1" Height="32" VerticalAlignment="Center">
41+
<Grid Margin="8,0" ColumnDefinitions="Auto,*,Auto">
42+
<Path Grid.Column="0"
43+
Width="14" Height="14"
44+
Data="{StaticResource Icons.File}"/>
3145

32-
<DataTemplate DataType="vm:BinaryFile">
33-
<Grid RowDefinitions="28,*" Background="Transparent">
34-
<v:BinaryFileAddressTextBox Grid.Row="0"
35-
Width="600" Height="26"
46+
<TextBlock Grid.Column="1"
47+
Margin="4,0,0,0"
48+
Text="{Binding File, Mode=OneWay}"
49+
VerticalAlignment="Center"/>
50+
51+
<v:BinaryFileAddressTextBox Grid.Column="2"
52+
Width="200" Height="26"
3653
HorizontalAlignment="Left" VerticalAlignment="Center"
3754
BorderThickness="1"
3855
BorderBrush="{DynamicResource Brush.Border2}"
@@ -48,23 +65,36 @@
4865
Data="{StaticResource Icons.Address}"/>
4966
</TextBox.InnerLeftContent>
5067
</v:BinaryFileAddressTextBox>
51-
52-
<Grid Grid.Row="1" ColumnDefinitions="*,Auto">
53-
<v:HexViewer Grid.Column="0"
54-
Foreground="{DynamicResource Brush.FG1}"
55-
HeaderForeground="{DynamicResource Brush.Accent}"
56-
ClipToBounds="True"/>
57-
58-
<ScrollBar Grid.Column="1"
59-
x:Name="HexViewerScroller"
60-
Margin="0,24,0,0"
61-
Orientation="Vertical"
62-
AllowAutoHide="False"
63-
SmallChange="18"
64-
ValueChanged="OnScrollBarValueChanged"
65-
SizeChanged="OnScrollBarSizeChanged"/>
66-
</Grid>
6768
</Grid>
68-
</DataTemplate>
69-
</UserControl.DataTemplates>
70-
</UserControl>
69+
</Border>
70+
71+
<!-- Body -->
72+
<Border Grid.Row="2" Margin="4,0,4,4" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}">
73+
<ContentControl x:Name="ContentPanel" Margin="8,4,0,4" Content="{Binding Content, Mode=OneWay}">
74+
<ContentControl.DataTemplates>
75+
<DataTemplate DataType="vm:BinaryFile">
76+
<Grid ColumnDefinitions="*,Auto" Background="Transparent" PointerWheelChanged="OnContentPointerWheelChanged">
77+
<v:HexViewer Grid.Column="0"
78+
Foreground="{DynamicResource Brush.FG1}"
79+
HeaderForeground="{DynamicResource Brush.Accent}"
80+
ClipToBounds="True"/>
81+
82+
<ScrollBar Grid.Column="1"
83+
Margin="0,24,0,0"
84+
Orientation="Vertical"
85+
AllowAutoHide="False"
86+
SmallChange="18"
87+
ValueChanged="OnScrollBarValueChanged"
88+
SizeChanged="OnScrollBarSizeChanged"/>
89+
</Grid>
90+
</DataTemplate>
91+
</ContentControl.DataTemplates>
92+
</ContentControl>
93+
</Border>
94+
95+
<!-- Loading Icon -->
96+
<v:LoadingIcon Grid.Row="2"
97+
Width="64" Height="64"
98+
IsVisible="{Binding IsLoading, Mode=OneWay}"/>
99+
</Grid>
100+
</v:ChromelessWindow>

src/Views/BinaryFileViewer.axaml.cs

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Globalization;
33
using System.Text;
4-
using System.Threading;
54

65
using Avalonia;
76
using Avalonia.Controls;
@@ -10,6 +9,7 @@
109
using Avalonia.Input.Platform;
1110
using Avalonia.Interactivity;
1211
using Avalonia.Media;
12+
using Avalonia.Threading;
1313
using Avalonia.VisualTree;
1414

1515
namespace SourceGit.Views
@@ -299,47 +299,39 @@ protected override void OnPointerPressed(PointerPressedEventArgs e)
299299
private long _highlightedIdx = -1;
300300
}
301301

302-
public partial class BinaryFileViewer : UserControl
302+
public partial class BinaryFileViewer : ChromelessWindow
303303
{
304304
public BinaryFileViewer()
305305
{
306306
InitializeComponent();
307307
}
308308

309-
protected override void OnUnloaded(RoutedEventArgs e)
309+
protected override void OnOpened(EventArgs e)
310310
{
311-
base.OnUnloaded(e);
311+
base.OnOpened(e);
312312

313-
_cancellation?.Cancel();
314-
315-
if (Content is ViewModels.BinaryFile old)
313+
// Queue loading to the next frame to avoid blocking the window-opening.
314+
Dispatcher.UIThread.Post(async () =>
316315
{
317-
Content = null;
318-
old.Dispose();
319-
}
316+
if (DataContext is ViewModels.BinaryFileViewer vm)
317+
await vm.LoadAsync();
318+
});
320319
}
321320

322-
protected override void OnDataContextChanged(EventArgs e)
321+
protected override void OnClosed(EventArgs e)
323322
{
324-
base.OnDataContextChanged(e);
323+
base.OnClosed(e);
325324

326-
var old = Content;
327-
Content = DataContext;
328-
_cancellation?.Cancel();
329-
330-
if (old is ViewModels.BinaryFile oldFile)
331-
oldFile.Dispose();
325+
if (DataContext is ViewModels.BinaryFileViewer vm)
326+
vm.Cleanup();
332327
}
333328

334-
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
329+
private void OnContentPointerWheelChanged(object sender, PointerWheelEventArgs e)
335330
{
336-
base.OnPointerWheelChanged(e);
337-
338-
if (Content is not ViewModels.BinaryFile)
331+
if (sender is not Grid grid)
339332
return;
340333

341-
// TextBox itself contains a ScrollBar.
342-
var scroller = this.FindDescendantOfType<ScrollBar>(false, s => s.Name.Equals("HexViewerScroller", StringComparison.Ordinal));
334+
var scroller = grid.FindDescendantOfType<ScrollBar>();
343335
if (scroller == null)
344336
return;
345337

@@ -368,25 +360,10 @@ private void OnScrollBarSizeChanged(object sender, SizeChangedEventArgs e)
368360
}
369361
}
370362

371-
private async void OnOpenHexViewer(object sender, RoutedEventArgs e)
372-
{
373-
if (DataContext is not Models.RevisionBinaryFile vm)
374-
return;
375-
376-
Content = new Models.Null();
377-
_cancellation = new();
378-
379-
var token = _cancellation.Token;
380-
var file = await ViewModels.BinaryFile.LoadAsync(vm.Repository, vm.File, vm.Revision);
381-
if (token.IsCancellationRequested)
382-
return;
383-
384-
Content = file;
385-
}
386-
387363
private void OnGotoAddressTextChanged(object sender, TextChangedEventArgs e)
388364
{
389-
if (sender is BinaryFileAddressTextBox { DataContext: ViewModels.BinaryFile file } textBox)
365+
if (sender is BinaryFileAddressTextBox textBox &&
366+
DataContext is ViewModels.BinaryFileViewer { Content: { } file })
390367
{
391368
var text = textBox.Text;
392369
if (string.IsNullOrEmpty(text))
@@ -405,8 +382,7 @@ private void OnGotoAddressTextChanged(object sender, TextChangedEventArgs e)
405382
if (idx < 0 || idx >= file.FileSize)
406383
return;
407384

408-
// TextBox itself contains a ScrollBar.
409-
var scroller = this.FindDescendantOfType<ScrollBar>(false, s => s.Name.Equals("HexViewerScroller", StringComparison.Ordinal));
385+
var scroller = ContentPanel.FindDescendantOfType<ScrollBar>();
410386
if (scroller == null)
411387
return;
412388

@@ -417,7 +393,5 @@ private void OnGotoAddressTextChanged(object sender, TextChangedEventArgs e)
417393
viewer?.SetHighlightedIndex(idx);
418394
}
419395
}
420-
421-
private CancellationTokenSource _cancellation = new();
422396
}
423397
}

0 commit comments

Comments
 (0)