Gtk: Initial working list view

This commit is contained in:
Ben Olden-Cooligan 2022-09-28 20:19:57 -07:00
parent 0a07d9dafa
commit c5f47588de
8 changed files with 90 additions and 17 deletions

View File

@ -20,6 +20,7 @@
</PropertyGroup>
<Import Project="..\NAPS2.Setup\CommonTargets.targets" />
<Import Project="..\NAPS2.Setup\NativeLibs.Linux.targets" />
<Import Project="..\NAPS2.Setup\SdkUsers.targets" />
<ItemGroup>

View File

@ -1,4 +1,8 @@
using Eto.Forms;
using Eto.GtkSharp;
using Gtk;
using Orientation = Gtk.Orientation;
using GtkImageView = Gtk.Image;
namespace NAPS2.EtoForms.Gtk;
@ -8,10 +12,27 @@ public class GtkListView<T> : IListView<T> where T : notnull
private ListSelection<T> _selection = ListSelection.Empty<T>();
private bool _refreshing;
private readonly ScrolledWindow _scrolledWindow;
private readonly FlowBox _flowBox;
private readonly Dictionary<T, (Widget widget, int index)> _widgetMap = new();
public GtkListView(ListViewBehavior<T> behavior)
{
_behavior = behavior;
_scrolledWindow = new ScrolledWindow();
_flowBox = new FlowBox
{
Orientation = Orientation.Horizontal,
Valign = Align.Start,
Homogeneous = false,
ActivateOnSingleClick = false,
MaxChildrenPerLine = uint.MaxValue,
Margin = 8,
ColumnSpacing = 16,
RowSpacing = 16
};
_scrolledWindow.Add(_flowBox);
// _flowBox.SelectionMode = SelectionMode.Multiple;
}
public int ImageSize { get; set; }
@ -30,7 +51,9 @@ public class GtkListView<T> : IListView<T> where T : notnull
e.Effects = _behavior.GetDropEffect(e.Data);
}
public Control Control => new Label();
public Control Control => _scrolledWindow.ToEto();
public event EventHandler? Updated;
public event EventHandler? SelectionChanged;
@ -40,24 +63,66 @@ public class GtkListView<T> : IListView<T> where T : notnull
public void SetItems(IEnumerable<T> items)
{
// TODO: Any better way to remove all?
foreach (var widget in _flowBox.Children)
{
_flowBox.Remove(widget);
}
foreach (var item in items)
{
var widget = GetItemWidget(item);
_flowBox.Add(widget);
}
Updated?.Invoke(this, EventArgs.Empty);
}
private Widget GetItemWidget(T item)
{
var flowBoxChild = new FlowBoxChild();
var image = _behavior.GetImage(item, ImageSize).ToGtk();
flowBoxChild.Add(image);
return flowBoxChild;
}
// TODO: Do we need this method? Clean up the name/doc at least
public void RegenerateImages()
{
foreach (var item in _widgetMap.Keys)
{
var (oldWidget, index) = _widgetMap[item];
_flowBox.Remove(oldWidget);
var newWidget = GetItemWidget(item);
_widgetMap[item] = (newWidget, index);
}
Updated?.Invoke(this, EventArgs.Empty);
}
public void ApplyDiffs(ListViewDiffs<T> diffs)
{
foreach (var op in diffs.AppendOperations)
{
var widget = GetItemWidget(op.Item);
var index = _widgetMap.Count;
_flowBox.Add(widget);
_widgetMap[op.Item] = (widget, index);
}
foreach (var op in diffs.ReplaceOperations)
{
var (oldWidget, index) = _widgetMap[op.Item];
_flowBox.Remove(oldWidget);
var newWidget = GetItemWidget(op.Item);
_widgetMap[op.Item] = (newWidget, index);
}
foreach (var op in diffs.TrimOperations)
{
foreach (var item in op.DeletedItems)
{
var (widget, _) = _widgetMap[item];
_flowBox.Remove(widget);
_widgetMap.Remove(item);
}
}
Updated?.Invoke(this, EventArgs.Empty);
}
public ListSelection<T> Selection

View File

@ -1,7 +1,7 @@
using System.Threading;
using Eto.Drawing;
using Eto.Forms;
using Eto.GtkSharp;
using NAPS2.EtoForms.Gtk;
using NAPS2.ImportExport.Images;
using NAPS2.WinForms;
@ -28,23 +28,16 @@ public class GtkDesktopForm : DesktopForm
imageList, imageTransfer, thumbnailController, thumbnailProvider, desktopController, desktopScanController,
imageListActions, desktopFormProvider, desktopSubFormController)
{
// For retina screens
_thumbnailController.Oversample = 2.0;
}
// protected override void SetContent(Control content)
// {
// var scrollView = new NSScrollView();
// scrollView.DocumentView = content.ToNative();
// Content = scrollView.ToEto();
// }
protected override void OnLoad(EventArgs e)
{
// TODO: What's the best place to initialize this? It needs to happen from the UI event loop.
Invoker.Current = new SyncContextInvoker(SynchronizationContext.Current);
base.OnLoad(e);
ClientSize = new Size(1000, 600);
// TODO: This is a bit of a hack as for some reason the view doesn't update unless we do this
((GtkListView<UiImage>)_listView).Updated += (_, _) => Content = _listView.Control;
}
protected override void CreateToolbarsAndMenus()

View File

@ -55,7 +55,8 @@ public class ImageListDiffer
var trimOps = ImmutableList<TrimOperation>.Empty;
if (newState.Count < _currentState.Count)
{
trimOps = trimOps.Add(new TrimOperation(_currentState.Count - newState.Count));
var deletedItems = _currentState.Skip(newState.Count).Select(x => x.Source).ToImmutableList();
trimOps = trimOps.Add(new TrimOperation(_currentState.Count - newState.Count, deletedItems));
}
_currentState = newState;

View File

@ -30,5 +30,5 @@ public record ListViewDiffs<T>(
public record ReplaceOperation(int Index, T Item);
public record TrimOperation(int Count);
public record TrimOperation(int Count, ImmutableList<T> DeletedItems);
}

View File

@ -29,7 +29,7 @@ public class LinuxSystemCompat : ISystemCompat
public string PdfiumLibraryName => "libpdfium.so";
public string SaneLibraryName => "libsane.so";
public string SaneLibraryName => "libsane.so.1";
public IntPtr LoadLibrary(string path) => LinuxInterop.dlopen(path, RTLD_LAZY | RTLD_GLOBAL);

View File

@ -20,9 +20,12 @@ public class NativeLibrary
}
}
}
var expectedPath =
Path.Combine(AssemblyHelper.LibFolder, PlatformCompat.System.LibrarySearchPaths[0], libraryName);
throw new Exception($"Library does not exist: {expectedPath}");
// TODO: Maybe do this for some platforms?
// var expectedPath =
// Path.Combine(AssemblyHelper.LibFolder, PlatformCompat.System.LibrarySearchPaths[0], libraryName);
// throw new Exception($"Library does not exist: {expectedPath}");
// Just the library name so it uses the system search paths
return libraryName;
}
private readonly Dictionary<Type, object> _funcCache = new();

View File

@ -0,0 +1,10 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ContentWithTargetPath Include="..\NAPS2.Setup\lib\linux\libpdfium.so"
Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>lib\linux\libpdfium.so</Link>
<TargetPath>_linux\libpdfium.so</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
</Project>