diff --git a/LogVisualizer/App.xaml b/LogVisualizer/App.xaml index f35fcf4..2f1a5e4 100644 --- a/LogVisualizer/App.xaml +++ b/LogVisualizer/App.xaml @@ -1,9 +1,8 @@ - - - - + + diff --git a/LogVisualizer/Models/LineGraph.xaml b/LogVisualizer/Controls/LineGraph.xaml similarity index 98% rename from LogVisualizer/Models/LineGraph.xaml rename to LogVisualizer/Controls/LineGraph.xaml index 99e614e..2841d18 100644 --- a/LogVisualizer/Models/LineGraph.xaml +++ b/LogVisualizer/Controls/LineGraph.xaml @@ -57,7 +57,7 @@ - + - diff --git a/LogVisualizer/Models/PlotGraph.xaml.cs b/LogVisualizer/Controls/PlotGraph.xaml.cs similarity index 81% rename from LogVisualizer/Models/PlotGraph.xaml.cs rename to LogVisualizer/Controls/PlotGraph.xaml.cs index 03ed3e7..982511d 100644 --- a/LogVisualizer/Models/PlotGraph.xaml.cs +++ b/LogVisualizer/Controls/PlotGraph.xaml.cs @@ -1,8 +1,10 @@ using MotecLogSerializer.LdParser; +using ScottPlot; using System.IO; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; +using Color = System.Windows.Media.Color; using DColor = System.Drawing.Color; namespace LogVisualizer.Models; @@ -17,14 +19,15 @@ public partial class PlotGraph : UserControl public readonly IEnumerable OrderedChannels; private static readonly SolidColorBrush SelectedBrush = new(Color.FromArgb(0xFF, 0x00, 0x80, 0xFF)); - private static readonly SolidColorBrush SelectedBrushBG = new(Color.FromArgb(0x10, 0xFF, 0xFF, 0xFF)); - private static readonly SolidColorBrush UnselectedBrush = new(Color.FromArgb(0x01, 0xFF, 0xFF, 0xFF)); + private static readonly SolidColorBrush SelectedBrushBG = new(Color.FromArgb(0x00, 0xFF, 0xFF, 0xFF)); + private static readonly SolidColorBrush UnselectedBrush = new(Color.FromArgb(0x00, 0xFF, 0xFF, 0xFF)); public PlotGraph(string FullFile, IEnumerable Channels) { InitializeComponent(); - this.Graph.Plot.Title(Path.GetFileName(FullFile)); + this.TitleTB.Text = Path.GetFileName(FullFile); + //this.Graph.Plot.Title(Path.GetFileName(FullFile)); this.Graph.Plot.SetStyle(new ScottPlot.PlotStyle() { GridMajorLineColor = ScottPlot.Color.FromColor(DColor.FromArgb(50, 255, 255, 255)), @@ -36,19 +39,16 @@ public PlotGraph(string FullFile, IEnumerable Channels) IEnumerable names = active.Select(c => c.Name); string fixName(LineGraph lg) => names.Count(n => n == lg.Key) > 1 ? $"{lg.Key}.{lg.MetaPtr}" : lg.Key; - this.Channels = active.Select(c => new LineGraph(c, 0.250)).ToDictionary(fixName); + this.Channels = active.Select(c => new LineGraph(c)).ToDictionary(fixName); this.OrderedChannels = this.Channels.Values.OrderBy(g => g.Key); foreach ((string key, LineGraph graph) in this.Channels.OrderBy(_ => _.Key)) { - graph.GraphUpdated += this.Graph.Refresh; - this.Graph.Plot.Add.Plottable(graph.ScatterLine); + graph.GraphUpdated += () => { this.Graph.Plot.Axes.AutoScale(); this.Graph.Refresh(); }; + this.Graph.Plot.Add.Plottable(graph.Line); } - this.Graph.Plot.XLabel("Time (s)"); - foreach (ScottPlot.IAxis axes in Graph.Plot.Axes.GetAxes()) - axes.Min = 0; - + //this.Graph.Plot.XLabel("Time (s)"); this.Graph.Refresh(); } diff --git a/LogVisualizer/LogVisualizer.csproj b/LogVisualizer/LogVisualizer.csproj index 809d052..436767b 100644 --- a/LogVisualizer/LogVisualizer.csproj +++ b/LogVisualizer/LogVisualizer.csproj @@ -16,7 +16,8 @@ - + + diff --git a/LogVisualizer/Windows/MainWindow.xaml b/LogVisualizer/Windows/MainWindow.xaml index 1ce607c..c0874b5 100644 --- a/LogVisualizer/Windows/MainWindow.xaml +++ b/LogVisualizer/Windows/MainWindow.xaml @@ -70,14 +70,14 @@ - + MouseDown="DockPanelMouseDown"> - + - - @@ -230,48 +231,72 @@ - - - + + + + + + + + + + - + public partial class MainWindow : Window, INotifyPropertyChanged -{ - public bool CanUnselectAll => this.ActiveGraph is not null && this.ActiveGraph.Channels.Values.Any(g => g.ScatterLine.IsVisible); +{ + public bool CanUnselectAll => this.ActiveGraph is not null && this.ActiveGraph.Channels.Values.Any(g => g.Line.IsVisible); public List FileGraphItems => this.Files.Select(ToMenuItem).ToList(); public bool CanUnselectGraph => this.ActiveGraph is not null; public event PropertyChangedEventHandler? PropertyChanged; @@ -44,7 +45,7 @@ public partial class MainWindow : Window, INotifyPropertyChanged OnPropertyChanged(nameof(CanUnselectAll)); } } - + private readonly Timer ScaleCheck = new() { Interval = 100, @@ -52,12 +53,74 @@ public partial class MainWindow : Window, INotifyPropertyChanged Enabled = false, }; + private UniformGrid ActiveView => (UniformGrid)this.TabController.SelectedContent; + public MainWindow() { this.InitializeComponent(); this._title = this.Title; this.ScaleCheck.Elapsed += (s, e) => this.Dispatcher.Invoke(this.CheckScale); this.DataContext = this; + + // Set the initial view to the first tab + this.TabController.Items.Insert(this.TabController.Items.Count-1, this.NewTab(false)); + } + + private int counter = 0; + private TabItem NewTab(bool canClose = true) + { + DockPanel dockPanel = new(); + TabItem tbi = new() { Content = new UniformGrid() { Background = System.Windows.Media.Brushes.Transparent, }, Header = dockPanel, Background = Brushes.Gray }; + var tb = new TextBox() + { + Background = Brushes.Transparent, Text = "Tab " + ++counter, IsReadOnly = true, BorderBrush = Brushes.Transparent, + AcceptsReturn = true, IsReadOnlyCaretVisible = false, IsHitTestVisible = false, MinWidth = 10 + }; + void lostFocus(object _, object __) + { + tb.IsReadOnly = true; + tb.IsHitTestVisible = false; + tb.BorderBrush = Brushes.Transparent; + tb.Background = tb.Text.Length == 0 ? new SolidColorBrush(Color.FromArgb(0xF0, 0xFF, 0x00, 0x00)) : (Brush)Brushes.Transparent; + tb.Focusable = false; + } + tbi.PreviewMouseDoubleClick += (s, e) => + { + tb.IsHitTestVisible = true; + tb.IsReadOnly = false; + tb.BorderBrush = Brushes.Cyan; + tb.Focusable = true; + Task.Run(() => this.Dispatcher.Invoke(tb.Focus)); + }; + tb.LostFocus += lostFocus; + tb.PreviewLostKeyboardFocus += lostFocus; + tb.TextChanged += (s, e) => + { + if (tb.Text.Contains('\n')) + { + tb.Text = tb.Text.Replace("\r", "").Replace("\n", ""); + lostFocus(s, e); + } + }; + dockPanel.Children.Add(tb); + + Button closeButton = new() { + Content = "x", Background = Brushes.Transparent, Foreground = Brushes.Red, + HorizontalAlignment = HorizontalAlignment.Right, BorderBrush=Brushes.Transparent, + Padding=new(-5), Margin=new(10,0,0,0), Width=15, + FontFamily = new("Cascadia Mono"), VerticalAlignment = VerticalAlignment.Stretch, + VerticalContentAlignment = VerticalAlignment.Center, HorizontalContentAlignment = HorizontalAlignment.Center + }; + if (canClose) + dockPanel.Children.Add(closeButton); + + closeButton.Click += (s, e) => + { + this.TabController.SelectedIndex -= 1; + this.TabController.Items.Remove(tbi); + }; + + return tbi; } private void MinimizeButton_Click(object sender, RoutedEventArgs e) @@ -72,14 +135,16 @@ private void MaximizeButton_Click(object sender, RoutedEventArgs e) private void CloseButton_Click(object sender, RoutedEventArgs e) => this.Close(); - private void DockPanel_MouseDown(object sender, MouseButtonEventArgs e) + private void DockPanelMouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton != MouseButton.Left) return; + this.TDock.Focus(); this.GrapplePoint = e.GetPosition(this); this.ScalePoint = null; this.ScaleCheck.Stop(); + e.Handled = true; } @@ -110,7 +175,7 @@ private void CheckScale() if (this.ScalePoint is not null && Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed) { Point p = Mouse.PrimaryDevice.GetPosition(this); - Debug.WriteLine(p); + if (this.ScalePoint.Value.X > 0) this.Width = p.X + 15; @@ -124,7 +189,7 @@ private void UncheckAllButtonClick(object sender, RoutedEventArgs e) this.ActiveGraph?.UncheckAll(); OnPropertyChanged(nameof(CanUnselectAll)); } - + private void UnselectAllButtonClick(object sender, RoutedEventArgs e) { this.GraphOnSelected(null); @@ -175,7 +240,7 @@ private void TextBoxTextChanged(object sender, TextChangedEventArgs? e) { this.searchFilter = this.SearchBar.Text; foreach (LineGraph graph in this.ListView.Items.OfType()) - graph.Visibility = graph.Key.Contains(this.searchFilter, StringComparison.InvariantCultureIgnoreCase) ? Visibility.Visible : Visibility.Hidden; + graph.Visibility = graph.Key.Contains(this.searchFilter, StringComparison.InvariantCultureIgnoreCase) ? Visibility.Visible : Visibility.Collapsed; } } @@ -188,10 +253,16 @@ private void ClearSearch(object sender, RoutedEventArgs? e) foreach (LineGraph graph in this.ListView.Items.OfType()) graph.Visibility = Visibility.Visible; + + if (sender is not ToggleButton) + this.ToggleButtonClick(sender, null); } private void GraphOnSelected(PlotGraph? obj) { + if (this.ActiveGraph == obj) + return; + foreach (PlotGraph graph in this.Graphs) { if (graph == obj) @@ -200,7 +271,22 @@ private void GraphOnSelected(PlotGraph? obj) } this.ActiveGraph = obj; - this.ListView.ItemsSource = obj?.OrderedChannels; + if (obj is null) { + this.ListView.ItemsSource = null; + return; + } + + ObservableCollection tmpCollection = []; + this.ListView.ItemsSource = tmpCollection; + + Task.Run(async () => + { + foreach(LineGraph item in obj.OrderedChannels) + { + this.Dispatcher.BeginInvoke(tmpCollection.Add, item); + await Task.Delay(1); + } + }); } private void ScaleOneMouseDown(object sender, MouseButtonEventArgs e) @@ -240,7 +326,7 @@ protected void OnPropertyChanged(string name) private void CloseAllFiles(object sender, RoutedEventArgs e) { this.Title = this._title; - this.GraphViewer.Children.Clear(); + this.ActiveView?.Children.Clear(); this.ListView.ItemsSource = null; this.Files.Clear(); @@ -250,7 +336,7 @@ private void CloseAllFiles(object sender, RoutedEventArgs e) private MenuItem ToMenuItem(KeyValuePair item) { - MenuItem menuItem = new() { Header = item.Key, Tag = item, HorizontalContentAlignment=HorizontalAlignment.Center, VerticalContentAlignment=VerticalAlignment.Center }; + MenuItem menuItem = new() { Header = item.Key, Tag = item, HorizontalContentAlignment = HorizontalAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center }; menuItem.Click += this.OpenGraph; return menuItem; } @@ -266,7 +352,7 @@ private void OpenGraph(object sender, RoutedEventArgs e) graph.OnRemove += g => { this.Graphs.Remove(g); - this.GraphViewer.Children.Remove(g); + this.ActiveView.Children.Remove(g); if (this.ActiveGraph == g) { @@ -276,16 +362,66 @@ private void OpenGraph(object sender, RoutedEventArgs e) }; this.Graphs.Add(graph); - this.GraphViewer.Children.Add(graph); + this.ActiveView.Children.Add(graph); } } private void ChangeLayout(object sender, RoutedEventArgs e) { - var ls = new LayoutSelector(this.GraphViewer.Rows, this.GraphViewer.Columns); + var ls = new LayoutSelector(this.ActiveView.Rows, this.ActiveView.Columns); ls.ShowDialog(); - this.GraphViewer.Rows = ls.Row; - this.GraphViewer.Columns = ls.Column; + this.ActiveView.Rows = ls.Row; + this.ActiveView.Columns = ls.Column; + } + + private void ToggleButtonClick(object sender, RoutedEventArgs? e) + { + if (OnlyViewEnabled.IsChecked == true) + { + OnlyViewEnabled.Foreground = Brushes.Red; + foreach (LineGraph graph in this.ListView.Items.OfType()) + graph.Visibility = graph.VisibilityCB.IsChecked == true ? Visibility.Visible : Visibility.Collapsed; + } + else if (sender is ToggleButton) + { + OnlyViewEnabled.Foreground = Brushes.Black; + if (this.searchOpen) + this.TextBoxTextChanged(sender, null); + else + this.ClearSearch(sender, null); + } + } + + private void TabControlSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is TabControl tabControl && tabControl.SelectedItem is TabItem tbi) + { + if (tbi == this.NewTabControl) + { + Task.Run(() => this.Dispatcher.Invoke(() => + { + int cnt = this.TabController.Items.Count - 1; + TabItem ntb = this.NewTab(); + this.TabController.Items.Insert(cnt, ntb); + this.TabController.SelectedItem = ntb; + })); + } + else + { + this.GraphOnSelected(null); + } + } + } + + private void EnableAll(object sender, RoutedEventArgs e) + { + foreach (LineGraph graph in this.ListView.Items.OfType()) + graph.TmpEnable(); + } + + private void MenuItem_Click(object sender, RoutedEventArgs e) + { + } } \ No newline at end of file