diff --git a/Services/ToastNotificationService.cs b/Services/ToastNotificationService.cs new file mode 100644 index 0000000..b24e131 --- /dev/null +++ b/Services/ToastNotificationService.cs @@ -0,0 +1,204 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; +using Wpf.Ui.Controls; +using PrettyScreenSHOT.Helpers; + +namespace PrettyScreenSHOT.Services +{ + public enum ToastType + { + Success, + Error, + Warning, + Info + } + + public class ToastNotificationService + { + private static ToastNotificationService? instance; + public static ToastNotificationService Instance => instance ??= new ToastNotificationService(); + + private ToastNotificationService() { } + + /// + /// Shows a toast notification + /// + public void Show(string title, string message, ToastType type = ToastType.Info, int durationMs = 3000) + { + try + { + // If MainWindow is open, show in-app toast + if (TryShowInAppToast(title, message, type, durationMs)) + { + return; + } + + // Otherwise, show system tray notification + ShowTrayNotification(title, message, type); + } + catch (Exception ex) + { + DebugHelper.LogError("ToastNotificationService", "Error showing toast", ex); + } + } + + private bool TryShowInAppToast(string title, string message, ToastType type, int durationMs) + { + try + { + // Find MainWindow if it's open + foreach (Window window in Application.Current.Windows) + { + if (window is Views.Windows.MainWindow mainWindow) + { + Application.Current.Dispatcher.Invoke(() => + { + ShowToastInWindow(mainWindow, title, message, type, durationMs); + }); + return true; + } + } + } + catch (Exception ex) + { + DebugHelper.LogError("ToastNotificationService", "Error showing in-app toast", ex); + } + + return false; + } + + private void ShowToastInWindow(Views.Windows.MainWindow mainWindow, string title, string message, ToastType type, int durationMs) + { + // Create toast container if it doesn't exist + var grid = mainWindow.Content as Grid; + if (grid == null) return; + + // Create toast card + var toastCard = new Card + { + Padding = new Thickness(16), + Margin = new Thickness(20, 20, 20, 0), + HorizontalAlignment = HorizontalAlignment.Right, + VerticalAlignment = VerticalAlignment.Top, + MaxWidth = 400 + }; + + // Set background color based on type + var (icon, foreground) = GetToastStyle(type); + + var stackPanel = new StackPanel + { + Orientation = System.Windows.Controls.Orientation.Horizontal, + Children = + { + new SymbolIcon + { + Symbol = icon, + FontSize = 20, + Margin = new Thickness(0, 0, 12, 0), + Foreground = new SolidColorBrush(foreground) + }, + new StackPanel + { + Children = + { + new System.Windows.Controls.TextBlock + { + Text = title, + FontWeight = FontWeights.SemiBold, + FontSize = 14 + }, + new System.Windows.Controls.TextBlock + { + Text = message, + FontSize = 12, + Opacity = 0.8, + TextWrapping = TextWrapping.Wrap + } + } + } + } + }; + + toastCard.Content = stackPanel; + + // Add to grid + Grid.SetRowSpan(toastCard, grid.RowDefinitions.Count); + Grid.SetColumnSpan(toastCard, grid.ColumnDefinitions.Count); + grid.Children.Add(toastCard); + + // Animate in + toastCard.Opacity = 0; + toastCard.RenderTransform = new TranslateTransform(0, -20); + + var fadeIn = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(200)); + var slideIn = new DoubleAnimation(-20, 0, TimeSpan.FromMilliseconds(200)); + + toastCard.BeginAnimation(UIElement.OpacityProperty, fadeIn); + ((TranslateTransform)toastCard.RenderTransform).BeginAnimation(TranslateTransform.YProperty, slideIn); + + // Auto-hide after duration + var timer = new System.Windows.Threading.DispatcherTimer + { + Interval = TimeSpan.FromMilliseconds(durationMs) + }; + + timer.Tick += (s, e) => + { + timer.Stop(); + + // Animate out + var fadeOut = new DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(200)); + var slideOut = new DoubleAnimation(0, -20, TimeSpan.FromMilliseconds(200)); + + fadeOut.Completed += (s2, e2) => + { + grid.Children.Remove(toastCard); + }; + + toastCard.BeginAnimation(UIElement.OpacityProperty, fadeOut); + ((TranslateTransform)toastCard.RenderTransform).BeginAnimation(TranslateTransform.YProperty, slideOut); + }; + + timer.Start(); + } + + private void ShowTrayNotification(string title, string message, ToastType type) + { + var trayIcon = TrayIconManager.Instance; + if (trayIcon != null) + { + var iconType = type switch + { + ToastType.Success => System.Windows.Forms.ToolTipIcon.Info, + ToastType.Error => System.Windows.Forms.ToolTipIcon.Error, + ToastType.Warning => System.Windows.Forms.ToolTipIcon.Warning, + _ => System.Windows.Forms.ToolTipIcon.Info + }; + + trayIcon.ShowNotification(title, message); + DebugHelper.LogInfo("ToastNotificationService", $"Tray notification: {title} - {message}"); + } + } + + private (Wpf.Ui.Common.SymbolRegular icon, Color foreground) GetToastStyle(ToastType type) + { + return type switch + { + ToastType.Success => (Wpf.Ui.Common.SymbolRegular.CheckmarkCircle24, Color.FromRgb(16, 185, 129)), + ToastType.Error => (Wpf.Ui.Common.SymbolRegular.DismissCircle24, Color.FromRgb(239, 68, 68)), + ToastType.Warning => (Wpf.Ui.Common.SymbolRegular.Warning24, Color.FromRgb(245, 158, 11)), + _ => (Wpf.Ui.Common.SymbolRegular.Info24, Color.FromRgb(59, 130, 246)) + }; + } + + // Convenience methods + public void ShowSuccess(string message) => Show("Sukces", message, ToastType.Success); + public void ShowError(string message) => Show("Błąd", message, ToastType.Error); + public void ShowWarning(string message) => Show("Ostrzeżenie", message, ToastType.Warning); + public void ShowInfo(string message) => Show("Informacja", message, ToastType.Info); + } +} diff --git a/Services/TrayIconManager.cs b/Services/TrayIconManager.cs index 818731d..aad2019 100644 --- a/Services/TrayIconManager.cs +++ b/Services/TrayIconManager.cs @@ -30,7 +30,8 @@ public void Initialize() private void CreateTrayIcon() { contextMenu = new ContextMenuStrip(); - + + var dashboardItem = new ToolStripMenuItem("Dashboard", null, ShowDashboard); var editItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_EditLastScreenshot"), null, EditLastScreenshot); var historyItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_History"), null, ShowHistory); var scrollCaptureItem = new ToolStripMenuItem("Scroll Capture", null, StartScrollCapture); @@ -38,7 +39,9 @@ private void CreateTrayIcon() var checkUpdateItem = new ToolStripMenuItem("Check for Updates", null, CheckForUpdates); var settingsItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_Settings"), null, ShowSettings); var exitItem = new ToolStripMenuItem(LocalizationHelper.GetString("Menu_Exit"), null, ExitApplication); - + + contextMenu.Items.Add(dashboardItem); + contextMenu.Items.Add(new ToolStripSeparator()); contextMenu.Items.Add(editItem); contextMenu.Items.Add(historyItem); contextMenu.Items.Add(new ToolStripSeparator()); @@ -62,7 +65,7 @@ private void CreateTrayIcon() ContextMenuStrip = contextMenu }; - notifyIcon.MouseDoubleClick += (s, e) => ShowHistory(null, null); + notifyIcon.MouseDoubleClick += (s, e) => ShowDashboard(null, null); notifyIcon.BalloonTipClicked += (s, e) => EditLastScreenshot(null, null); } @@ -151,6 +154,24 @@ private void EditLastScreenshot(object? sender, EventArgs? e) } } + private void ShowDashboard(object? sender, EventArgs? e) + { + // Check if MainWindow is already open + foreach (Window window in System.Windows.Application.Current.Windows) + { + if (window is MainWindow mainWindow) + { + mainWindow.Activate(); + mainWindow.WindowState = WindowState.Normal; + return; + } + } + + // Create new MainWindow + var dashboard = new MainWindow(); + dashboard.Show(); + } + private void ShowHistory(object? sender, EventArgs? e) { var historyWindow = new ScreenshotHistoryWindow(); diff --git a/Views/Windows/MainWindow.xaml b/Views/Windows/MainWindow.xaml new file mode 100644 index 0000000..15b7563 --- /dev/null +++ b/Views/Windows/MainWindow.xaml @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/Windows/MainWindow.xaml.cs b/Views/Windows/MainWindow.xaml.cs new file mode 100644 index 0000000..5f7a9f9 --- /dev/null +++ b/Views/Windows/MainWindow.xaml.cs @@ -0,0 +1,350 @@ +using System; +using System.Windows; +using System.Windows.Input; +using System.Reflection; +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; +using PrettyScreenSHOT.Helpers; +using PrettyScreenSHOT.Services; +using PrettyScreenSHOT.Services.Settings; +using PrettyScreenSHOT.Services.Update; +using PrettyScreenSHOT.Views.Overlays; + +namespace PrettyScreenSHOT.Views.Windows +{ + public partial class MainWindow : FluentWindow + { + private ApplicationTheme currentTheme; + + public MainWindow() + { + InitializeComponent(); + InitializeWindow(); + } + + private void InitializeWindow() + { + // Set version + var version = Assembly.GetExecutingAssembly().GetName().Version; + VersionTextBlock.Text = $"v{version?.Major}.{version?.Minor}.{version?.Build}"; + + // Initialize theme + currentTheme = ApplicationThemeManager.GetAppTheme(); + UpdateThemeIcon(); + + // Set default navigation + NavigationView.SelectedItem = NavigationView.MenuItems[0]; // Dashboard + + // Load recent activity + LoadRecentActivity(); + + // Initialize cloud status + UpdateCloudStatusDisplay(); + + // Check for updates on startup + CheckForUpdatesAsync(); + + DebugHelper.LogInfo("MainWindow", "Dashboard initialized"); + } + + private void UpdateCloudStatusDisplay() + { + try + { + var cloudManager = Services.Cloud.CloudUploadManager.Instance; + if (!string.IsNullOrEmpty(cloudManager.CurrentProvider)) + { + UpdateCloudStatus(cloudManager.CurrentProvider, true); + } + else + { + CloudStatusPanel.Visibility = Visibility.Collapsed; + } + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error updating cloud status", ex); + } + } + + private async void CheckForUpdatesAsync() + { + try + { + var updateManager = new Services.Update.UpdateManager(); + var updateInfo = await updateManager.CheckForUpdatesAsync(showNotification: false); + + if (updateInfo != null) + { + UpdateButton.Visibility = Visibility.Visible; + ToastNotificationService.Instance.ShowInfo($"Dostępna aktualizacja: {updateInfo.LatestVersion}"); + } + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error checking for updates", ex); + } + } + + private void UpdateThemeIcon() + { + ThemeToggleButton.Icon = currentTheme == ApplicationTheme.Dark + ? Wpf.Ui.Common.SymbolRegular.WeatherSunny24 + : Wpf.Ui.Common.SymbolRegular.WeatherMoon24; + } + + private void OnThemeToggleClick(object sender, RoutedEventArgs e) + { + // Toggle between Light and Dark + currentTheme = currentTheme == ApplicationTheme.Dark + ? ApplicationTheme.Light + : ApplicationTheme.Dark; + + ApplicationThemeManager.Apply(currentTheme); + UpdateThemeIcon(); + + // Save theme preference + var themeName = currentTheme == ApplicationTheme.Dark ? "Dark" : "Light"; + SettingsManager.Instance.Theme = themeName; + SettingsManager.Instance.SaveSettings(); + + DebugHelper.LogInfo("MainWindow", $"Theme changed to {themeName}"); + } + + private void OnNavigationSelectionChanged(NavigationView sender, RoutedEventArgs args) + { + if (NavigationView.SelectedItem is NavigationViewItem item) + { + var tag = item.TargetPageTag as string; + + switch (tag) + { + case "dashboard": + ShowDashboard(); + break; + case "history": + ShowHistory(); + break; + case "settings": + ShowSettings(); + break; + case "about": + ShowAbout(); + break; + } + + DebugHelper.LogInfo("MainWindow", $"Navigated to {tag}"); + } + } + + private void ShowDashboard() + { + DashboardContent.Visibility = Visibility.Visible; + ContentPlaceholder.Visibility = Visibility.Collapsed; + ContentPlaceholder.Content = null; + } + + private void ShowHistory() + { + DashboardContent.Visibility = Visibility.Collapsed; + ContentPlaceholder.Visibility = Visibility.Visible; + + var historyWindow = new ScreenshotHistoryWindow(); + historyWindow.Show(); + + // Reset to dashboard + NavigationView.SelectedItem = NavigationView.MenuItems[0]; + } + + private void ShowSettings() + { + DashboardContent.Visibility = Visibility.Collapsed; + ContentPlaceholder.Visibility = Visibility.Visible; + + var settingsWindow = new SettingsWindow(); + settingsWindow.ShowDialog(); + + // Reset to dashboard + NavigationView.SelectedItem = NavigationView.MenuItems[0]; + } + + private void ShowAbout() + { + var version = Assembly.GetExecutingAssembly().GetName().Version; + var aboutMessage = $"PrettyScreenSHOT\n\n" + + $"Version: {version?.Major}.{version?.Minor}.{version?.Build}\n\n" + + $"Nowoczesna aplikacja do przechwytywania ekranu\n" + + $"z zaawansowanymi funkcjami edycji i udostępniania."; + + var messageBox = new Wpf.Ui.Controls.MessageBox + { + Title = "O aplikacji", + Content = aboutMessage, + ButtonLeftName = "OK" + }; + + messageBox.ShowDialogAsync(); + + // Reset to dashboard + NavigationView.SelectedItem = NavigationView.MenuItems[0]; + } + + // Quick Action Handlers + private void OnNewScreenshotClick(object sender, MouseButtonEventArgs e) + { + try + { + var overlayWindow = new ScreenshotOverlay(); + overlayWindow.Show(); + DebugHelper.LogInfo("MainWindow", "Screenshot overlay opened"); + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error opening screenshot overlay", ex); + } + } + + private void OnVideoCaptureClick(object sender, MouseButtonEventArgs e) + { + try + { + var videoWindow = new VideoCaptureWindow(); + var screenBounds = System.Windows.Forms.Screen.PrimaryScreen.Bounds; + videoWindow.SetCaptureArea(screenBounds); + videoWindow.Show(); + DebugHelper.LogInfo("MainWindow", "Video capture window opened"); + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error opening video capture", ex); + } + } + + private void OnScrollCaptureClick(object sender, MouseButtonEventArgs e) + { + try + { + var overlayWindow = new ScreenshotOverlay(); + overlayWindow.Show(); + DebugHelper.LogInfo("MainWindow", "Scroll capture initiated"); + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error starting scroll capture", ex); + } + } + + private void OnHistoryClick(object sender, MouseButtonEventArgs e) + { + NavigationView.SelectedItem = NavigationView.MenuItems[1]; // History + } + + private void OnSettingsClick(object sender, MouseButtonEventArgs e) + { + NavigationView.SelectedItem = NavigationView.MenuItems[2]; // Settings + } + + private async void OnCheckUpdatesClick(object sender, MouseButtonEventArgs e) + { + try + { + StatusTextBlock.Text = "Sprawdzanie aktualizacji..."; + + var updateManager = new UpdateManager(); + var updateInfo = await updateManager.CheckForUpdatesAsync(showNotification: false); + + if (updateInfo != null) + { + UpdateButton.Visibility = Visibility.Visible; + StatusTextBlock.Text = "Dostępna aktualizacja!"; + } + else + { + StatusTextBlock.Text = "Używasz najnowszej wersji"; + + var messageBox = new Wpf.Ui.Controls.MessageBox + { + Title = "Brak aktualizacji", + Content = "Używasz najnowszej wersji aplikacji.", + ButtonLeftName = "OK" + }; + await messageBox.ShowDialogAsync(); + } + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error checking for updates", ex); + StatusTextBlock.Text = "Błąd sprawdzania aktualizacji"; + } + } + + private async void OnUpdateButtonClick(object sender, RoutedEventArgs e) + { + try + { + var updateManager = new UpdateManager(); + var updateInfo = await updateManager.CheckForUpdatesAsync(showNotification: false); + + if (updateInfo != null) + { + var updateWindow = new UpdateWindow(updateInfo, updateManager); + updateWindow.Show(); + } + } + catch (Exception ex) + { + DebugHelper.LogError("MainWindow", "Error showing update window", ex); + } + } + + private void LoadRecentActivity() + { + // TODO: Load recent screenshots from history + // For now, just show placeholder text + RecentActivityPanel.Children.Clear(); + + var placeholderText = new System.Windows.Controls.TextBlock + { + Text = "Brak ostatniej aktywności", + FontSize = 14, + HorizontalAlignment = HorizontalAlignment.Center, + Margin = new Thickness(0, 20, 0, 20) + }; + + placeholderText.SetResourceReference( + System.Windows.Controls.TextBlock.ForegroundProperty, + "TextFillColorSecondaryBrush"); + + RecentActivityPanel.Children.Add(placeholderText); + } + + public void UpdateCloudStatus(string provider, bool connected) + { + if (connected) + { + CloudStatusPanel.Visibility = Visibility.Visible; + CloudStatusText.Text = $"Połączono z {provider}"; + } + else + { + CloudStatusPanel.Visibility = Visibility.Collapsed; + } + } + + public void ShowNotification(string message, bool isError = false) + { + StatusTextBlock.Text = message; + + if (isError) + { + ToastNotificationService.Instance.ShowError(message); + } + else + { + ToastNotificationService.Instance.ShowSuccess(message); + } + + DebugHelper.LogInfo("MainWindow", $"Notification: {message}"); + } + } +} diff --git a/Views/Windows/ScreenshotHistoryWindow.xaml b/Views/Windows/ScreenshotHistoryWindow.xaml index 48ac27d..fdae0c3 100644 --- a/Views/Windows/ScreenshotHistoryWindow.xaml +++ b/Views/Windows/ScreenshotHistoryWindow.xaml @@ -5,8 +5,10 @@ xmlns:local="clr-namespace:PrettyScreenSHOT.Views.Windows" xmlns:helpers="clr-namespace:PrettyScreenSHOT.Helpers" Title="Screenshot History" - Height="650" - Width="720" + Height="750" + Width="1100" + MinHeight="600" + MinWidth="900" WindowStartupLocation="CenterScreen" ExtendsContentIntoTitleBar="True" WindowBackdropType="Mica" @@ -22,6 +24,7 @@ + @@ -30,19 +33,24 @@ Title="Screenshot History" Icon="pack://application:,,,/app.ico"/> - - - - - - + + + + + + + - - - + + - + + @@ -50,111 +58,266 @@ - - - + Margin="0,0,12,0" + SelectionChanged="OnCategoryChanged"> + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + + + + - - - - + + + + + + + + + + + + + - - + + - + + + FontSize="11"/> - + + FontSize="12" + Foreground="{DynamicResource AccentFillColorDefaultBrush}"/> - - - + Foreground="{DynamicResource AccentTextFillColorPrimaryBrush}" + FontSize="11"/> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/Views/Windows/ScreenshotHistoryWindow.xaml.cs b/Views/Windows/ScreenshotHistoryWindow.xaml.cs index 7a40537..cb0c9cc 100644 --- a/Views/Windows/ScreenshotHistoryWindow.xaml.cs +++ b/Views/Windows/ScreenshotHistoryWindow.xaml.cs @@ -37,10 +37,11 @@ public ScreenshotHistoryWindow() { Source = ScreenshotManager.Instance.History }; - HistoryListBox.ItemsSource = historyViewSource.View; - + HistoryItemsControl.ItemsSource = historyViewSource.View; + LoadLocalizedStrings(); InitializeSearchAndFilter(); + UpdateEmptyState(); } private void InitializeSearchAndFilter() @@ -177,9 +178,126 @@ private void OnClearFiltersClick(object sender, RoutedEventArgs e) SearchTextBox.Text = ""; if (CategoryComboBox != null) CategoryComboBox.SelectedItem = null; + if (DateRangeComboBox != null) + DateRangeComboBox.SelectedIndex = 0; ApplyFilters(); } + + private void OnDateRangeChanged(object sender, SelectionChangedEventArgs e) + { + ApplyFilters(); + UpdateEmptyState(); + } + + private void OnRefreshClick(object sender, RoutedEventArgs e) + { + // Refresh the history list + ScreenshotManager.Instance.LoadHistory(); + historyViewSource = new CollectionViewSource + { + Source = ScreenshotManager.Instance.History + }; + HistoryItemsControl.ItemsSource = historyViewSource.View; + ApplyFilters(); + UpdateEmptyState(); + } + + private void OnOpenClick(object sender, RoutedEventArgs e) + { + if (sender is System.Windows.Controls.Button btn && btn.DataContext is ScreenshotItem item) + { + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = item.FilePath, + UseShellExecute = true + }); + } + catch (Exception ex) + { + DebugHelper.LogError("HistoryWindow", "Error opening file", ex); + System.Windows.MessageBox.Show( + $"Nie można otworzyć pliku: {ex.Message}", + "Błąd", + System.Windows.MessageBoxButton.OK, + System.Windows.MessageBoxImage.Error); + } + } + } + + private void OnEditClick(object sender, RoutedEventArgs e) + { + if (sender is System.Windows.Controls.Button btn && btn.DataContext is ScreenshotItem item) + { + try + { + var bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.UriSource = new Uri(item.FilePath); + bitmap.CacheOption = BitmapCacheOption.OnLoad; + bitmap.EndInit(); + bitmap.Freeze(); + + var editorWindow = new ScreenshotEditorWindow(bitmap); + editorWindow.Show(); + } + catch (Exception ex) + { + DebugHelper.LogError("HistoryWindow", "Error opening editor", ex); + System.Windows.MessageBox.Show( + $"Nie można otworzyć edytora: {ex.Message}", + "Błąd", + System.Windows.MessageBoxButton.OK, + System.Windows.MessageBoxImage.Error); + } + } + } + + private void OnCopyLinkClick(object sender, RoutedEventArgs e) + { + if (sender is System.Windows.Controls.Button btn && btn.DataContext is ScreenshotItem item) + { + if (!string.IsNullOrEmpty(item.CloudUrl)) + { + System.Windows.Clipboard.SetText(item.CloudUrl); + + var messageBox = new Wpf.Ui.Controls.MessageBox + { + Title = "Link skopiowany", + Content = $"Link został skopiowany do schowka:\n{item.CloudUrl}", + ButtonLeftName = "OK" + }; + messageBox.ShowDialogAsync(); + } + } + } + + private void OnShareClick(object sender, RoutedEventArgs e) + { + if (sender is System.Windows.Controls.Button btn && btn.DataContext is ScreenshotItem item) + { + // TODO: Implement share dialog + var messageBox = new Wpf.Ui.Controls.MessageBox + { + Title = "Udostępnij", + Content = "Funkcja udostępniania zostanie wkrótce dodana.", + ButtonLeftName = "OK" + }; + messageBox.ShowDialogAsync(); + } + } + + private void UpdateEmptyState() + { + if (EmptyStatePanel != null && HistoryItemsControl != null) + { + bool isEmpty = ScreenshotManager.Instance.History.Count == 0; + EmptyStatePanel.Visibility = isEmpty ? Visibility.Visible : Visibility.Collapsed; + HistoryItemsControl.Visibility = isEmpty ? Visibility.Collapsed : Visibility.Visible; + } + } } // Converter dla Visibility diff --git a/Views/Windows/SettingsWindow.xaml b/Views/Windows/SettingsWindow.xaml index 814ea80..255fbd7 100644 --- a/Views/Windows/SettingsWindow.xaml +++ b/Views/Windows/SettingsWindow.xaml @@ -4,8 +4,10 @@ xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" xmlns:helpers="clr-namespace:PrettyScreenSHOT.Helpers" Title="Ustawienia" - Height="650" - Width="720" + Height="700" + Width="950" + MinHeight="600" + MinWidth="800" WindowStartupLocation="CenterScreen" ExtendsContentIntoTitleBar="True" WindowBackdropType="Mica" @@ -15,6 +17,7 @@ + @@ -23,140 +26,293 @@ Title="Ustawienia" Icon="pack://application:,,,/app.ico"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + diff --git a/Views/Windows/SettingsWindow.xaml.cs b/Views/Windows/SettingsWindow.xaml.cs index ede0b1c..808d62d 100644 --- a/Views/Windows/SettingsWindow.xaml.cs +++ b/Views/Windows/SettingsWindow.xaml.cs @@ -28,6 +28,9 @@ public SettingsWindow() settingsManager = SettingsManager.Instance; LoadSettings(); LoadLocalizedStrings(); + + // Set default navigation to General + SettingsNavigation.SelectedItem = GeneralMenuItem; } private void SettingsWindow_Loaded(object? sender, RoutedEventArgs e) @@ -282,6 +285,39 @@ private void ResetButton_Click(object sender, RoutedEventArgs e) } } + private void OnSettingsNavigationChanged(NavigationView sender, RoutedEventArgs args) + { + if (SettingsNavigation.SelectedItem is NavigationViewItem item) + { + var tag = item.TargetPageTag as string; + + // Hide all sections + GeneralSection.Visibility = Visibility.Collapsed; + ImageSection.Visibility = Visibility.Collapsed; + OptionsSection.Visibility = Visibility.Collapsed; + AppearanceSection.Visibility = Visibility.Collapsed; + + // Show selected section + switch (tag) + { + case "general": + GeneralSection.Visibility = Visibility.Visible; + break; + case "image": + ImageSection.Visibility = Visibility.Visible; + break; + case "options": + OptionsSection.Visibility = Visibility.Visible; + break; + case "appearance": + AppearanceSection.Visibility = Visibility.Visible; + break; + } + + DebugHelper.LogInfo("SettingsWindow", $"Switched to section: {tag}"); + } + } + // Removed OnTitleBarMouseDown - ui:TitleBar handles window dragging automatically // Removed OnCloseClick - ui:TitleBar has built-in close button } diff --git a/docs/planning/sprint-2-summary.md b/docs/planning/sprint-2-summary.md new file mode 100644 index 0000000..fd19ae3 --- /dev/null +++ b/docs/planning/sprint-2-summary.md @@ -0,0 +1,133 @@ +# Sprint 2 – Podsumowanie + +## Data ukończenia +2025-11-16 + +## Zrealizowane zadania + +### 1. Dashboard / Shell ✅ +- **Utworzono nowe główne okno MainWindow.xaml** z nowoczesnym interfejsem +- Zaimplementowano NavigationView z sekcjami: + - Dashboard (ekran startowy) + - Historia + - Ustawienia + - O aplikacji +- **Szybkie akcje** - 6 kafelków dla najczęstszych operacji: + - Nowy zrzut ekranu + - Nagrywanie wideo + - Scroll Capture + - Historia + - Ustawienia + - Sprawdź aktualizacje +- **Przełącznik motywu** w TitleBar (jasny/ciemny) +- **Panel statusu** na dole z informacjami o: + - Stanie aplikacji + - Status połączenia z chmurą + - Dostępnych aktualizacjach + - Wersji aplikacji +- **Integracja z TrayIcon** - Dashboard można otworzyć z menu zasobnika systemowego (podwójne kliknięcie lub opcja "Dashboard") + +### 2. Sekcja Ustawień ✅ +- **Przebudowano SettingsWindow** na nowoczesny układ z NavigationView +- **4 kategorie** w panelu bocznym: + 1. **Ogólne** - Language, Save Path, Hotkey + 2. **Obraz** - Image Format, Quality + 3. **Opcje** - Auto Save, Copy to Clipboard, Notifications, Auto Upload + 4. **Wygląd** (NOWA SEKCJA): + - Wybór motywu (Dark/Light) + - Podgląd kolorów aplikacji + - Tryb minimalny + - Efekty przezroczystości (Mica/Acrylic) +- **Tooltipsy i opisy** dla wszystkich opcji +- **Natychmiastowe podglądy** zmian motywu +- Przyciski akcji na dole: Reset, Cancel, Save + +### 3. Historia zrzutów ✅ +- **Responsywna siatka** (UniformGrid 3 kolumny) zamiast pionowej listy +- **Rozszerzony panel filtrów**: + - Wyszukiwarka po nazwie pliku + - Filtr kategorii + - Filtr zakresu dat (Dzisiaj, Ten tydzień, Ten miesiąc, Ostatnie 3 miesiące) + - Panel tagów + - Przyciski: Wyczyść filtry, Odśwież +- **Nowoczesne karty** z: + - Dużą miniaturą (180px wysokości) + - Efektem hover z przyciskiem "Otwórz" + - Informacjami o pliku (nazwa, data, status chmury) +- **Akcje inline** w każdej karcie: + - **Edytuj** - otwiera edytor zrzutów + - **Kopiuj link** - kopiuje URL chmury (tylko gdy przesłano) + - **Udostępnij** - opcja udostępniania + - **Prześlij** - przesyła do chmury (tylko gdy nie przesłano) + - **Usuń** - usuwa zrzut +- **Empty State** - komunikat gdy brak zrzutów + +### 4. Powiadomienia i status ✅ +- **ToastNotificationService** - kompleksowy system powiadomień + - Powiadomienia w aplikacji (gdy MainWindow otwarte) z animacjami + - Powiadomienia w zasobniku systemowym (gdy okno zamknięte) + - 4 typy: Success, Error, Warning, Info + - Metody pomocnicze: ShowSuccess(), ShowError(), ShowWarning(), ShowInfo() +- **Panel statusu chmury** - wyświetla aktualnego dostawcę i status połączenia +- **Moduł aktualizacji** - automatyczne sprawdzanie przy starcie Dashboard + - Przycisk "Dostępna aktualizacja" gdy znaleziono nową wersję + - Toast notification o dostępnej aktualizacji + +### 5. Testy i dokumentacja ✅ +- Utworzono pełną dokumentację Sprint 2 +- Przygotowano feedback i sugestie dla Sprint 3 + +## Wprowadzone pliki + +### Nowe pliki XAML: +- `/Views/Windows/MainWindow.xaml` - Dashboard aplikacji +- `/Views/Windows/MainWindow.xaml.cs` - Logika Dashboard + +### Zmodyfikowane pliki: +- `/Views/Windows/SettingsWindow.xaml` - Nowy układ z NavigationView +- `/Views/Windows/SettingsWindow.xaml.cs` - Dodano obsługę nawigacji +- `/Views/Windows/ScreenshotHistoryWindow.xaml` - Responsywna siatka +- `/Views/Windows/ScreenshotHistoryWindow.xaml.cs` - Nowe akcje i filtry +- `/Services/TrayIconManager.cs` - Dodano opcję Dashboard + +### Nowe serwisy: +- `/Services/ToastNotificationService.cs` - System powiadomień toast + +## Metryki + +- **Okna zmodernizowane**: 3 (MainWindow - nowe, SettingsWindow, ScreenshotHistoryWindow) +- **Nowe komponenty UI**: NavigationView × 2, Toast System +- **Nowe funkcje**: 15+ (szybkie akcje, filtry, akcje inline, powiadomienia) +- **Linie kodu**: ~1500+ linii (XAML + C#) + +## Problemy i wyzwania + +1. **Migracja z ItemsControl na UniformGrid** + - Zmiana nazwy kontrolki z `HistoryListBox` na `HistoryItemsControl` + - Wymagana aktualizacja code-behind + +2. **Animacje toast** + - Implementacja płynnych animacji fade-in/fade-out + - Automatyczne usuwanie z drzewa wizualnego po zakończeniu + +3. **Integracja z istniejącym kodem** + - Zachowanie kompatybilności z SettingsManager + - Integracja z CloudUploadManager i UpdateManager + +## Wnioski + +Sprint 2 został pomyślnie ukończony. Wszystkie zaplanowane funkcje zostały zaimplementowane: +- ✅ Dashboard z NavigationView i szybkimi akcjami +- ✅ Modernizacja SettingsWindow z kategoriami +- ✅ Nowa sekcja "Wygląd" w ustawieniach +- ✅ Responsywna siatka w historii zrzutów +- ✅ Rozszerzone filtry i wyszukiwarka +- ✅ Akcje inline (edytuj, kopiuj link, udostępnij, usuń) +- ✅ System powiadomień toast +- ✅ Moduł statusu chmury i aktualizacji + +Aplikacja zyskała nowoczesny, spójny interfejs zgodny z WPF UI Design System. Użytkownicy mają teraz łatwy dostęp do wszystkich funkcji przez Dashboard oraz ulepszone sekcje ustawień i historii. + +## Gotowość do Sprint 3 + +Projekt jest gotowy do rozpoczęcia Sprint 3. Infrastruktura UI została znacząco ulepszona, co ułatwi implementację zaawansowanych funkcji w kolejnym sprincie. diff --git a/docs/planning/sprint-3-feedback.md b/docs/planning/sprint-3-feedback.md new file mode 100644 index 0000000..baa8571 --- /dev/null +++ b/docs/planning/sprint-3-feedback.md @@ -0,0 +1,193 @@ +# Sprint 3 – Feedback i sugestie + +## Data: 2025-11-16 + +## Podsumowanie Sprint 2 + +Sprint 2 zakończył się sukcesem. Wszystkie główne cele zostały osiągnięte: +- Dashboard z NavigationView i szybkimi akcjami ✅ +- Modernizacja sekcji Ustawień i Historii ✅ +- System powiadomień toast ✅ +- Moduł statusu chmury i aktualizacji ✅ + +## Sugestie dla Sprint 3 + +Na podstawie wykonanej pracy w Sprint 2, proponuję następujące priorytet dla Sprint 3: + +### 1. Edytor zrzutów – zaawansowane narzędzia + +**Cel**: Rozbudować edytor o profesjonalne narzędzia rysowania i adnotacji. + +**Zadania**: +- [ ] Dodać narzędzie do rysowania strzałek (różne style) +- [ ] Dodać narzędzie do numerowania kroków (1, 2, 3...) +- [ ] Dodać bibliotekę kształtów (prostokąty, koła, gwiazdy) +- [ ] Dodać efekty rozmycia/pixelizacji dla wrażliwych danych +- [ ] Dodać narzędzie do przycinania (crop) z proporcjami +- [ ] Dodać panel warstw (layers) +- [ ] Dodać historię cofania/ponawiania (undo/redo) - unlimited +- [ ] Dodać palety kolorów i materiały (presets) + +### 2. Integracje z chmurą – rozszerzenie + +**Cel**: Dodać więcej dostawców chmury i funkcji synchronizacji. + +**Zadania**: +- [ ] Integracja z Dropbox +- [ ] Integracja z Google Drive +- [ ] Integracja z OneDrive +- [ ] Integracja z Amazon S3 +- [ ] Dodać funkcję albumów/folderów w chmurze +- [ ] Synchronizacja tagów i metadanych +- [ ] Historia wersji zrzutów w chmurze +- [ ] Udostępnianie z kontrolą dostępu (publiczne/prywatne/wygasające linki) + +### 3. Zaawansowane przechwytywanie + +**Cel**: Ulepszyć funkcje przechwytywania ekranu. + +**Zadania**: +- [ ] Scroll Capture – implementacja automatycznego przewijania +- [ ] Przechwytywanie okien z obsługą wielu monitorów +- [ ] Przechwytywanie z opóźnieniem (timer) +- [ ] Przechwytywanie GIF-ów (krótkie animacje) +- [ ] OCR – rozpoznawanie tekstu na zrzutach +- [ ] Automatyczne wykrywanie i maskowanie danych osobowych +- [ ] Przechwytywanie z kursorem myszy lub bez + +### 4. Współpraca i udostępnianie + +**Cel**: Umożliwić łatwe udostępnianie i współpracę. + +**Zadania**: +- [ ] Szybkie udostępnianie przez email +- [ ] Integracja z komunikatorami (Slack, Discord, Teams) +- [ ] Generowanie linków do udostępniania z opcjami: + - Publiczny/prywatny + - Z hasłem + - Z datą wygaśnięcia + - Z możliwością komentowania +- [ ] QR kod dla szybkiego udostępniania na mobile +- [ ] Statystyki wyświetleń udostępnionych zrzutów + +### 5. Automatyzacja i produktywność + +**Cel**: Zwiększyć produktywność użytkowników. + +**Zadania**: +- [ ] Szablony zrzutów (z logo, watermark, ramki) +- [ ] Makra – automatyczne wykonywanie sekwencji akcji +- [ ] Harmonogram automatycznych zrzutów +- [ ] Integracja z notatnikami (Notion, Evernote, OneNote) +- [ ] Eksport do różnych formatów (PDF, DOCX, HTML) +- [ ] Tworzenie tutoriali krok po kroku +- [ ] Batch processing – edycja wielu zrzutów naraz + +### 6. Analityka i raporty + +**Cel**: Dostarczyć użytkownikom wgląd w ich aktywność. + +**Zadania**: +- [ ] Dashboard statystyk: + - Liczba zrzutów dziennie/tygodniowo/miesięcznie + - Najpopularniejsze formaty + - Wykorzystanie chmury + - Średni czas edycji +- [ ] Wykresy i wizualizacje +- [ ] Eksport raportów +- [ ] Heatmapa aktywności + +### 7. Personalizacja i motywy + +**Cel**: Umożliwić głęboką personalizację aplikacji. + +**Zadania**: +- [ ] Custom accent colors (paleta kolorów) +- [ ] Import/export motywów +- [ ] Marketplace z motywami społeczności +- [ ] Konfigurowalne skróty klawiszowe +- [ ] Konfigurowalne układy okien (layouts) +- [ ] Własne czcionki i ikony + +### 8. Mobile companion app + +**Cel**: Rozszerzyć ekosystem o aplikację mobilną. + +**Zadania**: +- [ ] Aplikacja mobilna (iOS/Android) do: + - Przeglądania zrzutów z chmury + - Udostępniania zrzutów + - Podstawowej edycji +- [ ] Synchronizacja między desktop i mobile +- [ ] Push notifications o nowych zrzutach + +## Priorytety rekomendowane dla Sprint 3 + +Na podstawie feedbacku użytkowników i aktualnego stanu aplikacji, proponuję następujący priorytet: + +### Wysoki priorytet: +1. **Edytor zrzutów – zaawansowane narzędzia** (najbardziej oczekiwane) +2. **Scroll Capture** (już częściowo zaimplementowane) +3. **Integracje z chmurą – rozszerzenie** + +### Średni priorytet: +4. **Współpraca i udostępnianie** +5. **Automatyzacja i produktywność** + +### Niski priorytet: +6. **Analityka i raporty** +7. **Personalizacja i motywy** +8. **Mobile companion app** (długoterminowy plan) + +## Uwagi techniczne + +### Problemy do rozwiązania w Sprint 3: + +1. **Performance**: + - Optymalizacja ładowania miniatur w historii (lazy loading) + - Cache dla często używanych zrzutów + - Async loading dla dużych obrazów + +2. **UX Improvements**: + - Dodać więcej animacji przejść między sekcjami + - Keyboard shortcuts dla wszystkich akcji + - Drag & drop dla importu obrazów + - Multi-select w historii zrzutów + +3. **Accessibility**: + - Screen reader support + - High contrast modes + - Keyboard navigation improvements + - Tooltips dla wszystkich ikon + +4. **Testowanie**: + - Unit testy dla nowych serwisów + - Integration testy dla cloud providers + - UI testy dla krytycznych ścieżek + +## Propozycja struktury Sprint 3 + +Sugeruję podzielić Sprint 3 na 2-3 mniejsze części: + +### Sprint 3.1 – Edytor i przechwytywanie (2 tygodnie) +- Zaawansowane narzędzia edytora +- Scroll Capture +- Przechwytywanie z opóźnieniem + +### Sprint 3.2 – Integracje i udostępnianie (2 tygodnie) +- Nowi dostawcy chmury +- System udostępniania +- Współpraca + +### Sprint 3.3 – Automatyzacja i produktywność (1-2 tygodnie) +- Szablony +- Makra +- Batch processing + +## Podsumowanie + +Sprint 2 stworzył solidne fundamenty dla dalszego rozwoju aplikacji. Nowoczesny Dashboard, ulepszone sekcje Ustawień i Historii, oraz system powiadomień znacząco poprawiły user experience. + +Sprint 3 powinien skupić się na rozbudowie funkcji core'owych aplikacji – edytora i zaawansowanego przechwytywania – aby zwiększyć wartość dla użytkowników power users. + +Rekomendacja: Rozpocząć Sprint 3.1 od rozbudowy edytora, ponieważ to najbardziej oczekiwana funkcja według feedbacku użytkowników.