Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions src/modules/peek/Peek.FilePreviewer/Controls/SQLiteControl.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<!-- Licensed under the MIT License. See LICENSE in the project root for license information. -->

<UserControl
x:Class="Peek.FilePreviewer.Controls.SQLiteControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls"
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220" MinWidth="150" MaxWidth="320" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<!-- Left pane: table tree -->
<TreeView
x:Name="TableTreeView"
Grid.Column="0"
ItemInvoked="TableTreeView_ItemInvoked">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}" TextTrimming="CharacterEllipsis">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Content}" />
</ToolTipService.ToolTip>
</TextBlock>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>

<!-- Divider -->
<Border
Grid.Column="1"
Width="1"
Background="{ThemeResource CardStrokeColorDefaultBrush}" />

<!-- Right pane -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<!-- Record count header -->
<Border
x:Name="RecordCountHeader"
Grid.Row="0"
Padding="12,6"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="0,0,0,1"
Visibility="Collapsed">
<TextBlock x:Name="RecordCountText" />
</Border>

<!-- Data grid -->
<toolkit:DataGrid
x:Name="TableDataGrid"
Grid.Row="1"
AreRowDetailsFrozen="True"
AutoGenerateColumns="False"
CanDrag="False"
ColumnWidth="Auto"
GridLinesVisibility="All"
HeadersVisibility="Column"
IsReadOnly="True"
MinColumnWidth="80"
RowDetailsVisibilityMode="Collapsed"
SelectionMode="Extended"
SizeChanged="TableDataGrid_SizeChanged"
Visibility="Collapsed" />

<!-- Placeholder when no table is selected -->
<TextBlock
x:Name="NoSelectionText"
x:Uid="SQLite_NoTableSelected"
Grid.Row="0"
Grid.RowSpan="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</Grid>
</Grid>

<!-- Footer -->
<Border
Grid.Row="1"
Margin="16"
HorizontalAlignment="Center"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8">
<TextBlock
Padding="16,8,16,8"
IsTextSelectionEnabled="True"
Text="{x:Bind TableCount, Mode=OneWay}" />
</Border>
</Grid>
</UserControl>
206 changes: 206 additions & 0 deletions src/modules/peek/Peek.FilePreviewer/Controls/SQLiteControl.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;

using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using Peek.Common.Helpers;
using Peek.FilePreviewer.Previewers;
using Peek.FilePreviewer.Previewers.SQLitePreviewer;
using Peek.FilePreviewer.Previewers.SQLitePreviewer.Models;

namespace Peek.FilePreviewer.Controls
{
public sealed partial class SQLiteControl : UserControl
{
public static readonly DependencyProperty TablesProperty = DependencyProperty.Register(
nameof(Tables),
typeof(ObservableCollection<SQLiteTableInfo>),
typeof(SQLitePreviewer),
new PropertyMetadata(null, OnTablesPropertyChanged));

public static readonly DependencyProperty LoadingStateProperty = DependencyProperty.Register(
nameof(LoadingState),
typeof(PreviewState),
typeof(SQLitePreviewer),
new PropertyMetadata(PreviewState.Uninitialized));

public static readonly DependencyProperty TableCountProperty = DependencyProperty.Register(
nameof(TableCount),
typeof(string),
typeof(SQLitePreviewer),
new PropertyMetadata(null));

private double _lastColumnAutoWidth = double.NaN;

public ObservableCollection<SQLiteTableInfo>? Tables
{
get => (ObservableCollection<SQLiteTableInfo>?)GetValue(TablesProperty);
set => SetValue(TablesProperty, value);
}

public PreviewState? LoadingState
{
get => (PreviewState)GetValue(LoadingStateProperty);
set => SetValue(LoadingStateProperty, value);
}

public string? TableCount
{
get => (string?)GetValue(TableCountProperty);
set => SetValue(TableCountProperty, value);
}

public SQLiteControl()
{
this.InitializeComponent();
}

private static void OnTablesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (SQLiteControl)d;

if (e.OldValue is ObservableCollection<SQLiteTableInfo> oldCollection)
{
oldCollection.CollectionChanged -= control.OnTablesCollectionChanged;
}

control.TableTreeView.RootNodes.Clear();
control.ClearDataView();

if (e.NewValue is ObservableCollection<SQLiteTableInfo> newCollection)
{
newCollection.CollectionChanged += control.OnTablesCollectionChanged;
foreach (var table in newCollection)
{
control.AddTableNode(table);
}
}
}

private void OnTablesCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
{
foreach (SQLiteTableInfo table in e.NewItems)
{
AddTableNode(table);
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
TableTreeView.RootNodes.Clear();
ClearDataView();
}
}

private void AddTableNode(SQLiteTableInfo table)
{
var tableNode = new TreeViewNode { Content = table };
foreach (var col in table.Columns)
{
tableNode.Children.Add(new TreeViewNode { Content = col.DisplayText });
}

TableTreeView.RootNodes.Add(tableNode);
}

private void TableTreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
{
if (args.InvokedItem is TreeViewNode { Content: SQLiteTableInfo table })
{
ShowTableData(table);
}
}

private void ShowTableData(SQLiteTableInfo table)
{
_lastColumnAutoWidth = double.NaN;
TableDataGrid.Columns.Clear();
foreach (var col in table.Columns)
{
TableDataGrid.Columns.Add(new DataGridTextColumn
{
Header = col.Name,
Binding = new Binding { Path = new PropertyPath($"[{col.Name}]") },
IsReadOnly = true,
});
}

TableDataGrid.ItemsSource = table.Rows;

// After columns and rows are set, defer measurement until layout has completed
// so ActualWidth values are valid when we decide whether to stretch the last column.
TableDataGrid.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, AdjustLastColumnWidth);

RecordCountText.Text = string.Format(
CultureInfo.CurrentCulture,
ResourceLoaderInstance.ResourceLoader.GetString("SQLite_Row_Count"),
table.RowCount);

RecordCountHeader.Visibility = Visibility.Visible;
TableDataGrid.Visibility = Visibility.Visible;
NoSelectionText.Visibility = Visibility.Collapsed;
}

