diff --git a/Tests/ScreenLayoutTests.cs b/Tests/ScreenLayoutTests.cs index c095a3f3..d306134c 100644 --- a/Tests/ScreenLayoutTests.cs +++ b/Tests/ScreenLayoutTests.cs @@ -1,4 +1,5 @@ -using System.Windows; +using Dapplo.Windows.User32; +using System.Windows; using Text_Grab; namespace Tests; @@ -92,7 +93,7 @@ public void SmallRectanglesContained() double smallLeft2 = display2.CenterPoint().X - (sideLength / 2); double smallTop2 = display2.CenterPoint().Y - (sideLength / 2); Rect smallRect2 = new(smallLeft2, smallTop2, sideLength, sideLength); - + Assert.True(display2.Contains(smallRect2)); Assert.False(display1.Contains(smallRect2)); Assert.False(display3.Contains(smallRect2)); @@ -121,7 +122,7 @@ public void SmallRectanglesContained456() double smallLeft5 = display5.CenterPoint().X - (sideLength / 2); double smallTop5 = display5.CenterPoint().Y - (sideLength / 2); Rect smallRect5 = new(smallLeft5, smallTop5, sideLength, sideLength); - + Assert.True(display5.Contains(smallRect5)); Assert.False(display4.Contains(smallRect5)); Assert.False(display6.Contains(smallRect5)); @@ -134,4 +135,26 @@ public void SmallRectanglesContained456() Assert.False(display4.Contains(smallRect6)); Assert.False(display5.Contains(smallRect6)); } + + + [Fact] + public void CompareDapploToWinForms() + { + DisplayInfo[] dapploDisplays = Dapplo.Windows.User32.DisplayInfo.AllDisplayInfos; + + System.Windows.Forms.Screen[] winFormsDisplays = System.Windows.Forms.Screen.AllScreens; + + Assert.Equal(dapploDisplays.Length, winFormsDisplays.Length); + + for (int i = 0; i < dapploDisplays.Length; i++) + { + Rect dapploRect = dapploDisplays[i].Bounds; + Rect winFormsRect = winFormsDisplays[i].Bounds.AsRect(); + + Point dapploCenterPoint = dapploRect.CenterPoint(); + Point winFormsCenterPoint = winFormsRect.CenterPoint(); + + Assert.Equal(dapploCenterPoint, winFormsCenterPoint); + } + } } diff --git a/Text-Grab/Extensions/DapploExtensions.cs b/Text-Grab/Extensions/DapploExtensions.cs new file mode 100644 index 00000000..470a6094 --- /dev/null +++ b/Text-Grab/Extensions/DapploExtensions.cs @@ -0,0 +1,31 @@ +using Dapplo.Windows.User32; +using System.Windows; + +namespace Text_Grab.Extensions; +public static class DapploExtensions +{ + public static Point ScaledCenterPoint(this DisplayInfo displayInfo) + { + Rect displayRect = displayInfo.Bounds; + NativeMethods.GetScaleFactorForMonitor(displayInfo.MonitorHandle, out uint scaleFactor); + double scaleFraction = scaleFactor / 100.0; + Point rawCenter = displayRect.CenterPoint(); + Point displayScaledCenterPoint = new(rawCenter.X / scaleFraction, rawCenter.Y / scaleFraction); + return displayScaledCenterPoint; + } + + public static Rect ScaledBounds(this DisplayInfo displayInfo) + { + Rect displayRect = displayInfo.Bounds; + NativeMethods.GetScaleFactorForMonitor(displayInfo.MonitorHandle, out uint scaleFactor); + double scaleFraction = scaleFactor / 100.0; + + // Scale size and position + Rect scaledBounds = new( + displayRect.X / scaleFraction, + displayRect.Y / scaleFraction, + displayRect.Width / scaleFraction, + displayRect.Height / scaleFraction); + return scaledBounds; + } +} diff --git a/Text-Grab/NativeMethods.cs b/Text-Grab/NativeMethods.cs index c12e50c6..a1feff36 100644 --- a/Text-Grab/NativeMethods.cs +++ b/Text-Grab/NativeMethods.cs @@ -19,4 +19,7 @@ internal static partial class NativeMethods [LibraryImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static partial bool GetKeyboardState(byte[] keyState); + + [LibraryImport("shcore.dll")] + public static partial void GetScaleFactorForMonitor(IntPtr hMon, out uint pScale); } \ No newline at end of file diff --git a/Text-Grab/Text-Grab.csproj b/Text-Grab/Text-Grab.csproj index 11679457..870120ac 100644 --- a/Text-Grab/Text-Grab.csproj +++ b/Text-Grab/Text-Grab.csproj @@ -68,11 +68,11 @@ + - diff --git a/Text-Grab/Utilities/WindowUtilities.cs b/Text-Grab/Utilities/WindowUtilities.cs index 583d48f6..7437ffa8 100644 --- a/Text-Grab/Utilities/WindowUtilities.cs +++ b/Text-Grab/Utilities/WindowUtilities.cs @@ -1,14 +1,13 @@ -using System; +using Dapplo.Windows.User32; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using Text_Grab.Properties; +using Text_Grab.Extensions; using Text_Grab.Views; -// using Screen = System.Windows.Forms.Screen; -using WpfScreenHelper; using static OSInterop; namespace Text_Grab.Utilities; @@ -47,15 +46,18 @@ public static void SetWindowPosition(Window passedWindow) couldParseAll = double.TryParse(storedPosition[2], out double parsedWid); couldParseAll = double.TryParse(storedPosition[3], out double parsedHei); Rect storedSize = new((int)parsedX, (int)parsedY, (int)parsedWid, (int)parsedHei); - IEnumerable allScreens = Screen.AllScreens; + DisplayInfo[] allScreens = DisplayInfo.AllDisplayInfos; WindowCollection allWindows = Application.Current.Windows; if (parsedHei < 10 || parsedWid < 10) return; - foreach (Screen screen in allScreens) - if (screen.WpfBounds.IntersectsWith(storedSize)) + foreach (DisplayInfo screen in allScreens) + { + Rect screenRect = screen.Bounds; + if (screenRect.IntersectsWith(storedSize)) isStoredRectWithinScreen = true; + } if (isStoredRectWithinScreen && couldParseAll) { @@ -71,7 +73,7 @@ public static void SetWindowPosition(Window passedWindow) public static void LaunchFullScreenGrab(TextBox? destinationTextBox = null) { - IEnumerable allScreens = Screen.AllScreens; + DisplayInfo[] allScreens = DisplayInfo.AllDisplayInfos; WindowCollection allWindows = Application.Current.Windows; List allFullscreenGrab = new(); @@ -93,7 +95,7 @@ public static void LaunchFullScreenGrab(TextBox? destinationTextBox = null) double sideLength = 40; - foreach (Screen screen in allScreens) + foreach (DisplayInfo screen in allScreens) { FullscreenGrab fullScreenGrab = allFullscreenGrab[count]; fullScreenGrab.WindowStartupLocation = WindowStartupLocation.Manual; @@ -102,7 +104,7 @@ public static void LaunchFullScreenGrab(TextBox? destinationTextBox = null) fullScreenGrab.DestinationTextBox = destinationTextBox; fullScreenGrab.WindowState = WindowState.Normal; - Point screenCenterPoint = screen.GetCenterPoint(); + Point screenCenterPoint = screen.ScaledCenterPoint(); fullScreenGrab.Left = screenCenterPoint.X - (sideLength / 2); fullScreenGrab.Top = screenCenterPoint.Y - (sideLength / 2); @@ -114,10 +116,11 @@ public static void LaunchFullScreenGrab(TextBox? destinationTextBox = null) } } - public static Point GetCenterPoint(this Screen screen) + public static Point GetCenterPoint(this DisplayInfo screen) { - double x = screen.WpfBounds.Left + (screen.WpfBounds.Width / 2); - double y = screen.WpfBounds.Top + (screen.WpfBounds.Height / 2); + Rect screenRect = screen.Bounds; + double x = screenRect.Left + (screenRect.Width / 2); + double y = screenRect.Top + (screenRect.Height / 2); return new(x, y); } @@ -151,8 +154,8 @@ internal static async void CloseAllFullscreenGrabs() { if (window is FullscreenGrab fsg) { - if (!string.IsNullOrWhiteSpace(fsg.textFromOCR)) - stringFromOCR = fsg.textFromOCR; + if (!string.IsNullOrWhiteSpace(fsg.TextFromOCR)) + stringFromOCR = fsg.TextFromOCR; if (fsg.DestinationTextBox is not null) { diff --git a/Text-Grab/Views/FullscreenGrab.xaml.cs b/Text-Grab/Views/FullscreenGrab.xaml.cs index 1a37c6ca..ccd338ef 100644 --- a/Text-Grab/Views/FullscreenGrab.xaml.cs +++ b/Text-Grab/Views/FullscreenGrab.xaml.cs @@ -1,4 +1,5 @@ -using System; +using Dapplo.Windows.User32; +using System; using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; @@ -7,6 +8,7 @@ using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; +using Text_Grab.Extensions; using Text_Grab.Interfaces; using Text_Grab.Models; using Text_Grab.Properties; @@ -24,16 +26,16 @@ public partial class FullscreenGrab : Window { #region Fields - private System.Windows.Point clickedPoint = new System.Windows.Point(); + private System.Windows.Point clickedPoint = new(); private TextBox? destinationTextBox; private DpiScale? dpiScale; private bool isComboBoxReady = false; private bool isSelecting = false; private bool isShiftDown = false; - private Border selectBorder = new Border(); + private Border selectBorder = new(); private double selectLeft; private double selectTop; - private System.Windows.Point shiftPoint = new System.Windows.Point(); + private System.Windows.Point shiftPoint = new(); private double xShiftDelta; private double yShiftDelta; private HistoryInfo? historyInfo; @@ -69,8 +71,8 @@ public TextBox? DestinationTextBox } public bool IsFreeze { get; set; } = false; - public string? textFromOCR { get; set; } - private System.Windows.Forms.Screen? currentScreen { get; set; } + public string? TextFromOCR { get; set; } + private DisplayInfo? CurrentScreen { get; set; } #endregion Properties @@ -263,7 +265,6 @@ private void GetDpiAdjustedRegionOfSelectBorder(out DpiScale dpi, out double pos { System.Windows.Point absPosPoint = this.GetAbsolutePosition(); dpi = VisualTreeHelper.GetDpi(this); - int firstScreenBPP = System.Windows.Forms.Screen.AllScreens[0].BitsPerPixel; posLeft = Canvas.GetLeft(selectBorder) + (absPosPoint.X / dpi.PixelsPerDip); posTop = Canvas.GetTop(selectBorder) + (absPosPoint.Y / dpi.PixelsPerDip); @@ -361,7 +362,7 @@ private static async Task LoadOcrLanguages(ComboBox languagesComboBox, bool usin haveSetLastLang = true; if (tesseractIncompatibleElements is not null) - foreach (var element in tesseractIncompatibleElements) + foreach (FrameworkElement element in tesseractIncompatibleElements) element.Visibility = Visibility.Collapsed; } @@ -420,12 +421,12 @@ private void PanSelection(System.Windows.Point movingPoint) double leftValue = selectLeft + xShiftDelta; double topValue = selectTop + yShiftDelta; - if (currentScreen is not null && dpiScale is not null) + if (CurrentScreen is not null && dpiScale is not null) { - double currentScreenLeft = currentScreen.Bounds.Left; // Should always be 0 - double currentScreenRight = currentScreen.Bounds.Right / dpiScale.Value.DpiScaleX; - double currentScreenTop = currentScreen.Bounds.Top; // Should always be 0 - double currentScreenBottom = currentScreen.Bounds.Bottom / dpiScale.Value.DpiScaleY; + double currentScreenLeft = CurrentScreen.Bounds.Left; // Should always be 0 + double currentScreenRight = CurrentScreen.Bounds.Right / dpiScale.Value.DpiScaleX; + double currentScreenTop = CurrentScreen.Bounds.Top; // Should always be 0 + double currentScreenBottom = CurrentScreen.Bounds.Bottom / dpiScale.Value.DpiScaleY; leftValue = Math.Clamp(leftValue, currentScreenLeft, (currentScreenRight - selectBorder.Width)); topValue = Math.Clamp(topValue, currentScreenTop, (currentScreenBottom - selectBorder.Height)); @@ -444,9 +445,7 @@ private void PlaceGrabFrameInSelectionRect() // Then place it where the user just drew the region // Add space around the window to account for Titlebar // bottom bar and width of GrabFrame - DpiScale dpi; - double posLeft, posTop; - GetDpiAdjustedRegionOfSelectBorder(out dpi, out posLeft, out posTop); + GetDpiAdjustedRegionOfSelectBorder(out DpiScale dpi, out double posLeft, out double posTop); GrabFrame grabFrame = new() { @@ -510,11 +509,14 @@ private void RegionClickCanvas_MouseDown(object sender, MouseButtonEventArgs e) Canvas.SetLeft(selectBorder, clickedPoint.X); Canvas.SetTop(selectBorder, clickedPoint.Y); - var screens = System.Windows.Forms.Screen.AllScreens; - System.Drawing.Point formsPoint = new((int)clickedPoint.X, (int)clickedPoint.Y); - foreach (var scr in screens) - if (scr.Bounds.Contains(formsPoint)) - currentScreen = scr; + DisplayInfo[] screens = DisplayInfo.AllDisplayInfos; + System.Windows.Point formsPoint = new((int)clickedPoint.X, (int)clickedPoint.Y); + foreach (DisplayInfo scr in screens) + { + Rect bound = scr.ScaledBounds(); + if (bound.Contains(formsPoint)) + CurrentScreen = scr; + } } private void RegionClickCanvas_MouseMove(object sender, MouseEventArgs e) @@ -532,13 +534,13 @@ private void RegionClickCanvas_MouseMove(object sender, MouseEventArgs e) isShiftDown = false; - var left = Math.Min(clickedPoint.X, movingPoint.X); - var top = Math.Min(clickedPoint.Y, movingPoint.Y); + double left = Math.Min(clickedPoint.X, movingPoint.X); + double top = Math.Min(clickedPoint.Y, movingPoint.Y); selectBorder.Height = Math.Max(clickedPoint.Y, movingPoint.Y) - top; selectBorder.Width = Math.Max(clickedPoint.X, movingPoint.X) - left; - selectBorder.Height = selectBorder.Height + 2; - selectBorder.Width = selectBorder.Width + 2; + selectBorder.Height += 2; + selectBorder.Width += 2; clippingGeometry.Rect = new Rect( new System.Windows.Point(left, top), @@ -553,7 +555,7 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs return; isSelecting = false; - currentScreen = null; + CurrentScreen = null; CursorClipper.UnClipCursor(); RegionClickCanvas.ReleaseMouseCapture(); clippingGeometry.Rect = new Rect( @@ -568,25 +570,16 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs movingPoint.X = Math.Round(movingPoint.X); movingPoint.Y = Math.Round(movingPoint.Y); - double correctedLeft = Left; - double correctedTop = Top; - - if (correctedLeft < 0) - correctedLeft = 0; - - if (correctedTop < 0) - correctedTop = 0; - double xDimScaled = Canvas.GetLeft(selectBorder) * m.M11; double yDimScaled = Canvas.GetTop(selectBorder) * m.M22; - Rectangle regionScaled = new Rectangle( + Rectangle regionScaled = new( (int)xDimScaled, (int)yDimScaled, (int)(selectBorder.Width * m.M11), (int)(selectBorder.Height * m.M22)); - textFromOCR = string.Empty; + TextFromOCR = string.Empty; if (NewGrabFrameMenuItem.IsChecked is true) { @@ -596,9 +589,7 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs try { RegionClickCanvas.Children.Remove(selectBorder); } catch { } - Language? selectedOcrLang = LanguagesComboBox.SelectedItem as Language; - - if (selectedOcrLang is null) + if (LanguagesComboBox.SelectedItem is not Language selectedOcrLang) selectedOcrLang = LanguageUtilities.GetOCRLanguage(); string tessTag = string.Empty; @@ -608,22 +599,22 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs bool isSmallClick = (regionScaled.Width < 3 || regionScaled.Height < 3); - bool isSingleLine = SingleLineMenuItem is null ? false : SingleLineMenuItem.IsChecked; - bool isTable = TableMenuItem is null ? false : TableMenuItem.IsChecked; + bool isSingleLine = SingleLineMenuItem is not null && SingleLineMenuItem.IsChecked; + bool isTable = TableMenuItem is not null && TableMenuItem.IsChecked; if (isSmallClick) { BackgroundBrush.Opacity = 0; - textFromOCR = await OcrUtilities.GetClickedWordAsync(this, new System.Windows.Point(xDimScaled, yDimScaled), selectedOcrLang); + TextFromOCR = await OcrUtilities.GetClickedWordAsync(this, new System.Windows.Point(xDimScaled, yDimScaled), selectedOcrLang); } else if (isTable) - textFromOCR = await OcrUtilities.GetRegionsTextAsTableAsync(this, regionScaled, selectedOcrLang); + TextFromOCR = await OcrUtilities.GetRegionsTextAsTableAsync(this, regionScaled, selectedOcrLang); else - textFromOCR = await OcrUtilities.GetRegionsTextAsync(this, regionScaled, selectedOcrLang, tessTag); + TextFromOCR = await OcrUtilities.GetRegionsTextAsync(this, regionScaled, selectedOcrLang, tessTag); if (DefaultSettings.UseHistory && !isSmallClick) { - GetDpiAdjustedRegionOfSelectBorder(out DpiScale dpi, out double posLeft, out double posTop); + GetDpiAdjustedRegionOfSelectBorder(out _, out double posLeft, out double posTop); Rect historyRect = new() { @@ -641,13 +632,13 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs CaptureDateTime = DateTimeOffset.Now, PositionRect = historyRect, IsTable = TableToggleButton.IsChecked!.Value, - TextContent = textFromOCR, + TextContent = TextFromOCR, ImageContent = Singleton.Instance.CachedBitmap, SourceMode = TextGrabMode.Fullscreen, }; } - if (!string.IsNullOrWhiteSpace(textFromOCR)) + if (!string.IsNullOrWhiteSpace(TextFromOCR)) { if (SendToEditTextToggleButton.IsChecked is true && destinationTextBox is null) { @@ -656,7 +647,7 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs } OutputUtilities.HandleTextFromOcr( - textFromOCR, + TextFromOCR, isSingleLine, isTable, destinationTextBox); @@ -707,8 +698,8 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) { WindowState = WindowState.Maximized; FullWindow.Rect = new System.Windows.Rect(0, 0, Width, Height); - this.KeyDown += FullscreenGrab_KeyDown; - this.KeyUp += FullscreenGrab_KeyUp; + KeyDown += FullscreenGrab_KeyDown; + KeyUp += FullscreenGrab_KeyUp; SetImageToBackground(); @@ -725,10 +716,10 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) Topmost = false; #endif - List tesseractIncompatibleFrameworkElements = new() - { + List tesseractIncompatibleFrameworkElements = + [ TableMenuItem, TableToggleButton - }; + ]; await LoadOcrLanguages(LanguagesComboBox, usingTesseract, tesseractIncompatibleFrameworkElements); isComboBoxReady = true; @@ -740,12 +731,12 @@ private void Window_Unloaded(object sender, RoutedEventArgs e) { BackgroundImage.Source = null; BackgroundImage.UpdateLayout(); - currentScreen = null; + CurrentScreen = null; dpiScale = null; - textFromOCR = null; + TextFromOCR = null; - this.Loaded -= Window_Loaded; - this.Unloaded -= Window_Unloaded; + Loaded -= Window_Loaded; + Unloaded -= Window_Unloaded; RegionClickCanvas.MouseDown -= RegionClickCanvas_MouseDown; RegionClickCanvas.MouseMove -= RegionClickCanvas_MouseMove; @@ -767,14 +758,14 @@ private void Window_Unloaded(object sender, RoutedEventArgs e) SettingsButton.Click -= SettingsMenuItem_Click; CancelButton.Click -= CancelMenuItem_Click; - this.KeyDown -= FullscreenGrab_KeyDown; - this.KeyUp -= FullscreenGrab_KeyUp; + KeyDown -= FullscreenGrab_KeyDown; + KeyUp -= FullscreenGrab_KeyUp; } private void StandardModeToggleButton_Click(object sender, RoutedEventArgs e) { bool isActive = CheckIfCheckingOrUnchecking(sender); - WindowUtilities.FullscreenKeyDown(Key.N, isActive); + WindowUtilities.FullscreenKeyDown(Key.N, isActive); SelectSingleToggleButton(sender); if (isActive)