Skip to content

Commit

Permalink
Dynamic settings available in profile based on caps
Browse files Browse the repository at this point in the history
  • Loading branch information
cyanfish committed Aug 9, 2024
1 parent f569244 commit 629ab67
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 39 deletions.
90 changes: 62 additions & 28 deletions NAPS2.Lib/EtoForms/Ui/EditProfileForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public class EditProfileForm : EtoDialogBase
private readonly DeviceSelectorWidget _deviceSelectorWidget;
private readonly RadioButton _predefinedSettings;
private readonly RadioButton _nativeUi;
private readonly DropDown _paperSource = C.EnumDropDown<ScanSource>();
private readonly LayoutVisibility _nativeUiVis = new(true);
private readonly EnumDropDownWidget<ScanSource> _paperSource = new();
private readonly DropDown _pageSize = C.EnumDropDown<ScanPageSize>();
private readonly DropDown _resolution = C.EnumDropDown<ScanDpi>(
dpi => string.Format(SettingsResources.DpiFormat, dpi.ToIntDpi().ToString(CultureInfo.CurrentCulture)));
private readonly DropDown _bitDepth = C.EnumDropDown<ScanBitDepth>();
private readonly DropDown _horAlign = C.EnumDropDown<ScanHorizontalAlign>();
private readonly DropDown _scale = C.EnumDropDown<ScanScale>();
private readonly DropDownWidget<int> _resolution = new();
private readonly EnumDropDownWidget<ScanBitDepth> _bitDepth = new();
private readonly EnumDropDownWidget<ScanHorizontalAlign> _horAlign = new();
private readonly EnumDropDownWidget<ScanScale> _scale = new();
private readonly CheckBox _enableAutoSave = new() { Text = UiStrings.EnableAutoSave };
private readonly LinkButton _autoSaveSettings = new() { Text = UiStrings.AutoSaveSettings };
private readonly Button _advanced = new() { Text = UiStrings.Advanced };
Expand Down Expand Up @@ -54,6 +54,8 @@ public EditProfileForm(Naps2Config config, IScanPerformer scanPerformer, ErrorOu

_predefinedSettings = new RadioButton { Text = UiStrings.UsePredefinedSettings };
_nativeUi = new RadioButton(_predefinedSettings) { Text = UiStrings.UseNativeUi };
_resolution.Format = x => string.Format(SettingsResources.DpiFormat, x.ToString(CultureInfo.InvariantCulture));
_paperSource.SelectedItemChanged += PaperSource_SelectedItemChanged;
_pageSize.SelectedIndexChanged += PageSize_SelectedIndexChanged;
_predefinedSettings.CheckedChanged += PredefinedSettings_CheckedChanged;
_nativeUi.CheckedChanged += NativeUi_CheckedChanged;
Expand Down Expand Up @@ -100,7 +102,7 @@ protected override void BuildLayout()
? L.Row(
_predefinedSettings,
_nativeUi
)
).Visible(_nativeUiVis)
: C.None(),
C.Spacer(),
L.Row(
Expand Down Expand Up @@ -152,6 +154,26 @@ public ScanProfile ScanProfile

private void UpdateUiForCaps()
{
_suppressChangeEvent = true;

_paperSource.Items = ScanProfile.Caps?.PaperSources is [_, ..] paperSources
? paperSources
: EnumDropDownWidget<ScanSource>.DefaultItems;

var selectedSource = _paperSource.SelectedItem;

var validResolutions = selectedSource switch
{
ScanSource.Glass => ScanProfile.Caps?.GlassResolutions,
ScanSource.Feeder => ScanProfile.Caps?.FeederResolutions,
ScanSource.Duplex => ScanProfile.Caps?.DuplexResolutions,
_ => null
};
_resolution.Items = validResolutions is [_, ..]
? validResolutions
: EnumDropDownWidget<ScanDpi>.DefaultItems.Select(x => x.ToIntDpi());

_suppressChangeEvent = false;
}

private void UpdateCaps()
Expand All @@ -167,6 +189,7 @@ private void UpdateCaps()
}
else
{
ScanProfile.Caps = null;
if (updatedProfile.Device != null)
{
Task.Run(async () =>
Expand All @@ -191,8 +214,22 @@ private void UpdateCaps()

private ScanProfileCaps MapCaps(ScanCaps? caps)
{
List<ScanSource>? paperSources = null;
if (caps?.PaperSourceCaps is { } paperSourceCaps)
{
paperSources = new List<ScanSource>();
if (paperSourceCaps.SupportsFlatbed) paperSources.Add(ScanSource.Glass);
if (paperSourceCaps.SupportsFeeder) paperSources.Add(ScanSource.Feeder);
if (paperSourceCaps.SupportsDuplex) paperSources.Add(ScanSource.Duplex);
}

return new ScanProfileCaps
{
PaperSources = paperSources,
FeederCheck = caps?.PaperSourceCaps?.CanCheckIfFeederHasPaper,
GlassResolutions = caps?.FlatbedCaps?.DpiCaps?.CommonValues?.ToList(),
FeederResolutions = caps?.FeederCaps?.DpiCaps?.CommonValues?.ToList(),
DuplexResolutions = caps?.DuplexCaps?.DpiCaps?.CommonValues?.ToList()
};
}

Expand Down Expand Up @@ -228,25 +265,15 @@ protected override void OnLoad(EventArgs e)
}
_isDefault = ScanProfile.IsDefault;

// TODO: Allow selecting custom DPI values
int resolutionIndex = 0;
foreach (ScanDpi scanDpi in Enum.GetValues(typeof(ScanDpi)))
{
if (ScanProfile.Resolution.Dpi > scanDpi.ToIntDpi())
{
resolutionIndex++;
}
}

_paperSource.SelectedKey = ScanProfile.PaperSource.ToString();
_bitDepth.SelectedKey = ScanProfile.BitDepth.ToString();
_resolution.SelectedIndex = resolutionIndex;
_paperSource.SelectedItem = ScanProfile.PaperSource;
_bitDepth.SelectedItem = ScanProfile.BitDepth;
_resolution.SelectedItem = ScanProfile.Resolution.Dpi;
_contrastSlider.IntValue = ScanProfile.Contrast;
_brightnessSlider.IntValue = ScanProfile.Brightness;
UpdatePageSizeList();
SelectPageSize();
_scale.SelectedKey = ScanProfile.AfterScanScale.ToString();
_horAlign.SelectedKey = ScanProfile.PageAlign.ToString();
_scale.SelectedItem = ScanProfile.AfterScanScale;
_horAlign.SelectedItem = ScanProfile.PageAlign;

_enableAutoSave.Checked = ScanProfile.EnableAutoSave;

Expand Down Expand Up @@ -382,16 +409,16 @@ private ScanProfile GetUpdatedScanProfile()
MaxQuality = ScanProfile.MaxQuality,
UseNativeUI = _nativeUi.Checked,

AfterScanScale = EnumHelper.ParseOrDefault<ScanScale>(_scale.SelectedKey),
BitDepth = EnumHelper.ParseOrDefault<ScanBitDepth>(_bitDepth.SelectedKey),
AfterScanScale = _scale.SelectedItem,
BitDepth = _bitDepth.SelectedItem,
Brightness = _brightnessSlider.IntValue,
Contrast = _contrastSlider.IntValue,
PageAlign = EnumHelper.ParseOrDefault<ScanHorizontalAlign>(_horAlign.SelectedKey),
PageAlign = _horAlign.SelectedItem,
PageSize = pageSize.Type,
CustomPageSizeName = pageSize.CustomName,
CustomPageSize = pageSize.CustomDimens,
Resolution = new ScanResolution { Dpi = ((ScanDpi) _resolution.SelectedIndex).ToIntDpi() },
PaperSource = EnumHelper.ParseOrDefault<ScanSource>(_paperSource.SelectedKey),
Resolution = new ScanResolution { Dpi = _resolution.SelectedItem },
PaperSource = _paperSource.SelectedItem,

EnableAutoSave = _enableAutoSave.IsChecked(),
AutoSaveSettings = ScanProfile.AutoSaveSettings,
Expand Down Expand Up @@ -439,6 +466,7 @@ private void UpdateEnabledControls()
_displayName.Enabled = !locked;
_deviceSelectorWidget.Enabled = !deviceLocked;
_predefinedSettings.Enabled = _nativeUi.Enabled = !locked;
_nativeUiVis.IsVisible = _deviceSelectorWidget.Choice.Device == null || canUseNativeUi;

_paperSource.Enabled = settingsEnabled;
_resolution.Enabled = settingsEnabled;
Expand All @@ -462,6 +490,12 @@ private void UpdateEnabledControls()
private int _lastPageSizeIndex = -1;
private PageSizeListItem? _lastPageSizeItem;

private void PaperSource_SelectedItemChanged(object? sender, EventArgs e)
{
if (_suppressChangeEvent) return;
UpdateUiForCaps();
}

private void PageSize_SelectedIndexChanged(object? sender, EventArgs e)
{
if (_suppressPageSizeEvent) return;
Expand Down Expand Up @@ -509,7 +543,7 @@ private void Advanced_Click(object? sender, EventArgs e)
{
var form = FormFactory.Create<AdvancedProfileForm>();
ScanProfile.DriverName = DeviceDriver.ToString().ToLowerInvariant();
ScanProfile.BitDepth = (ScanBitDepth) _bitDepth.SelectedIndex;
ScanProfile.BitDepth = _bitDepth.SelectedItem;
form.ScanProfile = ScanProfile;
form.ShowModal();
}
Expand Down
131 changes: 131 additions & 0 deletions NAPS2.Lib/EtoForms/Widgets/DropDownWidget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using Eto.Forms;
using NAPS2.EtoForms.Layout;

namespace NAPS2.EtoForms.Widgets;

public class DropDownWidget<T> where T : notnull
{
private readonly DropDown _dropDown = new();
private T[] _items = [];

private bool _hasUserPreferredItem;
private T? _userPreferredItem;
private bool _changingItems;

public DropDownWidget()
{
EtoPlatform.Current.ConfigureDropDown(_dropDown);
_dropDown.SelectedIndexChanged += DropDown_SelectedIndexChanged;
if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
{
GetClosestItem = GetClosestItemByComparing;
}
}

public Func<T, string> Format { get; set; } = x => x?.ToString() ?? "";

public Func<T[], T, T>? GetClosestItem { get; set; }

public bool Enabled
{
get => _dropDown.Enabled;
set => _dropDown.Enabled = value;
}

public event EventHandler? SelectedItemChanged;

public IEnumerable<T> Items
{
get => _items;
set
{
_items = value.ToArray();
_changingItems = true;
var previousSelection = InternalSelectedItem;
_dropDown.Items.Clear();
foreach (var item in _items)
{
_dropDown.Items.Add(new ListItem
{
Text = Format(item),
Tag = item
});
}
InternalSelectedItem = GetPreferredSelection(previousSelection);
_changingItems = false;
if (!Equals(InternalSelectedItem, previousSelection))
{
SelectedItemChanged?.Invoke(this, EventArgs.Empty);
}
}
}

private T? GetPreferredSelection(T? previousSelection)
{
if (_hasUserPreferredItem && _items.Contains(_userPreferredItem))
{
return _userPreferredItem;
}
if (_items.Contains(previousSelection))
{
return previousSelection;
}
var compareTo = _hasUserPreferredItem ? _userPreferredItem : previousSelection;
if (_items.Length > 0 && compareTo != null && GetClosestItem != null)
{
return GetClosestItem(_items, compareTo);
}
if (_items.Length > 0)
{
return _items[0];
}
return default;
}

public T? SelectedItem
{
get => InternalSelectedItem;
set
{
_hasUserPreferredItem = true;
_userPreferredItem = value;
InternalSelectedItem = value;
}
}

private T? InternalSelectedItem
{
get
{
if (_dropDown.SelectedIndex == -1) return default;
return (T) ((ListItem) _dropDown.Items[_dropDown.SelectedIndex]).Tag;
}
set => _dropDown.SelectedIndex = Array.IndexOf(_items, value);
}

private void DropDown_SelectedIndexChanged(object? sender, EventArgs eventArgs)
{
if (!_changingItems)
{
_hasUserPreferredItem = true;
_userPreferredItem = InternalSelectedItem;
SelectedItemChanged?.Invoke(this, EventArgs.Empty);
}
}

private T GetClosestItemByComparing(T[] items, T x)
{
foreach (var y in items)
{
if (((IComparable<T>) x).CompareTo(y) <= 0)
{
return y;
}
}
return items[^1];
}

public static implicit operator LayoutElement(DropDownWidget<T> control) => control.AsControl();

public LayoutElement AsControl() => _dropDown;
}
14 changes: 14 additions & 0 deletions NAPS2.Lib/EtoForms/Widgets/EnumDropDownWidget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using NAPS2.Scan;

namespace NAPS2.EtoForms.Widgets;

public class EnumDropDownWidget<T> : DropDownWidget<T> where T : struct, Enum
{
public static IEnumerable<T> DefaultItems => (T[]) Enum.GetValues(typeof(T));

public EnumDropDownWidget()
{
Format = x => x.Description();
Items = DefaultItems;
}
}
5 changes: 3 additions & 2 deletions NAPS2.Lib/Scan/DeviceCapsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public DeviceCapsCache(IScanPerformer scanPerformer, ImageContext imageContext,

public ScanCaps? GetCachedCaps(ScanProfile profile)
{
if (profile.DriverName == null || profile.Device == null) return null;
var key = GetDeviceKey(profile);
lock (_capsCache)
{
Expand All @@ -33,6 +34,7 @@ public DeviceCapsCache(IScanPerformer scanPerformer, ImageContext imageContext,

public async Task<ScanCaps?> QueryCaps(ScanProfile profile)
{
if (profile.DriverName == null || profile.Device == null) return null;
var key = GetDeviceKey(profile);
bool contains;
lock (_capsCache)
Expand Down Expand Up @@ -123,8 +125,7 @@ private async Task<Image> DoLoadIcon(string iconUri)

private DeviceKey GetDeviceKey(ScanProfile profile)
{
if (profile.DriverName == null || profile.Device == null) throw new ArgumentException();
return new DeviceKey(profile.DriverName, profile.Device.ID, profile.WiaVersion, profile.TwainImpl);
return new DeviceKey(profile.DriverName!, profile.Device!.ID, profile.WiaVersion, profile.TwainImpl);
}

private record DeviceKey(string DriverName, string DeviceId, WiaApiVersion WiaVersion, TwainImpl TwainImpl);
Expand Down
Loading

0 comments on commit 629ab67

Please sign in to comment.