mirror of
https://github.com/cyanfish/naps2.git
synced 2024-08-16 18:50:45 +03:00
Migrate ErrorForm to Eto and implement dynamic visiblity/form size
This commit is contained in:
parent
9a3915547a
commit
3b02a34d33
@ -147,8 +147,11 @@ public class WinFormsEtoPlatform : EtoPlatform
|
||||
{
|
||||
if (control.ControlObject is wf.Label label)
|
||||
{
|
||||
label.AutoSize = false;
|
||||
label.Width = 0;
|
||||
if (!control.ParentWindow.Loaded)
|
||||
{
|
||||
label.AutoSize = false;
|
||||
label.Width = 0;
|
||||
}
|
||||
return label.GetPreferredSize(new sd.Size(defaultWidth, 0)).ToEto();
|
||||
}
|
||||
return base.GetWrappedSize(control, defaultWidth);
|
||||
|
@ -16,7 +16,7 @@ public abstract class EtoDialogBase : Dialog, IFormBase
|
||||
Resizable = true;
|
||||
ShowInTaskbar = true;
|
||||
LayoutController.Bind(this);
|
||||
LayoutController.ContentSet += (_, _) => FormStateController.UpdateLayoutSize(LayoutController);
|
||||
LayoutController.Invalidated += (_, _) => FormStateController.UpdateLayoutSize(LayoutController);
|
||||
}
|
||||
|
||||
// TODO: PR for Eto to integrate this
|
||||
|
@ -14,7 +14,7 @@ public abstract class EtoFormBase : Form, IFormBase
|
||||
FormStateController = new FormStateController(this, config);
|
||||
Resizable = true;
|
||||
LayoutController.Bind(this);
|
||||
LayoutController.ContentSet += (_, _) => FormStateController.UpdateLayoutSize(LayoutController);
|
||||
LayoutController.Invalidated += (_, _) => FormStateController.UpdateLayoutSize(LayoutController);
|
||||
}
|
||||
|
||||
public FormStateController FormStateController { get; }
|
||||
|
@ -4,6 +4,7 @@ using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms;
|
||||
|
||||
// TODO: Verify all the size handling works correctly with maximized forms.
|
||||
public class FormStateController
|
||||
{
|
||||
private readonly Window _window;
|
||||
@ -46,10 +47,27 @@ public class FormStateController
|
||||
if (AutoLayoutSize)
|
||||
{
|
||||
_minimumClientSize = layoutController.GetLayoutSize(false);
|
||||
var oldDefaultClientSize = DefaultClientSize;
|
||||
var oldMaximumClientSize = _maximumClientSize;
|
||||
DefaultClientSize = layoutController.GetLayoutSize(true) + DefaultExtraLayoutSize;
|
||||
if (FixedHeightLayout)
|
||||
_maximumClientSize = FixedHeightLayout ? new Size(0, _minimumClientSize.Height) : Size.Empty;
|
||||
|
||||
if (_loaded)
|
||||
{
|
||||
_maximumClientSize = new Size(0, _minimumClientSize.Height);
|
||||
// Dynamic re-sizing because the layout contents have changed (not just a normal resize/maximize etc).
|
||||
var size = EtoPlatform.Current.GetClientSize(_window);
|
||||
if (oldMaximumClientSize.Height > 0)
|
||||
{
|
||||
size.Height = Math.Min(size.Height, oldMaximumClientSize.Height);
|
||||
}
|
||||
size += DefaultClientSize - oldDefaultClientSize;
|
||||
size = Size.Max(size, _minimumClientSize);
|
||||
if (_maximumClientSize.Height > 0)
|
||||
{
|
||||
size.Height = Math.Min(size.Height, _maximumClientSize.Height);
|
||||
}
|
||||
EtoPlatform.Current.SetMinimumClientSize(_window, _minimumClientSize);
|
||||
EtoPlatform.Current.SetClientSize(_window, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public class ControlWithLayoutAttributes : LayoutElement
|
||||
Padding? padding = null, int? spacingAfter = null,
|
||||
int? width = null, int? minWidth = null, int? maxWidth = null, int? naturalWidth = null,
|
||||
int? height = null, int? minHeight = null, int? maxHeight = null, int? naturalHeight = null,
|
||||
int? wrapDefaultWidth = null, LayoutAlignment? alignment = null)
|
||||
int? wrapDefaultWidth = null, LayoutAlignment? alignment = null, LayoutVisibility? visibility = null)
|
||||
{
|
||||
Control = control.Control;
|
||||
XScale = xScale ?? control.XScale;
|
||||
@ -43,6 +43,7 @@ public class ControlWithLayoutAttributes : LayoutElement
|
||||
NaturalHeight = naturalHeight ?? control.NaturalHeight;
|
||||
WrapDefaultWidth = wrapDefaultWidth ?? control.WrapDefaultWidth;
|
||||
Alignment = alignment ?? control.Alignment;
|
||||
Visibility = visibility ?? control.Visibility;
|
||||
}
|
||||
|
||||
public static implicit operator ControlWithLayoutAttributes(Control control) =>
|
||||
@ -87,6 +88,11 @@ public class ControlWithLayoutAttributes : LayoutElement
|
||||
public override SizeF GetPreferredSize(LayoutContext context, RectangleF parentBounds)
|
||||
{
|
||||
var size = SizeF.Empty;
|
||||
if (!IsVisible)
|
||||
{
|
||||
EnsureIsAdded(context);
|
||||
return size;
|
||||
}
|
||||
if (Control != null)
|
||||
{
|
||||
EnsureIsAdded(context);
|
||||
@ -143,10 +149,15 @@ public class ControlWithLayoutAttributes : LayoutElement
|
||||
private void EnsureIsAdded(LayoutContext context)
|
||||
{
|
||||
if (Control == null) return;
|
||||
Control.Visible = IsVisible;
|
||||
if (context.IsFirstLayout && !_isAdded)
|
||||
{
|
||||
EtoPlatform.Current.AddToContainer(context.Layout, Control, context.InOverlay);
|
||||
_isAdded = true;
|
||||
if (Visibility != null)
|
||||
{
|
||||
Visibility.IsVisibleChanged += (_, _) => context.Invalidate();
|
||||
}
|
||||
}
|
||||
if (context.IsFirstLayout && !_isWindowSet && context.Window != null)
|
||||
{
|
||||
|
@ -71,6 +71,8 @@ public static class EtoLayoutExtensions
|
||||
new ControlWithLayoutAttributes(control, padding: new Padding(all));
|
||||
public static ControlWithLayoutAttributes Padding(this Control control, int left = 0, int top = 0, int right = 0, int bottom = 0) =>
|
||||
new ControlWithLayoutAttributes(control, padding: new Padding(left, top, right, bottom));
|
||||
public static ControlWithLayoutAttributes Visible(this Control control, LayoutVisibility visibility) =>
|
||||
new ControlWithLayoutAttributes(control, visibility: visibility);
|
||||
|
||||
public static ControlWithLayoutAttributes Wrap(this Label label, int defaultWidth)
|
||||
{
|
||||
@ -90,6 +92,12 @@ public static class EtoLayoutExtensions
|
||||
new ControlWithLayoutAttributes(control, minHeight: minHeight);
|
||||
public static ControlWithLayoutAttributes MaxHeight(this ControlWithLayoutAttributes control, int maxHeight) =>
|
||||
new ControlWithLayoutAttributes(control, maxHeight: maxHeight);
|
||||
public static ControlWithLayoutAttributes NaturalSize(this ControlWithLayoutAttributes control, int width, int height) =>
|
||||
new ControlWithLayoutAttributes(control, naturalWidth: width, naturalHeight: height);
|
||||
public static ControlWithLayoutAttributes NaturalWidth(this ControlWithLayoutAttributes control, int width) =>
|
||||
new ControlWithLayoutAttributes(control, naturalWidth: width);
|
||||
public static ControlWithLayoutAttributes NaturalHeight(this ControlWithLayoutAttributes control, int height) =>
|
||||
new ControlWithLayoutAttributes(control, naturalHeight: height);
|
||||
public static ControlWithLayoutAttributes XScale(this ControlWithLayoutAttributes control) =>
|
||||
new ControlWithLayoutAttributes(control, xScale: true);
|
||||
public static ControlWithLayoutAttributes YScale(this ControlWithLayoutAttributes control) =>
|
||||
@ -110,6 +118,8 @@ public static class EtoLayoutExtensions
|
||||
new ControlWithLayoutAttributes(control, padding: new Padding(all));
|
||||
public static ControlWithLayoutAttributes Padding(this ControlWithLayoutAttributes control, int left = 0, int top = 0, int right = 0, int bottom = 0) =>
|
||||
new ControlWithLayoutAttributes(control, padding: new Padding(left, top, right, bottom));
|
||||
public static ControlWithLayoutAttributes Visible(this ControlWithLayoutAttributes control, LayoutVisibility visibility) =>
|
||||
new ControlWithLayoutAttributes(control, visibility: visibility);
|
||||
|
||||
public static LayoutColumn Padding(this LayoutColumn column, Padding padding) =>
|
||||
new LayoutColumn(column, padding: padding);
|
||||
|
@ -44,13 +44,13 @@ public class LayoutColumn : LayoutLine<LayoutRow>
|
||||
|
||||
protected internal override bool DoesChildScale(LayoutElement child) => child.YScale;
|
||||
|
||||
protected override int GetSpacing(int i, LayoutContext context)
|
||||
protected override int GetSpacingCore(int i, LayoutContext context)
|
||||
{
|
||||
if (i < Children.Length - 1 && Children[i] is ControlWithLayoutAttributes { Control: Label })
|
||||
{
|
||||
return Children[i].SpacingAfter ?? LabelSpacing ?? context.DefaultLabelSpacing;
|
||||
}
|
||||
return base.GetSpacing(i, context);
|
||||
return base.GetSpacingCore(i, context);
|
||||
}
|
||||
|
||||
protected override float GetBreadth(SizeF size) => size.Width;
|
||||
|
@ -21,4 +21,6 @@ public record LayoutContext(Control Layout)
|
||||
public Window? Window { get; init; }
|
||||
|
||||
public bool InOverlay { get; init; }
|
||||
|
||||
public Action Invalidate { get; init; }
|
||||
}
|
@ -11,6 +11,7 @@ public class LayoutController
|
||||
private Window? _window;
|
||||
private bool _firstLayout = true;
|
||||
private bool _isShown;
|
||||
private bool _layoutQueued;
|
||||
|
||||
public LayoutElement? Content
|
||||
{
|
||||
@ -22,7 +23,7 @@ public class LayoutController
|
||||
DoLayout();
|
||||
if (setting)
|
||||
{
|
||||
ContentSet?.Invoke(this, EventArgs.Empty);
|
||||
Invalidated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,9 +56,17 @@ public class LayoutController
|
||||
return naturalSize;
|
||||
}
|
||||
|
||||
public void DoLayout()
|
||||
public void Invalidate()
|
||||
{
|
||||
if (_window == null || _content == null || !_isShown) return;
|
||||
_layoutQueued = true;
|
||||
Invalidated?.Invoke(this, EventArgs.Empty);
|
||||
_layoutQueued = false;
|
||||
DoLayout();
|
||||
}
|
||||
|
||||
private void DoLayout()
|
||||
{
|
||||
if (_window == null || _content == null || !_isShown || _layoutQueued) return;
|
||||
// TODO: Handle added/removed things
|
||||
var size = EtoPlatform.Current.GetClientSize(_window);
|
||||
int p = RootPadding;
|
||||
@ -82,9 +91,10 @@ public class LayoutController
|
||||
{
|
||||
DefaultSpacing = DefaultSpacing,
|
||||
DefaultLabelSpacing = DefaultLabelSpacing,
|
||||
IsFirstLayout = _firstLayout
|
||||
IsFirstLayout = _firstLayout,
|
||||
Invalidate = Invalidate
|
||||
};
|
||||
}
|
||||
|
||||
public event EventHandler? ContentSet;
|
||||
public event EventHandler? Invalidated;
|
||||
}
|
@ -16,6 +16,8 @@ public abstract class LayoutElement
|
||||
protected internal bool XScale { get; set; }
|
||||
protected internal bool YScale { get; set; }
|
||||
protected internal LayoutAlignment Alignment { get; set; }
|
||||
protected internal LayoutVisibility? Visibility { get; set; }
|
||||
protected internal bool IsVisible => Visibility?.IsVisible ?? true;
|
||||
protected internal int? SpacingAfter { get; set; }
|
||||
|
||||
public static implicit operator LayoutElement(Control control) =>
|
||||
|
@ -55,9 +55,18 @@ public abstract class LayoutLine<TOrthogonal> : LayoutContainer
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual int GetSpacing(int i, LayoutContext context)
|
||||
private int GetSpacing(int i, LayoutContext context)
|
||||
{
|
||||
if (Children.Skip(i + 1).All(child => !child.IsVisible))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (!Children[i].IsVisible) return 0;
|
||||
return GetSpacingCore(i, context);
|
||||
}
|
||||
|
||||
protected virtual int GetSpacingCore(int i, LayoutContext context)
|
||||
{
|
||||
if (i == Children.Length - 1) return 0;
|
||||
return Children[i].SpacingAfter ?? Spacing ?? context.DefaultSpacing;
|
||||
}
|
||||
|
||||
@ -111,19 +120,20 @@ public abstract class LayoutLine<TOrthogonal> : LayoutContainer
|
||||
{
|
||||
// If this line is supposed to be aligned with adjacent lines (e.g. 2 rows in a parent column or vice versa),
|
||||
// then our parent will have pre-calculated our cell sizes and scaling for us.
|
||||
cellLengths = Aligned ? context.CellLengths : null;
|
||||
cellScaling = Aligned ? context.CellScaling : null;
|
||||
if (Aligned && context.CellLengths != null && context.CellScaling != null)
|
||||
{
|
||||
cellLengths = context.CellLengths;
|
||||
cellScaling = context.CellScaling;
|
||||
return;
|
||||
}
|
||||
// If we aren't aligned or we don't have a parent to do that pre-calculation, then we just determine our cell
|
||||
// sizes and scaling directly without any special alignment constraints.
|
||||
if (cellLengths == null || cellScaling == null)
|
||||
cellLengths = new List<float>();
|
||||
cellScaling = new List<bool>();
|
||||
foreach (var child in Children)
|
||||
{
|
||||
cellLengths = new List<float>();
|
||||
cellScaling = new List<bool>();
|
||||
foreach (var child in Children)
|
||||
{
|
||||
cellLengths.Add(GetLength(child.GetPreferredSize(childContext, bounds)));
|
||||
cellScaling.Add(DoesChildScale(child));
|
||||
}
|
||||
cellLengths.Add(GetLength(child.GetPreferredSize(childContext, bounds)));
|
||||
cellScaling.Add(DoesChildScale(child));
|
||||
}
|
||||
}
|
||||
|
||||
|
22
NAPS2.Lib/EtoForms/Layout/LayoutVisibility.cs
Normal file
22
NAPS2.Lib/EtoForms/Layout/LayoutVisibility.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace NAPS2.EtoForms.Layout;
|
||||
|
||||
public class LayoutVisibility
|
||||
{
|
||||
private bool _isVisible;
|
||||
|
||||
public LayoutVisibility(bool isVisible)
|
||||
{
|
||||
_isVisible = isVisible;
|
||||
}
|
||||
|
||||
public bool IsVisible
|
||||
{
|
||||
get => _isVisible; set
|
||||
{
|
||||
_isVisible = value;
|
||||
IsVisibleChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler? IsVisibleChanged;
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms.Ui;
|
||||
|
||||
namespace NAPS2.EtoForms;
|
||||
|
||||
public class MessageBoxErrorOutput : ErrorOutput
|
||||
{
|
||||
private readonly DialogHelper _dialogHelper;
|
||||
private readonly IFormFactory _formFactory;
|
||||
|
||||
public MessageBoxErrorOutput(DialogHelper dialogHelper)
|
||||
public MessageBoxErrorOutput(IFormFactory formFactory)
|
||||
{
|
||||
_dialogHelper = dialogHelper;
|
||||
_formFactory = formFactory;
|
||||
}
|
||||
|
||||
public override void DisplayError(string errorMessage)
|
||||
@ -25,15 +26,12 @@ public class MessageBoxErrorOutput : ErrorOutput
|
||||
{
|
||||
Invoker.Current.SafeInvoke(() => ShowErrorWithDetails(errorMessage, exception.ToString()));
|
||||
}
|
||||
|
||||
private void ShowErrorWithDetails(string errorMessage, string details)
|
||||
{
|
||||
// TODO: Migrate error form
|
||||
MessageBox.Show(errorMessage, MessageBoxType.Error);
|
||||
// var form = new FError
|
||||
// {
|
||||
// ErrorMessage = errorMessage,
|
||||
// Details = details
|
||||
// };
|
||||
// form.ShowDialog();
|
||||
var form = _formFactory.Create<ErrorForm>();
|
||||
form.ErrorMessage = errorMessage;
|
||||
form.Details = details;
|
||||
form.ShowModal();
|
||||
}
|
||||
}
|
@ -91,7 +91,7 @@ public class EmailSettingsForm : EtoDialogBase
|
||||
_provider.Text = SettingsResources.EmailProvider_NotSelected;
|
||||
break;
|
||||
}
|
||||
LayoutController.DoLayout();
|
||||
LayoutController.Invalidate();
|
||||
}
|
||||
|
||||
private void Save()
|
||||
|
52
NAPS2.Lib/EtoForms/Ui/ErrorForm.cs
Normal file
52
NAPS2.Lib/EtoForms/Ui/ErrorForm.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Ui;
|
||||
|
||||
public class ErrorForm : EtoDialogBase
|
||||
{
|
||||
private readonly Label _message = new();
|
||||
private readonly TextArea _details = new() { ReadOnly = true };
|
||||
private readonly LayoutVisibility _detailsVisibility = new(false);
|
||||
|
||||
public ErrorForm(Naps2Config config)
|
||||
: base(config)
|
||||
{
|
||||
Title = UiStrings.ErrorFormTitle;
|
||||
FormStateController.RestoreFormState = false;
|
||||
|
||||
var image = new ImageView { Image = Icons.exclamation.ToEtoImage() };
|
||||
|
||||
FormStateController.FixedHeightLayout = true;
|
||||
LayoutController.Content = L.Column(
|
||||
L.Row(
|
||||
image.AlignCenter().Padding(right: 5),
|
||||
_message.Wrap(350).NaturalWidth(350).AlignCenter().XScale()
|
||||
),
|
||||
L.Row(
|
||||
C.Link(UiStrings.TechnicalDetails, ToggleDetails).AlignCenter(),
|
||||
C.Filler(),
|
||||
C.OkButton(this)
|
||||
),
|
||||
_details.NaturalHeight(120).Visible(_detailsVisibility).YScale()
|
||||
);
|
||||
}
|
||||
|
||||
private void ToggleDetails()
|
||||
{
|
||||
FormStateController.FixedHeightLayout = _detailsVisibility.IsVisible;
|
||||
_detailsVisibility.IsVisible = !_detailsVisibility.IsVisible;
|
||||
}
|
||||
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _message.Text;
|
||||
set => _message.Text = value;
|
||||
}
|
||||
|
||||
public string Details
|
||||
{
|
||||
get => _details.Text;
|
||||
set => _details.Text = value;
|
||||
}
|
||||
}
|
10
NAPS2.Lib/Icons.Designer.cs
generated
10
NAPS2.Lib/Icons.Designer.cs
generated
@ -459,6 +459,16 @@ namespace NAPS2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] exclamation {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("exclamation", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
|
@ -370,4 +370,7 @@
|
||||
<data name="favicon" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Icons\favicon.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="exclamation" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Icons\exclamation.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
BIN
NAPS2.Lib/Icons/exclamation.png
Normal file
BIN
NAPS2.Lib/Icons/exclamation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
18
NAPS2.Lib/Lang/Resources/UiStrings.Designer.cs
generated
18
NAPS2.Lib/Lang/Resources/UiStrings.Designer.cs
generated
@ -671,6 +671,15 @@ namespace NAPS2.Lang.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error.
|
||||
/// </summary>
|
||||
internal static string ErrorFormTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorFormTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Estimated download size: {0} MB.
|
||||
/// </summary>
|
||||
@ -1481,6 +1490,15 @@ namespace NAPS2.Lang.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Technical Details.
|
||||
/// </summary>
|
||||
internal static string TechnicalDetails {
|
||||
get {
|
||||
return ResourceManager.GetString("TechnicalDetails", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Tiff Options.
|
||||
/// </summary>
|
||||
|
@ -636,4 +636,10 @@
|
||||
<data name="WaitingForAuthorization" xml:space="preserve">
|
||||
<value>Waiting for authorization...</value>
|
||||
</data>
|
||||
<data name="ErrorFormTitle" xml:space="preserve">
|
||||
<value>Error</value>
|
||||
</data>
|
||||
<data name="TechnicalDetails" xml:space="preserve">
|
||||
<value>Technical Details</value>
|
||||
</data>
|
||||
</root>
|
Loading…
Reference in New Issue
Block a user