/src/Effects/PropertyBasedEffectConfigDialog.cs
C# | 438 lines | 334 code | 84 blank | 20 comment | 28 complexity | 812a87b9da35605a70fa8f5a7ce97b41 MD5 | raw file
- /////////////////////////////////////////////////////////////////////////////////
- // Paint.NET //
- // Copyright (C) Rick Brewster, Tom Jackson, and past contributors. //
- // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
- // See src/Resources/Files/License.txt for full licensing and attribution //
- // details. //
- // . //
- /////////////////////////////////////////////////////////////////////////////////
-
- using System.Linq;
- using PaintDotNet.Base;
- using PaintDotNet.Base.PropertySystem;
- using PaintDotNet.IndirectUI;
- using PaintDotNet.SystemLayer;
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Threading;
- using System.Windows.Forms;
-
- namespace PaintDotNet.Effects
- {
- internal sealed class PropertyBasedEffectConfigDialog
- : EffectConfigDialog<PropertyBasedEffect, PropertyBasedEffectConfigToken>
- {
- private const int DefaultClientWidth96Dpi = 345;
- private const int DefaultClientHeight96Dpi = 125;
-
- private readonly Button _okButton;
- private readonly Button _cancelButton;
- private readonly EtchedLine _etchedLine;
- private readonly ControlInfo _configUI;
- private readonly Panel _configUIPanel;
- private readonly Control _configUIControl;
- private PropertyCollection _properties;
- private readonly PropertyCollection _windowProperties;
-
- internal static PropertyCollection CreateWindowProperties()
- {
- var props = new List<Property>
- {
- new StringProperty(
- ControlInfoPropertyNames.WindowTitle,
- string.Empty,
- Math.Min(1024, StringProperty.MaxMaxLength)),
- new BooleanProperty(ControlInfoPropertyNames.WindowIsSizable, false),
- new DoubleProperty(ControlInfoPropertyNames.WindowWidthScale, 1.0, 1.0, 2.0)
- };
-
- // Title
-
- // Sizability -- defaults to false
-
- // Window width, as a scale of the default width (1.0 is obviously the default)
-
- return new PropertyCollection(props);
- }
-
- protected override PropertyBasedEffectConfigToken CreateInitialToken()
- {
- var token = new PropertyBasedEffectConfigToken(_properties);
- return token;
- }
-
- protected override void InitDialogFromToken(PropertyBasedEffectConfigToken effectTokenCopy)
- {
- // We run this twice so that rules don't execute on stale values
- // See bug #2719
- InitDialogFromTokenImpl(effectTokenCopy);
- InitDialogFromTokenImpl(effectTokenCopy);
- }
-
- private void InitDialogFromTokenImpl(PropertyBasedEffectConfigToken effectTokenCopy)
- {
- foreach (string propertyName in effectTokenCopy.PropertyNames)
- {
- var srcProperty = effectTokenCopy.GetProperty<Property>(propertyName);
- PropertyControlInfo dstPropertyControlInfo = _configUI.FindControlForPropertyName(propertyName);
-
- if (dstPropertyControlInfo == null) continue;
- Property dstProperty = dstPropertyControlInfo.Property;
-
- if (dstProperty.ReadOnly)
- {
- dstProperty.ReadOnly = false;
- dstProperty.Value = srcProperty.Value;
- dstProperty.ReadOnly = true;
- }
- else
- {
- dstProperty.Value = srcProperty.Value;
- }
- }
- }
-
- protected override void LoadIntoTokenFromDialog(PropertyBasedEffectConfigToken writeValuesHere)
- {
- foreach (string propertyName in EffectToken.PropertyNames)
- {
- PropertyControlInfo srcPropertyControlInfo = _configUI.FindControlForPropertyName(propertyName);
-
- if (srcPropertyControlInfo == null) continue;
- Property srcProperty = srcPropertyControlInfo.Property;
- Property dstProperty = writeValuesHere.GetProperty(srcProperty.Name);
-
- if (dstProperty.ReadOnly)
- {
- dstProperty.ReadOnly = false;
- dstProperty.Value = srcProperty.Value;
- dstProperty.ReadOnly = true;
- }
- else
- {
- dstProperty.Value = srcProperty.Value;
- }
- }
- }
-
- protected override void OnShown(EventArgs e)
- {
- if (_configUIControl is IFirstSelection)
- {
- ((IFirstSelection)_configUIControl).FirstSelect();
- }
-
- base.OnShown(e);
- }
-
- protected override void OnLoad(EventArgs e)
- {
- Size idealClientSize = OnLayoutImpl();
- Size idealWindowSize = ClientSizeToWindowSize(idealClientSize);
-
- Rectangle workingArea = Screen.FromControl(this).WorkingArea;
-
- var targetWindowSize = new Size(
- Math.Min(workingArea.Width, idealWindowSize.Width),
- Math.Min(workingArea.Height, idealWindowSize.Height));
-
- Size targetClientSize = WindowSizeToClientSize(targetWindowSize);
-
- if (ClientSize != targetClientSize)
- {
- ClientSize = targetClientSize;
- OnLayoutImpl();
- }
-
- var newMinimumSize = new Size(idealWindowSize.Width, Math.Min(workingArea.Height, Math.Min(idealWindowSize.Height, UI.ScaleHeight(500))));
-
- Size minimumClientSize = WindowSizeToClientSize(newMinimumSize);
-
- MinimumSize = newMinimumSize;
-
- ClientSize = new Size(
- (int)(ClientSize.Width * (double)_windowProperties[ControlInfoPropertyNames.WindowWidthScale].Value),
- Math.Min(ClientSize.Height, (workingArea.Height * 9) / 10)); // max height should ever be 90% of working screen area height
-
- base.OnLoad(e);
- }
-
- protected override void OnLayout(LayoutEventArgs levent)
- {
- Size idealClientSize = OnLayoutImpl();
- base.OnLayout(levent);
- }
-
- private Size OnLayoutImpl()
- {
- int leftMargin = UI.ScaleWidth(8);
- int rightMargin = UI.ScaleHeight(8);
- int insetWidth = ClientSize.Width - leftMargin - rightMargin;
-
- int topMargin = UI.ScaleHeight(8);
- int bottomMargin = UI.ScaleHeight(8);
- int insetHeight = ClientSize.Height - topMargin - bottomMargin;
-
- int hMargin = UI.ScaleWidth(6);
- int vMargin = UI.ScaleHeight(6);
-
- int minButtonWidth = UI.ScaleWidth(80);
-
- _cancelButton.Width = minButtonWidth;
- _cancelButton.PerformLayout();
- _cancelButton.Location = new Point(
- ClientSize.Width - rightMargin - _cancelButton.Width,
- ClientSize.Height - bottomMargin - _cancelButton.Height);
-
- _okButton.Width = minButtonWidth;
- _okButton.PerformLayout();
- _okButton.Location = new Point(
- _cancelButton.Left - hMargin - _okButton.Width,
- ClientSize.Height - bottomMargin - _okButton.Height);
-
- _etchedLine.Size = _etchedLine.GetPreferredSize(new Size(insetWidth, 0));
- _etchedLine.Location = new Point(
- leftMargin,
- Math.Min(_okButton.Top, _cancelButton.Top) - vMargin - _etchedLine.Height);
-
- // Commenting out this line of code, along with the others marked //2 and //3, fixes the
- // problem whereby the trackbar was always drawing a focus rectangle. However, if we enable
- // a resizable dialog, commenting out these lines results in some fidgety looking drawing.
- bool paintingSuspended = false;
- if (IsHandleCreated)
- {
- UI.SuspendControlPainting(_configUIPanel); //1
- paintingSuspended = true;
- }
-
- _configUIPanel.SuspendLayout();
-
- int configUIWidth = insetWidth;
- _configUIPanel.Bounds = new Rectangle(
- leftMargin,
- topMargin,
- insetWidth,
- ClientSize.Height - topMargin - (ClientSize.Height - _etchedLine.Top) - vMargin);
-
- Point autoScrollPos = _configUIPanel.AutoScrollPosition;
- _configUIPanel.AutoScroll = false;
-
- int propertyWidth1 = configUIWidth;
- int propertyWidth2 = configUIWidth - SystemInformation.VerticalScrollBarWidth - hMargin;
-
- int propertyWidth;
-
- if (_configUIControl.Width == propertyWidth1)
- {
- propertyWidth = propertyWidth1;
- }
- else if (_configUIControl.Height <= _configUIPanel.ClientRectangle.Height)
- {
- propertyWidth = propertyWidth1;
- }
- else
- {
- propertyWidth = propertyWidth2;
- }
-
- int tries = 3;
-
- while (tries > 0)
- {
- _configUIControl.SuspendLayout();
- _configUIControl.Location = _configUIPanel.AutoScrollPosition;
- _configUIControl.Width = propertyWidth;
- _configUIControl.ResumeLayout(false);
- _configUIControl.PerformLayout();
-
- if (_configUIControl.Height > _configUIPanel.ClientRectangle.Height)
- {
- propertyWidth = propertyWidth2;
- _configUIPanel.AutoScroll = true;
- --tries;
- }
- else if (_configUIControl.Height <= _configUIPanel.ClientRectangle.Height)
- {
- propertyWidth = propertyWidth1;
- _configUIPanel.AutoScroll = false;
- --tries;
- }
- else
- {
- break;
- }
- }
-
- _configUIPanel.ResumeLayout(false);
- _configUIPanel.PerformLayout();
-
- var finishPainting = new SendOrPostCallback(s =>
- {
- try
- {
- if (IsHandleCreated && !IsDisposed)
- {
- _configUIControl.Location = new Point(0, 0);
- _configUIPanel.AutoScrollPosition = new Point(-autoScrollPos.X, -autoScrollPos.Y);
-
- if (paintingSuspended)
- {
- UI.ResumeControlPainting(_configUIPanel); //2
- }
-
- Refresh();
- }
- }
-
- catch (Exception)
- {
- }
- });
-
- SynchronizationContext.Current.Post(finishPainting, null);
-
- var idealClientSize = new Size(
- ClientSize.Width,
- ClientSize.Height + _configUIControl.Height - _configUIPanel.Height);
-
- return idealClientSize;
- }
-
- internal override void OnBeforeConstructor(object context)
- {
- _properties = ((PropertyCollection)context).Clone();
- }
-
- public PropertyBasedEffectConfigDialog(IEnumerable<Property> propertyCollection, ICloneable<ControlInfo> configUI, ICloneable<PropertyCollection> windowProperties)
- : base(propertyCollection)
- {
- _windowProperties = windowProperties.Clone();
- _configUI = configUI.Clone();
-
- // Make sure that the properties in props and configUI are not the same objects
- if ((from property in propertyCollection
- let pci = _configUI.FindControlForPropertyName(property.Name)
- where pci != null && ReferenceEquals(property, pci.Property)
- select property).Any())
- {
- throw new ArgumentException("Property references in propertyCollection must not be the same as those in configUI");
- }
-
- SuspendLayout();
-
- _okButton = new Button();
- _cancelButton = new Button {Name = "cancelButton"};
- _configUIPanel = new Panel();
- _configUIControl = (Control)_configUI.CreateConcreteControl(this);
- _configUIControl.Location = new Point(0, 0);
-
- _configUIPanel.SuspendLayout();
- _configUIControl.SuspendLayout();
-
- _okButton.Name = "okButton";
- _okButton.AutoSize = true;
- _okButton.Click += OkButtonClick;
- _okButton.Text = PdnResources.GetString("Form.OkButton.Text");
- _okButton.FlatStyle = FlatStyle.System;
-
- _cancelButton.AutoSize = true;
- _cancelButton.Click += CancelButtonClick;
- _cancelButton.Text = PdnResources.GetString("Form.CancelButton.Text");
- _cancelButton.FlatStyle = FlatStyle.System;
-
- _configUIPanel.Name = "configUIPanel";
- _configUIPanel.TabStop = false;
- _configUIPanel.Controls.Add(_configUIControl);
-
- _configUIControl.Name = "configUIControl";
-
- _etchedLine = new EtchedLine {Name = "etchedLine"};
-
- Controls.AddRange(
- new Control[]
- {
- _okButton,
- _cancelButton,
- _etchedLine,
- _configUIPanel
- });
-
- int tabIndex = 0;
-
- _configUIControl.TabIndex = tabIndex;
- ++tabIndex;
-
- // Set up data binding
- foreach (PropertyControlInfo pci in
- _properties.Select(property => _configUI.FindControlForPropertyName(property.Name)))
- {
- if (pci == null)
- {
- throw new InvalidOperationException("Every property must have a control associated with it");
- }
- Property controlsProperty = pci.Property;
-
- // ASSUMPTION: We assume that the concrete WinForms Control holds a reference to
- // the same Property instance as the ControlInfo it was created from.
-
- controlsProperty.ValueChanged += ControlsPropertyValueChanged;
- }
-
- _okButton.TabIndex = tabIndex;
- ++tabIndex;
-
- _cancelButton.TabIndex = tabIndex;
- ++tabIndex;
-
- AcceptButton = _okButton;
- CancelButton = _cancelButton;
-
- var isSizable = (bool)_windowProperties[ControlInfoPropertyNames.WindowIsSizable].Value;
- FormBorderStyle = isSizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedDialog;
-
- Text = (string)_windowProperties[ControlInfoPropertyNames.WindowTitle].Value;
-
- ClientSize = new Size(UI.ScaleWidth(DefaultClientWidth96Dpi), UI.ScaleHeight(DefaultClientHeight96Dpi));
-
- _configUIControl.ResumeLayout(false);
- _configUIPanel.ResumeLayout(false);
-
- ResumeLayout(false);
- PerformLayout();
- }
-
- private void ControlsPropertyValueChanged(object sender, EventArgs e)
- {
- var controlsProperty = (Property)sender;
- Property property = _properties[controlsProperty.Name];
-
- if (!property.Value.Equals(controlsProperty.Value))
- {
- if (property.ReadOnly)
- {
- property.ReadOnly = false;
- property.Value = controlsProperty.Value;
- property.ReadOnly = true;
- }
- else
- {
- property.Value = controlsProperty.Value;
- }
- }
-
- FinishTokenUpdate();
- }
-
- private void CancelButtonClick(object sender, EventArgs e)
- {
- Close();
- }
-
- private void OkButtonClick(object sender, EventArgs e)
- {
- DialogResult = DialogResult.OK;
- Close();
- }
- }
- }