Skip to content

Commit 19c8016

Browse files
Copilottautcony
andcommitted
Add log viewer, about dialog, and drag-and-drop file support
Co-authored-by: tautcony <8295052+tautcony@users.noreply.github.com>
1 parent 5bbb546 commit 19c8016

File tree

8 files changed

+336
-4
lines changed

8 files changed

+336
-4
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Avalonia.Input.Platform;
5+
using CommunityToolkit.Mvvm.ComponentModel;
6+
using CommunityToolkit.Mvvm.Input;
7+
using ChapterTool.Util;
8+
9+
namespace ChapterTool.Avalonia.ViewModels;
10+
11+
public partial class LogWindowViewModel : ViewModelBase
12+
{
13+
[ObservableProperty]
14+
private string _logText = string.Empty;
15+
16+
[ObservableProperty]
17+
private int _lineCount = 0;
18+
19+
private IClipboard? _clipboard;
20+
21+
public LogWindowViewModel()
22+
{
23+
RefreshLog();
24+
25+
// Subscribe to log updates
26+
Logger.LogLineAdded += OnLogLineAdded;
27+
}
28+
29+
public void SetClipboard(IClipboard clipboard)
30+
{
31+
_clipboard = clipboard;
32+
}
33+
34+
private void OnLogLineAdded(string line, DateTime timestamp)
35+
{
36+
RefreshLog();
37+
}
38+
39+
public void RefreshLog()
40+
{
41+
LogText = Logger.LogText;
42+
LineCount = LogText.Split('\n').Length;
43+
}
44+
45+
[RelayCommand]
46+
private void ClearLog()
47+
{
48+
// Note: Logger doesn't have a clear method, so we just show empty
49+
LogText = "Log cleared (in-memory log still retained)";
50+
LineCount = 1;
51+
}
52+
53+
[RelayCommand]
54+
private async Task CopyLog()
55+
{
56+
try
57+
{
58+
if (_clipboard != null)
59+
{
60+
await _clipboard.SetTextAsync(LogText);
61+
}
62+
}
63+
catch
64+
{
65+
// Silently fail if clipboard access is not available
66+
}
67+
}
68+
}

ChapterTool.Avalonia/ViewModels/MainWindowViewModel.cs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using CommunityToolkit.Mvvm.Input;
1111
using ChapterTool.Util;
1212
using ChapterTool.Util.ChapterData;
13+
using ChapterTool.Avalonia.Views;
1314

1415
namespace ChapterTool.Avalonia.ViewModels;
1516

@@ -316,17 +317,50 @@ private void ApplyExpression()
316317
[RelayCommand]
317318
private void ShowLog()
318319
{
319-
// Log viewer - show current log content
320-
var logText = Logger.LogText;
321-
StatusMessage = $"Log has {logText.Split('\n').Length} lines";
320+
// Open log viewer window
321+
var logWindow = new LogWindow
322+
{
323+
DataContext = new LogWindowViewModel()
324+
};
325+
logWindow.Show();
326+
StatusMessage = "Log viewer opened";
322327
}
323328

324329
[RelayCommand]
325330
private void ShowAbout()
326331
{
332+
// Open about dialog
333+
if (_mainWindow != null)
334+
{
335+
var aboutWindow = new AboutWindow();
336+
aboutWindow.ShowDialog(_mainWindow);
337+
}
327338
StatusMessage = "ChapterTool - Modern Edition | .NET 8 + Avalonia UI";
328339
}
329340