private void TableDataGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
AdjustLastColumnWidth();
}

private void AdjustLastColumnWidth()
{
if (TableDataGrid.Columns.Count == 0 || TableDataGrid.ActualWidth <= 0)
{
return;
}

var lastCol = TableDataGrid.Columns[TableDataGrid.Columns.Count - 1];

// Capture the last column's natural auto-width the first time it is measured.
// Once the column is Star-stretched we keep using the stored value so that
// window resizes can correctly revert to Auto when the grid becomes too narrow.
if (!lastCol.Width.IsStar && lastCol.ActualWidth > 0)
{
_lastColumnAutoWidth = lastCol.ActualWidth;
}

if (double.IsNaN(_lastColumnAutoWidth) || _lastColumnAutoWidth <= 0)
{
return;
}

double otherColumnsWidth = 0;
for (int i = 0; i < TableDataGrid.Columns.Count - 1; i++)
{
otherColumnsWidth += TableDataGrid.Columns[i].ActualWidth;
}

if (otherColumnsWidth + _lastColumnAutoWidth < TableDataGrid.ActualWidth)
{
lastCol.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
else
{
lastCol.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}

private void ClearDataView()
{
_lastColumnAutoWidth = double.NaN;
TableDataGrid.Columns.Clear();
TableDataGrid.ItemsSource = null;
RecordCountHeader.Visibility = Visibility.Collapsed;
TableDataGrid.Visibility = Visibility.Collapsed;
NoSelectionText.Visibility = Visibility.Visible;
}
}
}
7 changes: 7 additions & 0 deletions src/modules/peek/Peek.FilePreviewer/FilePreview.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@
Source="{x:Bind ArchivePreviewer.Tree, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(ArchivePreviewer, Previewer.State), Mode=OneWay}" />

<controls:SQLiteControl
x:Name="SQLitePreview"
LoadingState="{x:Bind SQLitePreviewer.State, Mode=OneWay}"
Tables="{x:Bind SQLitePreviewer.Tables, Mode=OneWay}"
TableCount="{x:Bind SQLitePreviewer.TableCountText, Mode=OneWay}"
Visibility="{x:Bind IsPreviewVisible(SQLitePreviewer, Previewer.State), Mode=OneWay}" />

<controls:DriveControl
x:Name="DrivePreview"
Source="{x:Bind DrivePreviewer.Preview, Mode=OneWay}"
Expand Down
5 changes: 5 additions & 0 deletions src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
[NotifyPropertyChangedFor(nameof(AudioPreviewer))]
[NotifyPropertyChangedFor(nameof(BrowserPreviewer))]
[NotifyPropertyChangedFor(nameof(ArchivePreviewer))]
[NotifyPropertyChangedFor(nameof(SQLitePreviewer))]
[NotifyPropertyChangedFor(nameof(ShellPreviewHandlerPreviewer))]
[NotifyPropertyChangedFor(nameof(DrivePreviewer))]
[NotifyPropertyChangedFor(nameof(SpecialFolderPreviewer))]
Expand Down Expand Up @@ -111,6 +112,8 @@

public IArchivePreviewer? ArchivePreviewer => Previewer as IArchivePreviewer;

public ISQLitePreviewer? SQLitePreviewer => Previewer as ISQLitePreviewer;

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

ISQ is not a recognized word. (unrecognized-spelling)

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

ISQ is not a recognized word. (unrecognized-spelling)

public IShellPreviewHandlerPreviewer? ShellPreviewHandlerPreviewer => Previewer as IShellPreviewHandlerPreviewer;

public IDrivePreviewer? DrivePreviewer => Previewer as IDrivePreviewer;
Expand Down Expand Up @@ -198,6 +201,7 @@
AudioPreview.Visibility = Visibility.Collapsed;
BrowserPreview.Visibility = Visibility.Collapsed;
ArchivePreview.Visibility = Visibility.Collapsed;
SQLitePreview.Visibility = Visibility.Collapsed;
DrivePreview.Visibility = Visibility.Collapsed;
UnsupportedFilePreview.Visibility = Visibility.Collapsed;

Expand Down Expand Up @@ -265,6 +269,7 @@
ImagePreview.Source = null;
ArchivePreview.Source = null;
BrowserPreview.Source = null;
SQLitePreview.Tables = null;
DrivePreview.Source = null;

ShellPreviewHandlerPreviewer?.Clear();
Expand Down
6 changes: 6 additions & 0 deletions src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<None Remove="Controls\BrowserControl.xaml" />
<None Remove="Controls\DriveControl.xaml" />
<None Remove="Controls\ShellPreviewHandlerControl.xaml" />
<None Remove="Controls\SQLiteControl.xaml" />
<None Remove="Controls\SpecialFolderPreview\SpecialFolderInformationalPreviewControl.xaml" />
<None Remove="Controls\SpecialFolderPreview\SpecialFolderPreview.xaml" />
<None Remove="Controls\UnsupportedFilePreview\FailedFallbackPreviewControl.xaml" />
Expand All @@ -30,6 +31,8 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" />
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="SharpCompress" />
Expand Down Expand Up @@ -92,6 +95,9 @@
<Page Update="Controls\ArchiveControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Controls\SQLiteControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Controls\SpecialFolderPreview\SpecialFolderInformationalPreviewControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
Expand Down
Loading