PageRenderTime 56ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Effects/PropertyBasedEffectConfigDialog.cs

https://bitbucket.org/tuldok89/openpdn
C# | 438 lines | 334 code | 84 blank | 20 comment | 28 complexity | 812a87b9da35605a70fa8f5a7ce97b41 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) Rick Brewster, Tom Jackson, and past contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using System.Linq;
  10. using PaintDotNet.Base;
  11. using PaintDotNet.Base.PropertySystem;
  12. using PaintDotNet.IndirectUI;
  13. using PaintDotNet.SystemLayer;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Drawing;
  17. using System.Threading;
  18. using System.Windows.Forms;
  19. namespace PaintDotNet.Effects
  20. {
  21. internal sealed class PropertyBasedEffectConfigDialog
  22. : EffectConfigDialog<PropertyBasedEffect, PropertyBasedEffectConfigToken>
  23. {
  24. private const int DefaultClientWidth96Dpi = 345;
  25. private const int DefaultClientHeight96Dpi = 125;
  26. private readonly Button _okButton;
  27. private readonly Button _cancelButton;
  28. private readonly EtchedLine _etchedLine;
  29. private readonly ControlInfo _configUI;
  30. private readonly Panel _configUIPanel;
  31. private readonly Control _configUIControl;
  32. private PropertyCollection _properties;
  33. private readonly PropertyCollection _windowProperties;
  34. internal static PropertyCollection CreateWindowProperties()
  35. {
  36. var props = new List<Property>
  37. {
  38. new StringProperty(
  39. ControlInfoPropertyNames.WindowTitle,
  40. string.Empty,
  41. Math.Min(1024, StringProperty.MaxMaxLength)),
  42. new BooleanProperty(ControlInfoPropertyNames.WindowIsSizable, false),
  43. new DoubleProperty(ControlInfoPropertyNames.WindowWidthScale, 1.0, 1.0, 2.0)
  44. };
  45. // Title
  46. // Sizability -- defaults to false
  47. // Window width, as a scale of the default width (1.0 is obviously the default)
  48. return new PropertyCollection(props);
  49. }
  50. protected override PropertyBasedEffectConfigToken CreateInitialToken()
  51. {
  52. var token = new PropertyBasedEffectConfigToken(_properties);
  53. return token;
  54. }
  55. protected override void InitDialogFromToken(PropertyBasedEffectConfigToken effectTokenCopy)
  56. {
  57. // We run this twice so that rules don't execute on stale values
  58. // See bug #2719
  59. InitDialogFromTokenImpl(effectTokenCopy);
  60. InitDialogFromTokenImpl(effectTokenCopy);
  61. }
  62. private void InitDialogFromTokenImpl(PropertyBasedEffectConfigToken effectTokenCopy)
  63. {
  64. foreach (string propertyName in effectTokenCopy.PropertyNames)
  65. {
  66. var srcProperty = effectTokenCopy.GetProperty<Property>(propertyName);
  67. PropertyControlInfo dstPropertyControlInfo = _configUI.FindControlForPropertyName(propertyName);
  68. if (dstPropertyControlInfo == null) continue;
  69. Property dstProperty = dstPropertyControlInfo.Property;
  70. if (dstProperty.ReadOnly)
  71. {
  72. dstProperty.ReadOnly = false;
  73. dstProperty.Value = srcProperty.Value;
  74. dstProperty.ReadOnly = true;
  75. }
  76. else
  77. {
  78. dstProperty.Value = srcProperty.Value;
  79. }
  80. }
  81. }
  82. protected override void LoadIntoTokenFromDialog(PropertyBasedEffectConfigToken writeValuesHere)
  83. {
  84. foreach (string propertyName in EffectToken.PropertyNames)
  85. {
  86. PropertyControlInfo srcPropertyControlInfo = _configUI.FindControlForPropertyName(propertyName);
  87. if (srcPropertyControlInfo == null) continue;
  88. Property srcProperty = srcPropertyControlInfo.Property;
  89. Property dstProperty = writeValuesHere.GetProperty(srcProperty.Name);
  90. if (dstProperty.ReadOnly)
  91. {
  92. dstProperty.ReadOnly = false;
  93. dstProperty.Value = srcProperty.Value;
  94. dstProperty.ReadOnly = true;
  95. }
  96. else
  97. {
  98. dstProperty.Value = srcProperty.Value;
  99. }
  100. }
  101. }
  102. protected override void OnShown(EventArgs e)
  103. {
  104. if (_configUIControl is IFirstSelection)
  105. {
  106. ((IFirstSelection)_configUIControl).FirstSelect();
  107. }
  108. base.OnShown(e);
  109. }
  110. protected override void OnLoad(EventArgs e)
  111. {
  112. Size idealClientSize = OnLayoutImpl();
  113. Size idealWindowSize = ClientSizeToWindowSize(idealClientSize);
  114. Rectangle workingArea = Screen.FromControl(this).WorkingArea;
  115. var targetWindowSize = new Size(
  116. Math.Min(workingArea.Width, idealWindowSize.Width),
  117. Math.Min(workingArea.Height, idealWindowSize.Height));
  118. Size targetClientSize = WindowSizeToClientSize(targetWindowSize);
  119. if (ClientSize != targetClientSize)
  120. {
  121. ClientSize = targetClientSize;
  122. OnLayoutImpl();
  123. }
  124. var newMinimumSize = new Size(idealWindowSize.Width, Math.Min(workingArea.Height, Math.Min(idealWindowSize.Height, UI.ScaleHeight(500))));
  125. Size minimumClientSize = WindowSizeToClientSize(newMinimumSize);
  126. MinimumSize = newMinimumSize;
  127. ClientSize = new Size(
  128. (int)(ClientSize.Width * (double)_windowProperties[ControlInfoPropertyNames.WindowWidthScale].Value),
  129. Math.Min(ClientSize.Height, (workingArea.Height * 9) / 10)); // max height should ever be 90% of working screen area height
  130. base.OnLoad(e);
  131. }
  132. protected override void OnLayout(LayoutEventArgs levent)
  133. {
  134. Size idealClientSize = OnLayoutImpl();
  135. base.OnLayout(levent);
  136. }
  137. private Size OnLayoutImpl()
  138. {
  139. int leftMargin = UI.ScaleWidth(8);
  140. int rightMargin = UI.ScaleHeight(8);
  141. int insetWidth = ClientSize.Width - leftMargin - rightMargin;
  142. int topMargin = UI.ScaleHeight(8);
  143. int bottomMargin = UI.ScaleHeight(8);
  144. int insetHeight = ClientSize.Height - topMargin - bottomMargin;
  145. int hMargin = UI.ScaleWidth(6);
  146. int vMargin = UI.ScaleHeight(6);
  147. int minButtonWidth = UI.ScaleWidth(80);
  148. _cancelButton.Width = minButtonWidth;
  149. _cancelButton.PerformLayout();
  150. _cancelButton.Location = new Point(
  151. ClientSize.Width - rightMargin - _cancelButton.Width,
  152. ClientSize.Height - bottomMargin - _cancelButton.Height);
  153. _okButton.Width = minButtonWidth;
  154. _okButton.PerformLayout();
  155. _okButton.Location = new Point(
  156. _cancelButton.Left - hMargin - _okButton.Width,
  157. ClientSize.Height - bottomMargin - _okButton.Height);
  158. _etchedLine.Size = _etchedLine.GetPreferredSize(new Size(insetWidth, 0));
  159. _etchedLine.Location = new Point(
  160. leftMargin,
  161. Math.Min(_okButton.Top, _cancelButton.Top) - vMargin - _etchedLine.Height);
  162. // Commenting out this line of code, along with the others marked //2 and //3, fixes the
  163. // problem whereby the trackbar was always drawing a focus rectangle. However, if we enable
  164. // a resizable dialog, commenting out these lines results in some fidgety looking drawing.
  165. bool paintingSuspended = false;
  166. if (IsHandleCreated)
  167. {
  168. UI.SuspendControlPainting(_configUIPanel); //1
  169. paintingSuspended = true;
  170. }
  171. _configUIPanel.SuspendLayout();
  172. int configUIWidth = insetWidth;
  173. _configUIPanel.Bounds = new Rectangle(
  174. leftMargin,
  175. topMargin,
  176. insetWidth,
  177. ClientSize.Height - topMargin - (ClientSize.Height - _etchedLine.Top) - vMargin);
  178. Point autoScrollPos = _configUIPanel.AutoScrollPosition;
  179. _configUIPanel.AutoScroll = false;
  180. int propertyWidth1 = configUIWidth;
  181. int propertyWidth2 = configUIWidth - SystemInformation.VerticalScrollBarWidth - hMargin;
  182. int propertyWidth;
  183. if (_configUIControl.Width == propertyWidth1)
  184. {
  185. propertyWidth = propertyWidth1;
  186. }
  187. else if (_configUIControl.Height <= _configUIPanel.ClientRectangle.Height)
  188. {
  189. propertyWidth = propertyWidth1;
  190. }
  191. else
  192. {
  193. propertyWidth = propertyWidth2;
  194. }
  195. int tries = 3;
  196. while (tries > 0)
  197. {
  198. _configUIControl.SuspendLayout();
  199. _configUIControl.Location = _configUIPanel.AutoScrollPosition;
  200. _configUIControl.Width = propertyWidth;
  201. _configUIControl.ResumeLayout(false);
  202. _configUIControl.PerformLayout();
  203. if (_configUIControl.Height > _configUIPanel.ClientRectangle.Height)
  204. {
  205. propertyWidth = propertyWidth2;
  206. _configUIPanel.AutoScroll = true;
  207. --tries;
  208. }
  209. else if (_configUIControl.Height <= _configUIPanel.ClientRectangle.Height)
  210. {
  211. propertyWidth = propertyWidth1;
  212. _configUIPanel.AutoScroll = false;
  213. --tries;
  214. }
  215. else
  216. {
  217. break;
  218. }
  219. }
  220. _configUIPanel.ResumeLayout(false);
  221. _configUIPanel.PerformLayout();
  222. var finishPainting = new SendOrPostCallback(s =>
  223. {
  224. try
  225. {
  226. if (IsHandleCreated && !IsDisposed)
  227. {
  228. _configUIControl.Location = new Point(0, 0);
  229. _configUIPanel.AutoScrollPosition = new Point(-autoScrollPos.X, -autoScrollPos.Y);
  230. if (paintingSuspended)
  231. {
  232. UI.ResumeControlPainting(_configUIPanel); //2
  233. }
  234. Refresh();
  235. }
  236. }
  237. catch (Exception)
  238. {
  239. }
  240. });
  241. SynchronizationContext.Current.Post(finishPainting, null);
  242. var idealClientSize = new Size(
  243. ClientSize.Width,
  244. ClientSize.Height + _configUIControl.Height - _configUIPanel.Height);
  245. return idealClientSize;
  246. }
  247. internal override void OnBeforeConstructor(object context)
  248. {
  249. _properties = ((PropertyCollection)context).Clone();
  250. }
  251. public PropertyBasedEffectConfigDialog(IEnumerable<Property> propertyCollection, ICloneable<ControlInfo> configUI, ICloneable<PropertyCollection> windowProperties)
  252. : base(propertyCollection)
  253. {
  254. _windowProperties = windowProperties.Clone();
  255. _configUI = configUI.Clone();
  256. // Make sure that the properties in props and configUI are not the same objects
  257. if ((from property in propertyCollection
  258. let pci = _configUI.FindControlForPropertyName(property.Name)
  259. where pci != null && ReferenceEquals(property, pci.Property)
  260. select property).Any())
  261. {
  262. throw new ArgumentException("Property references in propertyCollection must not be the same as those in configUI");
  263. }
  264. SuspendLayout();
  265. _okButton = new Button();
  266. _cancelButton = new Button {Name = "cancelButton"};
  267. _configUIPanel = new Panel();
  268. _configUIControl = (Control)_configUI.CreateConcreteControl(this);
  269. _configUIControl.Location = new Point(0, 0);
  270. _configUIPanel.SuspendLayout();
  271. _configUIControl.SuspendLayout();
  272. _okButton.Name = "okButton";
  273. _okButton.AutoSize = true;
  274. _okButton.Click += OkButtonClick;
  275. _okButton.Text = PdnResources.GetString("Form.OkButton.Text");
  276. _okButton.FlatStyle = FlatStyle.System;
  277. _cancelButton.AutoSize = true;
  278. _cancelButton.Click += CancelButtonClick;
  279. _cancelButton.Text = PdnResources.GetString("Form.CancelButton.Text");
  280. _cancelButton.FlatStyle = FlatStyle.System;
  281. _configUIPanel.Name = "configUIPanel";
  282. _configUIPanel.TabStop = false;
  283. _configUIPanel.Controls.Add(_configUIControl);
  284. _configUIControl.Name = "configUIControl";
  285. _etchedLine = new EtchedLine {Name = "etchedLine"};
  286. Controls.AddRange(
  287. new Control[]
  288. {
  289. _okButton,
  290. _cancelButton,
  291. _etchedLine,
  292. _configUIPanel
  293. });
  294. int tabIndex = 0;
  295. _configUIControl.TabIndex = tabIndex;
  296. ++tabIndex;
  297. // Set up data binding
  298. foreach (PropertyControlInfo pci in
  299. _properties.Select(property => _configUI.FindControlForPropertyName(property.Name)))
  300. {
  301. if (pci == null)
  302. {
  303. throw new InvalidOperationException("Every property must have a control associated with it");
  304. }
  305. Property controlsProperty = pci.Property;
  306. // ASSUMPTION: We assume that the concrete WinForms Control holds a reference to
  307. // the same Property instance as the ControlInfo it was created from.
  308. controlsProperty.ValueChanged += ControlsPropertyValueChanged;
  309. }
  310. _okButton.TabIndex = tabIndex;
  311. ++tabIndex;
  312. _cancelButton.TabIndex = tabIndex;
  313. ++tabIndex;
  314. AcceptButton = _okButton;
  315. CancelButton = _cancelButton;
  316. var isSizable = (bool)_windowProperties[ControlInfoPropertyNames.WindowIsSizable].Value;
  317. FormBorderStyle = isSizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedDialog;
  318. Text = (string)_windowProperties[ControlInfoPropertyNames.WindowTitle].Value;
  319. ClientSize = new Size(UI.ScaleWidth(DefaultClientWidth96Dpi), UI.ScaleHeight(DefaultClientHeight96Dpi));
  320. _configUIControl.ResumeLayout(false);
  321. _configUIPanel.ResumeLayout(false);
  322. ResumeLayout(false);
  323. PerformLayout();
  324. }
  325. private void ControlsPropertyValueChanged(object sender, EventArgs e)
  326. {
  327. var controlsProperty = (Property)sender;
  328. Property property = _properties[controlsProperty.Name];
  329. if (!property.Value.Equals(controlsProperty.Value))
  330. {
  331. if (property.ReadOnly)
  332. {
  333. property.ReadOnly = false;
  334. property.Value = controlsProperty.Value;
  335. property.ReadOnly = true;
  336. }
  337. else
  338. {
  339. property.Value = controlsProperty.Value;
  340. }
  341. }
  342. FinishTokenUpdate();
  343. }
  344. private void CancelButtonClick(object sender, EventArgs e)
  345. {
  346. Close();
  347. }
  348. private void OkButtonClick(object sender, EventArgs e)
  349. {
  350. DialogResult = DialogResult.OK;
  351. Close();
  352. }
  353. }
  354. }