341+
public async void HandleFileDrop(string[] files)
342+
{
343+
if (files == null || files.Length == 0) return;
344+
345+
var filePath = files[0]; // Take first file
346+
FilePath = filePath;
347+
348+
StatusMessage = "Loading dropped file...";
349+
Logger.Log($"File dropped: {filePath}");
350+
351+
try
352+
{
353+
await LoadChapterFile(filePath);
354+
StatusMessage = $"Loaded: {Path.GetFileName(filePath)} - {Chapters.Count} chapters";
355+
WindowTitle = $"ChapterTool - {Path.GetFileName(filePath)}";
356+
}
357+
catch (Exception ex)
358+
{
359+
StatusMessage = $"Error: {ex.Message}";
360+
Logger.Log($"Error loading dropped file: {ex.Message}");
361+
}
362+
}
363+
330364
partial void OnAutoGenNameChanged(bool value)
331365
{
332366
UpdateChapterDisplay();
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<Window 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+
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="400"
6+
x:Class="ChapterTool.Avalonia.Views.AboutWindow"
7+
Title="About ChapterTool"
8+
Width="500" Height="400"
9+
CanResize="False"
10+
WindowStartupLocation="CenterOwner"
11+
Icon="/Assets/icon.ico">
12+
13+
<Border Padding="20">
14+
<StackPanel Spacing="16">
15+
<!-- App Icon -->
16+
<Border Width="80" Height="80" HorizontalAlignment="Center">
17+
<Image Source="/Assets/icon.ico" />
18+
</Border>
19+
20+
<!-- App Name and Version -->
21+
<StackPanel Spacing="4" HorizontalAlignment="Center">
22+
<TextBlock Text="ChapterTool"
23+
FontSize="24"
24+
FontWeight="Bold"
25+
HorizontalAlignment="Center"/>
26+
<TextBlock Text="Modern Cross-Platform Edition"
27+
FontSize="14"
28+
Foreground="#666666"
29+
HorizontalAlignment="Center"/>
30+
<TextBlock Text="Version 2.0.0"
31+
FontSize="12"
32+
Foreground="#888888"
33+
HorizontalAlignment="Center"/>
34+
</StackPanel>
35+
36+
<!-- Description -->
37+
<TextBlock Text="A modern tool for extracting and editing chapters from various media formats"
38+
TextWrapping="Wrap"
39+
HorizontalAlignment="Center"
40+
TextAlignment="Center"
41+
MaxWidth="400"
42+
Foreground="#555555"/>
43+
44+
<Separator/>
45+
46+
<!-- Technology Stack -->
47+
<StackPanel Spacing="8">
48+
<TextBlock Text="Technology Stack:" FontWeight="Bold" Foreground="#333333"/>
49+
<StackPanel Spacing="4" Margin="16,0,0,0">
50+
<TextBlock Text="• .NET 8.0" Foreground="#555555"/>
51+
<TextBlock Text="• Avalonia UI 11.3.6" Foreground="#555555"/>
52+
<TextBlock Text="• MVVM Toolkit 8.2.1" Foreground="#555555"/>
53+
</StackPanel>
54+
</StackPanel>
55+
56+
<Separator/>
57+
58+
<!-- Supported Formats -->
59+
<StackPanel Spacing="8">
60+
<TextBlock Text="Supported Formats:" FontWeight="Bold" Foreground="#333333"/>
61+
<TextBlock Text="MPLS, XML, OGM, IFO, Matroska, CUE, FLAC, TAK, XPL, MP4, WebVTT"
62+
TextWrapping="Wrap"
63+
Margin="16,0,0,0"
64+
Foreground="#555555"/>
65+
</StackPanel>
66+
67+
<Separator/>
68+
69+
<!-- Copyright -->
70+
<StackPanel Spacing="4">
71+
<TextBlock Text="© 2014-2025 TautCony"
72+
HorizontalAlignment="Center"
73+
Foreground="#888888"
74+
FontSize="11"/>
75+
<TextBlock Text="Licensed under GPL v3+"
76+
HorizontalAlignment="Center"
77+
Foreground="#888888"
78+
FontSize="11"/>
79+
</StackPanel>
80+
81+
<!-- Close Button -->
82+
<Button Content="Close"
83+
HorizontalAlignment="Center"
84+
MinWidth="100"
85+
Click="CloseButton_Click"
86+
Margin="0,8,0,0"/>
87+
</StackPanel>
88+
</Border>
89+
</Window>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Avalonia.Controls;
2+
using Avalonia.Interactivity;
3+
4+
namespace ChapterTool.Avalonia.Views;
5+
6+
public partial class AboutWindow : Window
7+
{
8+
public AboutWindow()
9+
{
10+
InitializeComponent();
11+
}
12+
13+
private void CloseButton_Click(object? sender, RoutedEventArgs e)
14+
{
15+
Close();
16+
}
17+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<Window xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:vm="using:ChapterTool.Avalonia.ViewModels"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
7+
x:Class="ChapterTool.Avalonia.Views.LogWindow"
8+
x:DataType="vm:LogWindowViewModel"
9+
Title="Log Viewer"
10+
Width="800" Height="600"
11+
MinWidth="600" MinHeight="400"
12+
Icon="/Assets/icon.ico">
13+
14+
<Design.DataContext>
15+
<vm:LogWindowViewModel/>
16+
</Design.DataContext>
17+
18+
<DockPanel>
19+
<!-- Toolbar -->
20+
<Border DockPanel.Dock="Top" BorderBrush="#CCCCCC" BorderThickness="0,0,0,1" Padding="8">
21+
<StackPanel Orientation="Horizontal" Spacing="8">
22+
<Button Command="{Binding ClearLogCommand}" Padding="8,4">
23+
<StackPanel Orientation="Horizontal" Spacing="4">
24+
<PathIcon Data="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"
25+
Width="14" Height="14"/>
26+
<TextBlock Text="Clear" VerticalAlignment="Center"/>
27+
</StackPanel>
28+
</Button>
29+
30+
<Button Command="{Binding CopyLogCommand}" Padding="8,4">
31+
<StackPanel Orientation="Horizontal" Spacing="4">
32+
<PathIcon Data="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"
33+
Width="14" Height="14"/>
34+
<TextBlock Text="Copy" VerticalAlignment="Center"/>
35+
</StackPanel>
36+
</Button>
37+
38+
<Separator/>
39+
40+
<TextBlock Text="{Binding LineCount, StringFormat='Lines: {0}'}"
41+
VerticalAlignment="Center"
42+
Margin="8,0"/>
43+
</StackPanel>
44+
</Border>
45+
46+
<!-- Log Content -->
47+
<Border BorderBrush="#CCCCCC" BorderThickness="1" Margin="8">
48+
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
49+
<TextBox Text="{Binding LogText}"
50+
IsReadOnly="True"
51+
TextWrapping="NoWrap"
52+
BorderThickness="0"
53+
Background="White"
54+
FontFamily="Consolas,Courier New,monospace"
55+
FontSize="12"
56+
AcceptsReturn="True"/>
57+
</ScrollViewer>
58+
</Border>
59+
</DockPanel>
60+
</Window>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Avalonia.Controls;
2+
3+
namespace ChapterTool.Avalonia.Views;
4+
5+
public partial class LogWindow : Window
6+
{
7+
public LogWindow()
8+
{
9+
InitializeComponent();
10+
11+
// Set clipboard reference if ViewModel is available
12+
if (DataContext is ViewModels.LogWindowViewModel viewModel && Clipboard != null)
13+
{
14+
viewModel.SetClipboard(Clipboard);
15+
}
16+
}
17+
}

ChapterTool.Avalonia/Views/MainWindow.axaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
Icon="/Assets/icon.ico"
1010
Title="{Binding WindowTitle}"
1111
Width="1200" Height="700"
12-
MinWidth="800" MinHeight="600">
12+
MinWidth="800" MinHeight="600"
13+
DragDrop.AllowDrop="True">
1314

1415
<Design.DataContext>
1516
<vm:MainWindowViewModel/>
@@ -59,6 +60,12 @@
5960
Background="#FAFAFA">
6061
<DockPanel>
6162
<TextBlock DockPanel.Dock="Left" Text="File: " FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,8,0"/>
63+
<TextBlock DockPanel.Dock="Right"
64+
Text="💾 Drag &amp; drop files here"
65+
VerticalAlignment="Center"
66+
Margin="8,0,0,0"
67+
Foreground="#888888"
68+
FontSize="11"/>
6269
<TextBlock Text="{Binding FilePath, FallbackValue='No file loaded'}"
6370
TextWrapping="Wrap" VerticalAlignment="Center"
6471
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"/>

ChapterTool.Avalonia/Views/MainWindow.axaml.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Avalonia.Controls;
2+
using Avalonia.Input;
23
using ChapterTool.Avalonia.ViewModels;
34

45
namespace ChapterTool.Avalonia.Views;
@@ -14,5 +15,44 @@ public MainWindow()
1415
{
1516
viewModel.SetMainWindow(this);
1617
}
18+
19+
// Enable drag and drop
20+
AddHandler(DragDrop.DropEvent, Drop);
21+
AddHandler(DragDrop.DragOverEvent, DragOver);
22+
}
23+
24+
private void DragOver(object? sender, DragEventArgs e)
25+
{
26+
// Only allow files
27+
if (e.Data.Contains(DataFormats.Files))
28+
{
29+
e.DragEffects = DragDropEffects.Copy;
30+
}
31+
else
32+
{
33+
e.DragEffects = DragDropEffects.None;
34+
}
35+
}
36+
37+
private void Drop(object? sender, DragEventArgs e)
38+
{
39+
if (e.Data.Contains(DataFormats.Files))
40+
{
41+
var files = e.Data.GetFiles();
42+
if (files != null)
43+
{
44+
var filePaths = new System.Collections.Generic.List<string>();
45+
foreach (var file in files)
46+
{
47+
filePaths.Add(file.Path.LocalPath);
48+
}
49+
50+
if (DataContext is MainWindowViewModel viewModel && filePaths.Count > 0)
51+
{
52+
viewModel.HandleFileDrop(filePaths.ToArray());
53+
}
54+
}
55+
}
1756
}
1857
}
58+

0 commit comments

Comments
 (0)