Toasts – Diff between revs 43 and 44
?pathlinks?
Rev 43 | Rev 44 | |||
---|---|---|---|---|
Line 15... | Line 15... | |||
15 | using System.Threading; |
15 | using System.Threading; |
|
16 | using System.Threading.Tasks; |
16 | using System.Threading.Tasks; |
|
17 | using System.Windows.Forms; |
17 | using System.Windows.Forms; |
|
18 | using TheArtOfDev.HtmlRenderer.WinForms; |
18 | using TheArtOfDev.HtmlRenderer.WinForms; |
|
19 | using Toasts.Properties; |
19 | using Toasts.Properties; |
|
20 | using static System.Windows.Forms.VisualStyles.VisualStyleElement; |
- | ||
21 | using static TheArtOfDev.HtmlRenderer.Adapters.RGraphicsPath; |
- | ||
22 | using static Toasts.FormAnimator; |
- | ||
Line 23... | Line 20... | |||
23 | |
20 | |
|
24 | namespace Toasts |
21 | namespace Toasts |
|
25 | { |
22 | { |
|
26 | public partial class ToastForm : Form |
23 | public partial class ToastForm : Form |
|
27 | { |
24 | { |
|
28 | #region Public Fields and Properties |
25 | #region Public Fields and Properties |
|
29 | |
26 | |
|
30 | public bool EnableChime { get; set; } = true; |
27 | public bool EnableChime { get; set; } = true; |
|
31 | |
28 | |
|
32 | public int LingerTime { get; set; } = 5000; |
29 | public int LingerTime { get; set; } = 5000; |
|
33 | |
30 | |
|
34 | public FormAnimator.AnimationMethod AnimationMethodDetached { get; set; } = FormAnimator.AnimationMethod.Fade; |
31 | public FormAnimator.AnimationMethod AnimationMethodDetached { get; set; } = FormAnimator.AnimationMethod.Fade; |
|
35 | |
32 | |
|
36 | public FormAnimator.AnimationDirection AnimationDirectionDetached { get; set; } = FormAnimator.AnimationDirection.None; |
33 | public FormAnimator.AnimationDirection AnimationDirectionDetached { get; set; } = FormAnimator.AnimationDirection.None; |
|
37 | |
34 | |
|
38 | public FormAnimator.AnimationMethod AnimationMethod { get; set; } = FormAnimator.AnimationMethod.Slide; |
35 | public FormAnimator.AnimationMethod AnimationMethod { get; set; } = FormAnimator.AnimationMethod.Slide; |
|
39 | |
36 | |
|
40 | public FormAnimator.AnimationDirection AnimationDirection { get; set; } = FormAnimator.AnimationDirection.Up; |
37 | public FormAnimator.AnimationDirection AnimationDirection { get; set; } = FormAnimator.AnimationDirection.Up; |
|
41 | |
38 | |
|
42 | public int AnimationDuration { get; set; } = 500; |
39 | public int AnimationDuration { get; set; } = 500; |
|
43 | |
40 | |
|
44 | public string ContentType { get; internal set; } = "text/plain"; |
41 | public string ContentType { get; internal set; } = "text/plain"; |
|
45 | |
42 | |
|
46 | public byte[] Chime |
43 | public byte[] Chime |
|
47 | { |
44 | { |
|
48 | get => _chime; |
45 | get => _chime; |
|
49 | set |
46 | set |
|
50 | { |
47 | { |
|
51 | if (value is null) |
48 | if (value is null) |
|
52 | { |
49 | { |
|
53 | return; |
50 | return; |
|
54 | } |
51 | } |
|
55 | |
52 | |
|
56 | _chime = value; |
53 | _chime = value; |
|
57 | } |
54 | } |
|
58 | } |
55 | } |
|
59 | |
56 | |
|
60 | /// <summary> |
57 | /// <summary> |
|
61 | /// |
58 | /// |
|
62 | /// </summary> |
59 | /// </summary> |
|
63 | /// <remarks>clone the image for safety</remarks> |
60 | /// <remarks>clone the image for safety</remarks> |
|
64 | public Image Image |
61 | public Image Image |
|
65 | { |
62 | { |
|
66 | get |
63 | get |
|
67 | { |
64 | { |
|
68 | try |
65 | try |
|
Line 69... | Line 66... | |||
69 | { |
66 | { |
|
70 | |
67 | |
|
71 | return new Bitmap(imageBox.Image); |
68 | return new Bitmap(imageBox.Image); |
|
72 | } |
69 | } |
|
73 | catch |
70 | catch |
|
74 | { |
71 | { |
|
75 | return null; |
72 | return null; |
|
76 | } |
73 | } |
|
77 | } |
74 | } |
|
78 | set => imageBox.Image = value; |
75 | set => imageBox.Image = value; |
|
79 | } |
76 | } |
|
80 | |
77 | |
|
81 | #endregion |
78 | #endregion |
|
82 | |
79 | |
|
83 | #region Static Fields and Constants |
80 | #region Static Fields and Constants |
|
84 | |
81 | |
|
85 | private static readonly object OpenNotificationsLock = new object(); |
82 | private static readonly object OpenNotificationsLock = new object(); |
|
86 | |
83 | |
|
87 | private static readonly HashSet<ToastForm> OpenNotifications = new HashSet<ToastForm>(); |
84 | private static readonly HashSet<ToastForm> OpenNotifications = new HashSet<ToastForm>(); |
|
88 | |
85 | |
|
89 | #endregion |
86 | #endregion |
|
90 | |
87 | |
|
91 | #region Private Fields and Properties |
88 | #region Private Fields and Properties |
|
92 | |
89 | |
|
93 | private bool _toastDetached; |
90 | private bool _toastDetached; |
|
94 | private object _toastDetachedLock = new object(); |
91 | private object _toastDetachedLock = new object(); |
|
95 | private bool _mouseDown; |
92 | private bool _mouseDown; |
|
96 | private Point _lastLocation; |
93 | private Point _lastLocation; |
|
97 | |
94 | |
|
98 | #endregion |
95 | #endregion |
|
99 | |
96 | |
|
100 | #region Private Overrides |
97 | #region Private Overrides |
|
101 | |
98 | |
|
102 | protected override bool ShowWithoutActivation => true; |
99 | protected override bool ShowWithoutActivation => true; |
|
103 | protected override CreateParams CreateParams |
100 | protected override CreateParams CreateParams |
|
104 | { |
101 | { |
|
105 | get |
102 | get |
|
106 | { |
103 | { |
|
107 | |
104 | |
|
108 | var baseParams = base.CreateParams; |
105 | var baseParams = base.CreateParams; |
|
109 | |
106 | |
|
110 | //const int WS_EX_NOACTIVATE = 0x08000000; |
107 | //const int WS_EX_NOACTIVATE = 0x08000000; |
|
111 | const int WS_EX_TOOLWINDOW = 0x00000080; |
108 | const int WS_EX_TOOLWINDOW = 0x00000080; |
|
112 | const int WS_EX_TOPMOST = 0x00000008; |
109 | const int WS_EX_TOPMOST = 0x00000008; |
|
113 | baseParams.ExStyle |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; |
110 | baseParams.ExStyle |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; |
|
114 | |
111 | |
|
115 | return baseParams; |
112 | return baseParams; |
|
Line 116... | Line 113... | |||
116 | } |
113 | } |
|
117 | } |
114 | } |
|
Line 214... | Line 242... | |||
214 | if (EnableChime) |
242 | if (EnableChime) |
|
215 | { |
243 | { |
|
216 | Task.Factory.StartNew(() => |
244 | Task.Factory.StartNew(() => |
|
217 | { |
245 | { |
|
218 | try |
246 | try |
|
219 | { |
247 | { |
|
220 | using (var memoryStream = new MemoryStream(Chime)) |
248 | using (var memoryStream = new MemoryStream(Chime)) |
|
221 | { |
249 | { |
|
222 | using (var player = new SoundPlayer(memoryStream)) |
250 | using (var player = new SoundPlayer(memoryStream)) |
|
223 | { |
251 | { |
|
224 | player.Play(); |
252 | player.Play(); |
|
225 | } |
253 | } |
|
226 | } |
254 | } |
|
227 | } |
255 | } |
|
228 | catch |
256 | catch |
|
229 | { |
257 | { |
|
230 | Debug.WriteLine("Sound file could not be played."); |
258 | Debug.WriteLine("Sound file could not be played."); |
|
231 | } |
259 | } |
|
232 | }); |
260 | }); |
|
233 | } |
261 | } |
|
234 | |
262 | |
|
235 | try |
263 | try |
|
236 | { |
264 | { |
|
237 | lock (OpenNotificationsLock) |
265 | lock (OpenNotificationsLock) |
|
238 | { |
266 | { |
|
239 | // if not image is provided, just collapse the panel for the extra space |
267 | // if not image is provided, just collapse the panel for the extra space |
|
240 | if (imageBox.Image == null) |
268 | if (imageBox.Image == null) |
|
241 | { |
269 | { |
|
242 | splitContainer1.Panel1Collapsed = true; |
270 | splitContainer1.Panel1Collapsed = true; |
|
243 | } |
271 | } |
|
244 | |
272 | |
|
245 | Invoke(new MethodInvoker(() => |
273 | Invoke(new MethodInvoker(() => |
|
246 | { |
274 | { |
|
247 | // compute notification height from body |
275 | // compute notification height from body |
|
248 | var maxWidth = tableLayoutPanel4.Width; |
276 | var maxWidth = tableLayoutPanel4.Width; |
|
249 | using (var m_Bitmap = new Bitmap(64, 64)) |
277 | using (var m_Bitmap = new Bitmap(64, 64)) |
|
250 | { |
278 | { |
|
251 | using (var graphics = Graphics.FromImage(m_Bitmap)) |
279 | using (var graphics = Graphics.FromImage(m_Bitmap)) |
|
252 | { |
280 | { |
|
- | 281 | switch (ContentType) |
||
- | 282 | { |
||
- | 283 | case "text/markdown": |
||
253 | switch(ContentType) |
284 | var htmlDocument = new HtmlAgilityPack.HtmlDocument(); |
|
- | 285 | var panelText = htmlPanel1.Text; |
||
- | 286 | if (!string.IsNullOrEmpty(panelText)) |
||
254 | { |
287 | { |
|
- | 288 | htmlDocument.LoadHtml(panelText); |
||
255 | case "text/markdown": |
289 | if (htmlDocument.DocumentNode != null && htmlDocument.DocumentNode.Descendants().Any()) |
|
- | 290 | { |
||
- | 291 | var imgNodes = htmlDocument.DocumentNode.SelectNodes("//img"); |
||
256 | var htmlDocument = new HtmlAgilityPack.HtmlDocument(); |
292 | if (imgNodes != null && imgNodes.Any()) |
|
- | 293 | { |
||
- | 294 | foreach (var node in imgNodes) |
||
257 | htmlDocument.LoadHtml(htmlPanel1.Text); |
295 | { |
|
258 | foreach (var node in htmlDocument.DocumentNode.SelectNodes("//img")) |
296 | node.SetAttributeValue("style", $"max-width: {maxWidth}px"); |
|
259 | { |
297 | } |
|
- | 298 | } |
||
260 | node.SetAttributeValue("style", $"max-width: {maxWidth}px"); |
299 | } |
|
261 | } |
300 | |
|
262 | |
301 | htmlPanel1.Text = htmlDocument.DocumentNode.WriteTo(); |
|
263 | htmlPanel1.Text = htmlDocument.DocumentNode.WriteTo(); |
302 | } |
|
264 | break; |
303 | break; |
|
265 | default: |
304 | default: |
|
266 | break; |
305 | break; |
|
267 | } |
306 | } |
|
268 | |
307 | |
|
269 | PointF point = new PointF(0, 0); |
308 | PointF point = new PointF(0, 0); |
|
270 | SizeF maxSize = new SizeF(maxWidth, Screen.PrimaryScreen.WorkingArea.Height); |
309 | SizeF maxSize = new SizeF(maxWidth, Screen.PrimaryScreen.WorkingArea.Height); |
|
271 | var renderSize = HtmlRender.Render(graphics, htmlPanel1.Text, point, maxSize); |
310 | var renderSize = HtmlRender.Render(graphics, htmlPanel1.Text, point, maxSize); |
|
272 | // total height = height of text fitting in rectangle + the height of the title on top + one extra line for the last line wrap |
311 | // total height = height of text fitting in rectangle + the height of the title on top + one extra line for the last line wrap |
|
273 | var computedOptimalHeight = (int)Math.Round(renderSize.Height) + labelTitle.Height + (int)Math.Round(htmlPanel1.Font.GetHeight()); |
312 | var computedOptimalHeight = (int)Math.Round(renderSize.Height) + labelTitle.Height + (int)Math.Round(htmlPanel1.Font.GetHeight()); |
|
274 | // keep the default height of a notification constant |
313 | // keep the default height of a notification constant |
|
275 | if(computedOptimalHeight > Height) |
314 | if (computedOptimalHeight > Height) |
|
276 | { |
315 | { |
|
277 | Height = computedOptimalHeight; |
316 | Height = computedOptimalHeight; |
|
278 | } |
317 | } |
|
279 | } |
318 | } |
|
280 | } |
319 | } |
|
281 | })); |
320 | })); |
|
282 | |
321 | |
|
283 | if (EnablePin) |
322 | if (EnablePin) |
|
Line 293... | Line 332... | |||
293 | |
332 | |
|
294 | _formAnimator.Method = AnimationMethodDetached; |
333 | _formAnimator.Method = AnimationMethodDetached; |
|
Line 295... | Line 334... | |||
295 | _formAnimator.Direction = AnimationDirectionDetached; |
334 | _formAnimator.Direction = AnimationDirectionDetached; |
|
517 | } |
561 | |
|
518 | } |
562 | #endregion |
|
519 | } |
563 | } |