Skip to content

Commit 0df2dd5

Browse files
authored
Merge pull request #34 from Co0ob1iee/claude/sprint-1-gui-planning-011gB3rT8YernQkFrfsevVGv
feat: Complete Sprint 2 - Dashboard, modernized Settings and History
2 parents 58efdf9 + 770a8a3 commit 0df2dd5

10 files changed

Lines changed: 1904 additions & 225 deletions
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
using System;
2+
using System.Windows;
3+
using System.Windows.Controls;
4+
using System.Windows.Media;
5+
using System.Windows.Media.Animation;
6+
using Wpf.Ui.Controls;
7+
using PrettyScreenSHOT.Helpers;
8+
9+
namespace PrettyScreenSHOT.Services
10+
{
11+
public enum ToastType
12+
{
13+
Success,
14+
Error,
15+
Warning,
16+
Info
17+
}
18+
19+
public class ToastNotificationService
20+
{
21+
private static ToastNotificationService? instance;
22+
public static ToastNotificationService Instance => instance ??= new ToastNotificationService();
23+
24+
private ToastNotificationService() { }
25+
26+
/// <summary>
27+
/// Shows a toast notification
28+
/// </summary>
29+
public void Show(string title, string message, ToastType type = ToastType.Info, int durationMs = 3000)
30+
{
31+
try
32+
{
33+
// If MainWindow is open, show in-app toast
34+
if (TryShowInAppToast(title, message, type, durationMs))
35+
{
36+
return;
37+
}
38+
39+
// Otherwise, show system tray notification
40+
ShowTrayNotification(title, message, type);
41+
}
42+
catch (Exception ex)
43+
{
44+
DebugHelper.LogError("ToastNotificationService", "Error showing toast", ex);
45+
}
46+
}
47+
48+
private bool TryShowInAppToast(string title, string message, ToastType type, int durationMs)
49+
{
50+
try
51+
{
52+
// Find MainWindow if it's open
53+
foreach (Window window in Application.Current.Windows)
54+
{
55+
if (window is Views.Windows.MainWindow mainWindow)
56+
{
57+
Application.Current.Dispatcher.Invoke(() =>
58+
{
59+
ShowToastInWindow(mainWindow, title, message, type, durationMs);
60+
});
61+
return true;
62+
}
63+
}
64+
}
65+
catch (Exception ex)
66+
{
67+
DebugHelper.LogError("ToastNotificationService", "Error showing in-app toast", ex);
68+
}
69+
70+
return false;
71+
}
72+
73+
private void ShowToastInWindow(Views.Windows.MainWindow mainWindow, string title, string message, ToastType type, int durationMs)
74+
{
75+
// Create toast container if it doesn't exist
76+
var grid = mainWindow.Content as Grid;
77+
if (grid == null) return;
78+
79+
// Create toast card
80+
var toastCard = new Card
81+
{
82+
Padding = new Thickness(16),
83+
Margin = new Thickness(20, 20, 20, 0),
84+
HorizontalAlignment = HorizontalAlignment.Right,
85+
VerticalAlignment = VerticalAlignment.Top,
86+
MaxWidth = 400
87+
};
88+
89+
// Set background color based on type
90+
var (icon, foreground) = GetToastStyle(type);
91+
92+
var stackPanel = new StackPanel
93+
{
94+
Orientation = System.Windows.Controls.Orientation.Horizontal,
95+
Children =
96+
{
97+
new SymbolIcon
98+
{
99+
Symbol = icon,
100+
FontSize = 20,
101+
Margin = new Thickness(0, 0, 12, 0),
102+
Foreground = new SolidColorBrush(foreground)
103+
},
104+
new StackPanel
105+
{
106+
Children =
107+
{
108+
new System.Windows.Controls.TextBlock
109+
{
110+
Text = title,
111+
FontWeight = FontWeights.SemiBold,
112+
FontSize = 14
113+
},
114+
new System.Windows.Controls.TextBlock
115+
{
116+
Text = message,
117+
FontSize = 12,
118+
Opacity = 0.8,
119+
TextWrapping = TextWrapping.Wrap
120+
}
121+
}
122+
}
123+
}
124+
};
125+
126+
toastCard.Content = stackPanel;
127+
128+
// Add to grid
129+
Grid.SetRowSpan(toastCard, grid.RowDefinitions.Count);
130+
Grid.SetColumnSpan(toastCard, grid.ColumnDefinitions.Count);
131+
grid.Children.Add(toastCard);
132+
133+
// Animate in
134+
toastCard.Opacity = 0;
135+
toastCard.RenderTransform = new TranslateTransform(0, -20);
136+
137+
var fadeIn = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(200));
138+
var slideIn = new DoubleAnimation(-20, 0, TimeSpan.FromMilliseconds(200));
139+
140+
toastCard.BeginAnimation(UIElement.OpacityProperty, fadeIn);
141+
((TranslateTransform)toastCard.RenderTransform).BeginAnimation(TranslateTransform.YProperty, slideIn);
142+
143+
// Auto-hide after duration
144+
var timer = new System.Windows.Threading.DispatcherTimer
145+
{
146+
Interval = TimeSpan.FromMilliseconds(durationMs)
147+
};
148+
149+
timer.Tick += (s, e) =>
150+
{
151+
timer.Stop();
152+
153+
// Animate out
154+
var fadeOut = new DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(200));
155+
var slideOut = new DoubleAnimation(0, -20, TimeSpan.FromMilliseconds(200));
156+
157+
fadeOut.Completed += (s2, e2) =>
158+
{
159+
grid.Children.Remove(toastCard);
160+
};
161+
162+
toastCard.BeginAnimation(UIElement.OpacityProperty, fadeOut);
163+
((TranslateTransform)toastCard.RenderTransform).BeginAnimation(TranslateTransform.YProperty, slideOut);
164+
};
165+
166+
timer.Start();
167+
}
168+
169+
private void ShowTrayNotification(string title, string message, ToastType type)
170+
{
171+
var trayIcon = TrayIconManager.Instance;
172+
if (trayIcon != null)
173+
{
174+
var iconType = type switch
175+
{
176+
ToastType.Success => System.Windows.Forms.ToolTipIcon.Info,
177+
ToastType.Error => System.Windows.Forms.ToolTipIcon.Error,
178+
ToastType.Warning => System.Windows.Forms.ToolTipIcon.Warning,
179+
_ => System.Windows.Forms.ToolTipIcon.Info
180+
};
181+
182+
trayIcon.ShowNotification(title, message);
183+
DebugHelper.LogInfo("ToastNotificationService", $"Tray notification: {title} - {message}");
184+
}
185+
}
186+
187+
private (Wpf.Ui.Common.SymbolRegular icon, Color foreground) GetToastStyle(ToastType type)
188+
{
189+
return type switch
190+
{
191+
ToastType.Success => (Wpf.Ui.Common.SymbolRegular.CheckmarkCircle24, Color.FromRgb(16, 185, 129)),
192+
ToastType.Error => (Wpf.Ui.Common.SymbolRegular.DismissCircle24, Color.FromRgb(239, 68, 68)),
193+
ToastType.Warning => (Wpf.Ui.Common.SymbolRegular.Warning24, Color.FromRgb(245, 158, 11)),
194+
_ => (Wpf.Ui.Common.SymbolRegular.Info24, Color.FromRgb(59, 130, 246))
195+
};
196+
}
197+
198+
// Convenience methods
199+
public void ShowSuccess(string message) => Show("Sukces", message, ToastType.Success);
200+
public void ShowError(string message) => Show("Błąd", message, ToastType.Error);
201+
public void ShowWarning(string message) => Show("Ostrzeżenie", message, ToastType.Warning);
202+
public void ShowInfo(string message) => Show("Informacja", message, ToastType.Info);
203+
}
204+
}

