mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Gtk: Initial working list view
This commit is contained in:
parent
0a07d9dafa
commit
c5f47588de
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
10
NAPS2.Setup/NativeLibs.Linux.targets
Normal file
10
NAPS2.Setup/NativeLibs.Linux.targets
Normal 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>
|
Loading…
Reference in New Issue
Block a user