Services/TrayIconManager.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ public void Initialize()
3030
private void CreateTrayIcon()
3131
{
3232
contextMenu = new ContextMenuStrip();
33-
33+
34+
var dashboardItem = new ToolStripMenuItem("Dashboard", null, ShowDashboard);
3435
var editItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_EditLastScreenshot"), null, EditLastScreenshot);
3536
var historyItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_History"), null, ShowHistory);
3637
var scrollCaptureItem = new ToolStripMenuItem("Scroll Capture", null, StartScrollCapture);
3738
var videoCaptureItem = new ToolStripMenuItem("Video Capture", null, StartVideoCapture);
3839
var checkUpdateItem = new ToolStripMenuItem("Check for Updates", null, CheckForUpdates);
3940
var settingsItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_Settings"), null, ShowSettings);
4041
var exitItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_Exit"), null, ExitApplication);
41-
42+
43+
contextMenu.Items.Add(dashboardItem);
44+
contextMenu.Items.Add(new ToolStripSeparator());
4245
contextMenu.Items.Add(editItem);
4346
contextMenu.Items.Add(historyItem);
4447
contextMenu.Items.Add(new ToolStripSeparator());
@@ -62,7 +65,7 @@ private void CreateTrayIcon()
6265
ContextMenuStrip = contextMenu
6366
};
6467

65-
notifyIcon.MouseDoubleClick += (s, e) => ShowHistory(null, null);
68+
notifyIcon.MouseDoubleClick += (s, e) => ShowDashboard(null, null);
6669
notifyIcon.BalloonTipClicked += (s, e) => EditLastScreenshot(null, null);
6770
}
6871

@@ -151,6 +154,24 @@ private void EditLastScreenshot(object? sender, EventArgs? e)
151154
}
152155
}
153156

157+
private void ShowDashboard(object? sender, EventArgs? e)
158+
{
159+
// Check if MainWindow is already open
160+
foreach (Window window in System.Windows.Application.Current.Windows)
161+
{
162+
if (window is MainWindow mainWindow)
163+
{
164+
mainWindow.Activate();
165+
mainWindow.WindowState = WindowState.Normal;
166+
return;
167+
}
168+
}
169+
170+
// Create new MainWindow
171+
var dashboard = new MainWindow();
172+
dashboard.Show();
173+
}
174+
154175
private void ShowHistory(object? sender, EventArgs? e)
155176
{
156177
var historyWindow = new ScreenshotHistoryWindow();

0 commit comments

Comments
 (0)