diff --git a/Eve-O-Preview/Configuration/Implementation/ThumbnailConfiguration.cs b/Eve-O-Preview/Configuration/Implementation/ThumbnailConfiguration.cs
index eac17e9..779f509 100644
--- a/Eve-O-Preview/Configuration/Implementation/ThumbnailConfiguration.cs
+++ b/Eve-O-Preview/Configuration/Implementation/ThumbnailConfiguration.cs
@@ -24,6 +24,8 @@ namespace EveOPreview.Configuration.Implementation
this.MinimizeToTray = false;
this.ThumbnailRefreshPeriod = 500;
+ this.EnableCompatibilityMode = false;
+
this.ThumbnailOpacity = 0.5;
this.EnableClientLayoutTracking = false;
@@ -54,6 +56,9 @@ namespace EveOPreview.Configuration.Implementation
public bool MinimizeToTray { get; set; }
public int ThumbnailRefreshPeriod { get; set; }
+ [JsonProperty("CompatibilityMode")]
+ public bool EnableCompatibilityMode { get; set; }
+
[JsonProperty("ThumbnailsOpacity")]
public double ThumbnailOpacity { get; set; }
diff --git a/Eve-O-Preview/Configuration/Interface/IThumbnailConfiguration.cs b/Eve-O-Preview/Configuration/Interface/IThumbnailConfiguration.cs
index 565e395..3cb1158 100644
--- a/Eve-O-Preview/Configuration/Interface/IThumbnailConfiguration.cs
+++ b/Eve-O-Preview/Configuration/Interface/IThumbnailConfiguration.cs
@@ -8,6 +8,8 @@ namespace EveOPreview.Configuration
bool MinimizeToTray { get; set; }
int ThumbnailRefreshPeriod { get; set; }
+ bool EnableCompatibilityMode { get; set; }
+
double ThumbnailOpacity { get; set; }
bool EnableClientLayoutTracking { get; set; }
diff --git a/Eve-O-Preview/Eve-O-Preview.csproj b/Eve-O-Preview/Eve-O-Preview.csproj
index c469a1f..5e9d4aa 100644
--- a/Eve-O-Preview/Eve-O-Preview.csproj
+++ b/Eve-O-Preview/Eve-O-Preview.csproj
@@ -147,6 +147,7 @@
+
@@ -157,6 +158,15 @@
+
+ Form
+
+
+ Component
+
+
+ Form
+
@@ -212,7 +222,7 @@
ThumbnailView.cs
-
+
diff --git a/Eve-O-Preview/Program.cs b/Eve-O-Preview/Program.cs
index 430e490..9ab309b 100644
--- a/Eve-O-Preview/Program.cs
+++ b/Eve-O-Preview/Program.cs
@@ -11,14 +11,16 @@ namespace EveOPreview
{
static class Program
{
- private static string MutexName = "EVE-O Preview Single Instance Mutex";
+ private static string MUTEX_NAME = "EVE-O Preview Single Instance Mutex";
+
+ private static Mutex _singleInstanceMutex;
/// The main entry point for the application.
[STAThread]
static void Main()
{
#if DEBUG
- var expirationDate = new DateTime(2019, 5, 1);
+ var expirationDate = new DateTime(2019, 5, 15);
if (DateTime.Today >= expirationDate)
{
MessageBox.Show(@"This Beta version is expired. Please download a new build at https://github.com/Phrynohyas/eve-o-preview/releases", @"EVE-O Preview", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
@@ -28,11 +30,11 @@ namespace EveOPreview
// The very usual Mutex-based single-instance screening
// 'token' variable is used to store reference to the instance Mutex
// during the app lifetime
- object token = Program.GetInstanceToken();
+ Program._singleInstanceMutex = Program.GetInstanceToken();
// If it was not possible to acquire the app token then another app instance is already running
// Nothing to do here
- if (token == null)
+ if (Program._singleInstanceMutex == null)
{
return;
}
@@ -46,7 +48,7 @@ namespace EveOPreview
controller.Run();
}
- private static object GetInstanceToken()
+ private static Mutex GetInstanceToken()
{
// The code might look overcomplicated here for a single Mutex operation
// Yet we had already experienced a Windows-level issue
@@ -55,7 +57,7 @@ namespace EveOPreview
// exceptions later
try
{
- Mutex.OpenExisting(Program.MutexName);
+ Mutex.OpenExisting(Program.MUTEX_NAME);
// if that didn't fail then another instance is already running
return null;
}
@@ -65,7 +67,7 @@ namespace EveOPreview
}
catch (Exception)
{
- Mutex token = new Mutex(true, Program.MutexName, out var result);
+ Mutex token = new Mutex(true, Program.MUTEX_NAME, out var result);
return result ? token : null;
}
}
@@ -105,9 +107,11 @@ namespace EveOPreview
IApplicationController controller = new ApplicationController(container);
// UI classes
- controller.RegisterView()
- .RegisterView()
- .RegisterInstance(new ApplicationContext());
+ controller.RegisterView();
+ controller.RegisterView();
+
+ controller.RegisterView();
+ controller.RegisterInstance(new ApplicationContext());
return controller;
}
diff --git a/Eve-O-Preview/Services/Implementation/DwmThumbnail.cs b/Eve-O-Preview/Services/Implementation/DwmThumbnail.cs
index 76b9388..873ded8 100644
--- a/Eve-O-Preview/Services/Implementation/DwmThumbnail.cs
+++ b/Eve-O-Preview/Services/Implementation/DwmThumbnail.cs
@@ -36,7 +36,7 @@ namespace EveOPreview.Services.Implementation
try
{
- this._handle = DwmApiNativeMethods.DwmRegisterThumbnail(destination, source);
+ this._handle = DwmNativeMethods.DwmRegisterThumbnail(destination, source);
}
catch (ArgumentException)
{
@@ -63,7 +63,7 @@ namespace EveOPreview.Services.Implementation
try
{
- DwmApiNativeMethods.DwmUnregisterThumbnail(this._handle);
+ DwmNativeMethods.DwmUnregisterThumbnail(this._handle);
}
catch (ArgumentException)
{
@@ -88,7 +88,7 @@ namespace EveOPreview.Services.Implementation
try
{
- DwmApiNativeMethods.DwmUpdateThumbnailProperties(this._handle, this._properties);
+ DwmNativeMethods.DwmUpdateThumbnailProperties(this._handle, this._properties);
}
catch (ArgumentException)
{
diff --git a/Eve-O-Preview/Services/Implementation/ThumbnailManager.cs b/Eve-O-Preview/Services/Implementation/ThumbnailManager.cs
index 005927c..9f55ac2 100644
--- a/Eve-O-Preview/Services/Implementation/ThumbnailManager.cs
+++ b/Eve-O-Preview/Services/Implementation/ThumbnailManager.cs
@@ -10,7 +10,7 @@ using MediatR;
namespace EveOPreview.Services
{
- class ThumbnailManager : IThumbnailManager
+ sealed class ThumbnailManager : IThumbnailManager
{
#region Private constants
private const int WINDOW_POSITION_THRESHOLD_LOW = -10_000;
diff --git a/Eve-O-Preview/Services/Implementation/WindowManager.cs b/Eve-O-Preview/Services/Implementation/WindowManager.cs
index a2096f1..b7e7135 100644
--- a/Eve-O-Preview/Services/Implementation/WindowManager.cs
+++ b/Eve-O-Preview/Services/Implementation/WindowManager.cs
@@ -1,14 +1,19 @@
using System;
+using System.Drawing;
using System.Runtime.InteropServices;
using EveOPreview.Services.Interop;
namespace EveOPreview.Services.Implementation
{
- class WindowManager : IWindowManager
+ sealed class WindowManager : IWindowManager
{
+ #region Private constants
+ private const int WINDOW_SIZE_THRESHOLD = 300;
+ #endregion
+
public WindowManager()
{
- this.IsCompositionEnabled = DwmApiNativeMethods.DwmIsCompositionEnabled();
+ this.IsCompositionEnabled = DwmNativeMethods.DwmIsCompositionEnabled();
}
public bool IsCompositionEnabled { get; }
@@ -63,12 +68,42 @@ namespace EveOPreview.Services.Implementation
return User32NativeMethods.IsIconic(handle);
}
- public IDwmThumbnail RegisterThumbnail(IntPtr destination, IntPtr source)
+ public IDwmThumbnail GetLiveThumbnail(IntPtr destination, IntPtr source)
{
IDwmThumbnail thumbnail = new DwmThumbnail(this);
thumbnail.Register(destination, source);
return thumbnail;
}
+
+ public Image GetStaticThumbnail(IntPtr source)
+ {
+ var sourceContext = User32NativeMethods.GetDC(source);
+
+ User32NativeMethods.GetClientRect(source, out RECT windowRect);
+
+ var width = windowRect.Right - windowRect.Left;
+ var height = windowRect.Bottom - windowRect.Top;
+
+ // Check if there is anything to make thumbnail of
+ if ((width < WINDOW_SIZE_THRESHOLD) || (height < WINDOW_SIZE_THRESHOLD))
+ {
+ return null;
+ }
+
+ var destContext = Gdi32NativeMethods.CreateCompatibleDC(sourceContext);
+ var bitmap = Gdi32NativeMethods.CreateCompatibleBitmap(sourceContext, width, height);
+
+ var oldBitmap = Gdi32NativeMethods.SelectObject(destContext, bitmap);
+ Gdi32NativeMethods.BitBlt(destContext, 0, 0, width, height, sourceContext, 0, 0, Gdi32NativeMethods.SRCCOPY);
+ Gdi32NativeMethods.SelectObject(destContext, oldBitmap);
+ Gdi32NativeMethods.DeleteDC(destContext);
+ User32NativeMethods.ReleaseDC(source, sourceContext);
+
+ Image image = Image.FromHbitmap(bitmap);
+ Gdi32NativeMethods.DeleteObject(bitmap);
+
+ return image;
+ }
}
}
\ No newline at end of file
diff --git a/Eve-O-Preview/Services/Interface/IWindowManager.cs b/Eve-O-Preview/Services/Interface/IWindowManager.cs
index e482a16..50a01b6 100644
--- a/Eve-O-Preview/Services/Interface/IWindowManager.cs
+++ b/Eve-O-Preview/Services/Interface/IWindowManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.Drawing;
namespace EveOPreview.Services
{
@@ -15,6 +16,7 @@ namespace EveOPreview.Services
(int Left, int Top, int Right, int Bottom) GetWindowPosition(IntPtr handle);
bool IsWindowMinimized(IntPtr handle);
- IDwmThumbnail RegisterThumbnail(IntPtr destination, IntPtr source);
+ IDwmThumbnail GetLiveThumbnail(IntPtr destination, IntPtr source);
+ Image GetStaticThumbnail(IntPtr source);
}
}
\ No newline at end of file
diff --git a/Eve-O-Preview/Services/Interop/DwmApiNativeMethods.cs b/Eve-O-Preview/Services/Interop/DwmNativeMethods.cs
similarity index 95%
rename from Eve-O-Preview/Services/Interop/DwmApiNativeMethods.cs
rename to Eve-O-Preview/Services/Interop/DwmNativeMethods.cs
index 90794e0..5cb7325 100644
--- a/Eve-O-Preview/Services/Interop/DwmApiNativeMethods.cs
+++ b/Eve-O-Preview/Services/Interop/DwmNativeMethods.cs
@@ -4,7 +4,7 @@ using System.Drawing;
namespace EveOPreview.Services.Interop
{
- static class DwmApiNativeMethods
+ static class DwmNativeMethods
{
[DllImport("dwmapi.dll", PreserveSig = false)]
public static extern void DwmEnableBlurBehindWindow(IntPtr hWnd, DWM_BLURBEHIND pBlurBehind);
diff --git a/Eve-O-Preview/Services/Interop/Gdi32NativeMethods.cs b/Eve-O-Preview/Services/Interop/Gdi32NativeMethods.cs
new file mode 100644
index 0000000..f966511
--- /dev/null
+++ b/Eve-O-Preview/Services/Interop/Gdi32NativeMethods.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace EveOPreview.Services.Interop
+{
+ static class Gdi32NativeMethods
+ {
+ public const int SRCCOPY = 13369376;
+
+ [DllImport("gdi32.dll")]
+ public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
+
+ [DllImport("gdi32.dll")]
+ public static extern bool DeleteDC(IntPtr hdc);
+
+ [DllImport("gdi32.dll")]
+ public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int width, int height);
+
+ [DllImport("gdi32.dll")]
+ public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
+
+ [DllImport("gdi32.dll")]
+ public static extern bool DeleteObject(IntPtr hObject);
+
+ [DllImport("gdi32.dll")]
+ public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop);
+ }
+}
diff --git a/Eve-O-Preview/Services/Interop/User32NativeMethods.cs b/Eve-O-Preview/Services/Interop/User32NativeMethods.cs
index fbde187..6adc97a 100644
--- a/Eve-O-Preview/Services/Interop/User32NativeMethods.cs
+++ b/Eve-O-Preview/Services/Interop/User32NativeMethods.cs
@@ -24,7 +24,10 @@ namespace EveOPreview.Services.Interop
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
- public static extern int GetWindowRect(IntPtr hwnd, out RECT rect);
+ public static extern int GetWindowRect(IntPtr hWnd, out RECT rect);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetClientRect(IntPtr hWnd, out RECT rect);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
@@ -42,5 +45,14 @@ namespace EveOPreview.Services.Interop
[DllImport("user32.dll")]
public static extern bool IsZoomed(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr GetWindowDC(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr GetDC(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hdc);
}
}
\ No newline at end of file
diff --git a/Eve-O-Preview/View/Implementation/LiveThumbnailView.cs b/Eve-O-Preview/View/Implementation/LiveThumbnailView.cs
new file mode 100644
index 0000000..543c94d
--- /dev/null
+++ b/Eve-O-Preview/View/Implementation/LiveThumbnailView.cs
@@ -0,0 +1,48 @@
+using System;
+using EveOPreview.Services;
+
+namespace EveOPreview.View
+{
+ sealed class LiveThumbnailView : ThumbnailView
+ {
+ #region Private fields
+ private IDwmThumbnail _thumbnail;
+ #endregion
+
+ public LiveThumbnailView(IWindowManager windowManager)
+ : base(windowManager)
+ {
+ }
+
+ public override void Close()
+ {
+ this._thumbnail?.Unregister();
+ base.Close();
+ }
+
+ protected override void RefreshThumbnail(bool forceRefresh)
+ {
+ // To prevent flickering the old broken thumbnail is removed AFTER the new shiny one is created
+ IDwmThumbnail obsoleteThumbnail = forceRefresh ? this._thumbnail : null;
+
+ if ((this._thumbnail == null) || forceRefresh)
+ {
+ this.RegisterThumbnail();
+ }
+
+ obsoleteThumbnail?.Unregister();
+ }
+
+ protected override void ResizeThumbnail(int baseWidth, int baseHeight, int highlightWidthTop, int highlightWidthRight, int highlightWidthBottom, int highlightWidthLeft)
+ {
+ this._thumbnail.Move(0 + highlightWidthLeft, 0 + highlightWidthTop, baseWidth - highlightWidthRight, baseHeight - highlightWidthBottom);
+ this._thumbnail.Update();
+ }
+
+ private void RegisterThumbnail()
+ {
+ this._thumbnail = this.WindowManager.GetLiveThumbnail(this.Handle, this.Id);
+ this._thumbnail.Update();
+ }
+ }
+}
diff --git a/Eve-O-Preview/View/Implementation/MainForm.resx b/Eve-O-Preview/View/Implementation/MainForm.resx
index 426d527..0537524 100644
--- a/Eve-O-Preview/View/Implementation/MainForm.resx
+++ b/Eve-O-Preview/View/Implementation/MainForm.resx
@@ -141,12 +141,72 @@
True
+
+ False
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
False
True
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
True
@@ -180,6 +240,39 @@
True
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ True
+
False
@@ -216,6 +309,12 @@
True
+
+ False
+
+
+ True
+
False
@@ -225,6 +324,54 @@
True
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
True
@@ -276,6 +423,27 @@
True
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
True
@@ -303,6 +471,21 @@
True
+
+ False
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ True
+
True
@@ -324,6 +507,36 @@
True
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
False
diff --git a/Eve-O-Preview/View/Implementation/StaticThumbnailImage.cs b/Eve-O-Preview/View/Implementation/StaticThumbnailImage.cs
new file mode 100644
index 0000000..f5ec500
--- /dev/null
+++ b/Eve-O-Preview/View/Implementation/StaticThumbnailImage.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Windows.Forms;
+
+namespace EveOPreview.View
+{
+ sealed class StaticThumbnailImage : PictureBox
+ {
+ protected override void WndProc(ref Message m)
+ {
+ const int WM_NCHITTEST = 0x0084;
+ const int HTTRANSPARENT = (-1);
+
+ if (m.Msg == WM_NCHITTEST)
+ {
+ m.Result = (IntPtr)HTTRANSPARENT;
+ }
+ else
+ {
+ base.WndProc(ref m);
+ }
+ }
+ }
+}
diff --git a/Eve-O-Preview/View/Implementation/StaticThumbnailView.cs b/Eve-O-Preview/View/Implementation/StaticThumbnailView.cs
new file mode 100644
index 0000000..cabe743
--- /dev/null
+++ b/Eve-O-Preview/View/Implementation/StaticThumbnailView.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using EveOPreview.Services;
+
+namespace EveOPreview.View
+{
+ sealed class StaticThumbnailView : ThumbnailView
+ {
+ #region Private fields
+ private readonly PictureBox _thumbnail;
+ #endregion
+
+ public StaticThumbnailView(IWindowManager windowManager)
+ : base(windowManager)
+ {
+ this._thumbnail = new StaticThumbnailImage
+ {
+ TabStop = false,
+ SizeMode = PictureBoxSizeMode.StretchImage,
+ Location = new Point(0, 0),
+ Size = new Size(this.ClientSize.Width, this.ClientSize.Height)
+ };
+ this.Controls.Add(this._thumbnail);
+ }
+
+ protected override void RefreshThumbnail(bool forceRefresh)
+ {
+ if (!forceRefresh)
+ {
+ return;
+ }
+
+ var thumbnail = this.WindowManager.GetStaticThumbnail(this.Id);
+ if (thumbnail != null)
+ {
+ var oldImage = this._thumbnail.Image;
+ this._thumbnail.Image = thumbnail;
+ oldImage?.Dispose();
+ }
+ }
+
+ protected override void ResizeThumbnail(int baseWidth, int baseHeight, int highlightWidthTop, int highlightWidthRight, int highlightWidthBottom, int highlightWidthLeft)
+ {
+ this._thumbnail.Location = new Point(0 + highlightWidthLeft, 0 + highlightWidthTop);
+ this._thumbnail.Size = new Size(baseWidth - highlightWidthLeft - highlightWidthRight, baseHeight - highlightWidthTop - highlightWidthBottom);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Eve-O-Preview/View/Implementation/ThumbnailView.Designer.cs b/Eve-O-Preview/View/Implementation/ThumbnailView.Designer.cs
index c0540fd..697d424 100644
--- a/Eve-O-Preview/View/Implementation/ThumbnailView.Designer.cs
+++ b/Eve-O-Preview/View/Implementation/ThumbnailView.Designer.cs
@@ -19,11 +19,12 @@ namespace EveOPreview.View
// ThumbnailView
//
this.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.Black;
+ this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.ClientSize = new System.Drawing.Size(153, 89);
this.ControlBox = false;
+ this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.MaximizeBox = false;
this.MinimizeBox = false;
diff --git a/Eve-O-Preview/View/Implementation/ThumbnailView.cs b/Eve-O-Preview/View/Implementation/ThumbnailView.cs
index bc5cf0f..65e1e7d 100644
--- a/Eve-O-Preview/View/Implementation/ThumbnailView.cs
+++ b/Eve-O-Preview/View/Implementation/ThumbnailView.cs
@@ -7,26 +7,32 @@ using EveOPreview.UI.Hotkeys;
namespace EveOPreview.View
{
- public partial class ThumbnailView : Form, IThumbnailView
+ public abstract partial class ThumbnailView : Form, IThumbnailView
{
#region Private constants
private const int RESIZE_EVENT_TIMEOUT = 500;
+f private const double OPACITY_THRESHOLD = 0.9;
+ private const double OPACITY_EPSILON = 0.1;
#endregion
#region Private fields
- private readonly IWindowManager _windowManager;
private readonly ThumbnailOverlay _overlay;
- private IDwmThumbnail _thumbnail;
// Part of the logic (namely current size / position management)
// was moved to the view due to the performance reasons
private bool _isOverlayVisible;
private bool _isTopMost;
+ private bool _isHighlightEnabled;
+ private bool _isHighlightRequested;
+ private int _highlightWidth;
+
private bool _isLocationChanged;
private bool _isSizeChanged;
+
private bool _isCustomMouseModeActive;
- private bool _isHighlightEnabled;
- private int _highlightWidth;
+
+ private double _opacity;
+
private DateTime _suppressResizeEventsTimestamp;
private Size _baseZoomSize;
private Point _baseZoomLocation;
@@ -36,29 +42,34 @@ namespace EveOPreview.View
private HotkeyHandler _hotkeyHandler;
#endregion
- public ThumbnailView(IWindowManager windowManager)
+ protected ThumbnailView(IWindowManager windowManager)
{
this.SuppressResizeEvent();
- this._windowManager = windowManager;
+ this.WindowManager = windowManager;
this.IsActive = false;
this.IsOverlayEnabled = false;
this._isOverlayVisible = false;
this._isTopMost = false;
+ this._isHighlightEnabled = false;
+ this._isHighlightRequested = false;
this._isLocationChanged = true;
this._isSizeChanged = true;
+
this._isCustomMouseModeActive = false;
- this._isHighlightEnabled = false;
+ this._opacity = 1.0;
InitializeComponent();
this._overlay = new ThumbnailOverlay(this, this.MouseDown_Handler);
}
+ protected IWindowManager WindowManager { get; }
+
public IntPtr Id { get; set; }
public string Title
@@ -131,12 +142,11 @@ namespace EveOPreview.View
base.Hide();
}
- public new void Close()
+ public new virtual void Close()
{
this.SuppressResizeEvent();
this.IsActive = false;
- this._thumbnail?.Unregister();
this._overlay.Close();
base.Close();
}
@@ -155,13 +165,33 @@ namespace EveOPreview.View
public void SetOpacity(double opacity)
{
- this.Opacity = opacity;
+ if (opacity >= OPACITY_THRESHOLD)
+ {
+ opacity = 1.0;
+ }
- // Overlay opacity settings
- // Of the thumbnail's opacity is almost full then set the overlay's one to
- // full. Otherwise set it to half of the thumbnail opacity
- // Opacity value is stored even if the overlay is not displayed atm
- this._overlay.Opacity = this.Opacity > 0.9 ? 1.0 : 1.0 - (1.0 - this.Opacity) / 2;
+ if (Math.Abs(opacity - this._opacity) < OPACITY_EPSILON)
+ {
+ return;
+ }
+
+ try
+ {
+ this.Opacity = opacity;
+
+ // Overlay opacity settings
+ // Of the thumbnail's opacity is almost full then set the overlay's one to
+ // full. Otherwise set it to half of the thumbnail opacity
+ // Opacity value is stored even if the overlay is not displayed atm
+ this._overlay.Opacity = opacity > 0.8 ? 1.0 : 1.0 - (1.0 - opacity) / 2;
+
+ this._opacity = opacity;
+ }
+ catch (Win32Exception)
+ {
+ // Something went wrong in WinForms internals
+ // Opacity will be updated in the next cycle
+ }
}
public void SetFrames(bool enable)
@@ -177,9 +207,6 @@ namespace EveOPreview.View
this.SuppressResizeEvent();
this.FormBorderStyle = style;
-
- // Notify about possible contents position change
- this._isSizeChanged = true;
}
public void SetTopMost(bool enableTopmost)
@@ -198,20 +225,20 @@ namespace EveOPreview.View
public void SetHighlight(bool enabled, Color color, int width)
{
- if (this._isHighlightEnabled == enabled)
+ if (this._isHighlightRequested == enabled)
{
return;
}
if (enabled)
{
- this._isHighlightEnabled = true;
+ this._isHighlightRequested = true;
this._highlightWidth = width;
this.BackColor = color;
}
else
{
- this._isHighlightEnabled = false;
+ this._isHighlightRequested = false;
this.BackColor = SystemColors.Control;
}
@@ -305,69 +332,31 @@ namespace EveOPreview.View
public void Refresh(bool forceRefresh)
{
- // To prevent flickering the old broken thumbnail is removed AFTER the new shiny one is created
- IDwmThumbnail obsoleteThumbnail = forceRefresh ? this._thumbnail : null;
+ this.RefreshThumbnail(forceRefresh);
+ this.HighlightThumbnail(forceRefresh || this._isSizeChanged);
+ this.RefreshOverlay(forceRefresh || this._isSizeChanged || this._isLocationChanged);
- if ((this._thumbnail == null) || forceRefresh)
- {
- this.RegisterThumbnail();
- }
-
- bool sizeChanged = this._isSizeChanged || forceRefresh;
- bool locationChanged = this._isLocationChanged || forceRefresh;
-
- if (sizeChanged)
- {
- this.RecalculateThumbnailSize();
-
- this.UpdateThumbnail();
-
- this._isSizeChanged = false;
- }
-
- obsoleteThumbnail?.Unregister();
-
- this._overlay.EnableOverlayLabel(this.IsOverlayEnabled);
-
- if (!this._isOverlayVisible)
- {
- // One-time action to show the Overlay before it is set up
- // Otherwise its position won't be set
- this._overlay.Show();
- this._isOverlayVisible = true;
- }
- else
- {
- if (!(sizeChanged || locationChanged))
- {
- // No need to adjust in the overlay location if it is already visible and properly set
- return;
- }
- }
-
- Size overlaySize = this.ClientSize;
- Point overlayLocation = this.Location;
-
- int borderWidth = (this.Size.Width - this.ClientSize.Width) / 2;
- overlayLocation.X += borderWidth;
- overlayLocation.Y += (this.Size.Height - this.ClientSize.Height) - borderWidth;
-
- this._isLocationChanged = false;
- this._overlay.Size = overlaySize;
- this._overlay.Location = overlayLocation;
- this._overlay.Refresh();
+ this._isSizeChanged = false;
}
- private void RecalculateThumbnailSize()
+ protected abstract void RefreshThumbnail(bool forceRefresh);
+
+ protected abstract void ResizeThumbnail(int baseWidth, int baseHeight, int highlightWidthTop, int highlightWidthRight, int highlightWidthBottom, int highlightWidthLeft);
+
+ private void HighlightThumbnail(bool forceRefresh)
{
- // This approach would work only for square-shaped thumbnail window
- // To get PROPER results we have to do some crazy math
- //int delta = this._isHighlightEnabled ? this._highlightWidth : 0;
- //this._thumbnail.rcDestination = new RECT(0 + delta, 0 + delta, this.ClientSize.Width - delta, this.ClientSize.Height - delta);
- if (!this._isHighlightEnabled)
+ if (!forceRefresh && (this._isHighlightRequested == this._isHighlightEnabled))
{
- //No highlighting enabled, so no odd math required
- this._thumbnail.Move(0, 0, this.ClientSize.Width, this.ClientSize.Height);
+ // Nothing to do here
+ return;
+ }
+
+ this._isHighlightEnabled = this._isHighlightRequested;
+
+ if (!this._isHighlightRequested)
+ {
+ //No highlighting enabled, so no math required
+ this.ResizeThumbnail(this.ClientSize.Width, this.ClientSize.Height, 0, 0, 0, 0);
return;
}
@@ -381,7 +370,38 @@ namespace EveOPreview.View
int highlightWidthLeft = (baseWidth - actualWidth) / 2;
int highlightWidthRight = baseWidth - actualWidth - highlightWidthLeft;
- this._thumbnail.Move(0 + highlightWidthLeft, 0 + this._highlightWidth, baseWidth - highlightWidthRight, baseHeight - this._highlightWidth);
+ this.ResizeThumbnail(this.ClientSize.Width, this.ClientSize.Height, this._highlightWidth, highlightWidthRight, this._highlightWidth, highlightWidthLeft);
+ }
+
+ private void RefreshOverlay(bool forceRefresh)
+ {
+ if (this._isOverlayVisible && !forceRefresh)
+ {
+ // No need to update anything. Everything is already set up
+ return;
+ }
+
+ this._overlay.EnableOverlayLabel(this.IsOverlayEnabled);
+
+ if (!this._isOverlayVisible)
+ {
+ // One-time action to show the Overlay before it is set up
+ // Otherwise its position won't be set
+ this._overlay.Show();
+ this._isOverlayVisible = true;
+ }
+
+ Size overlaySize = this.ClientSize;
+ Point overlayLocation = this.Location;
+
+ int borderWidth = (this.Size.Width - this.ClientSize.Width) / 2;
+ overlayLocation.X += borderWidth;
+ overlayLocation.Y += (this.Size.Height - this.ClientSize.Height) - borderWidth;
+
+ this._isLocationChanged = false;
+ this._overlay.Size = overlaySize;
+ this._overlay.Location = overlayLocation;
+ this._overlay.Refresh();
}
private void SuppressResizeEvent()
@@ -482,18 +502,6 @@ namespace EveOPreview.View
}
#endregion
- #region Thumbnail management
- private void RegisterThumbnail()
- {
- this._thumbnail = this._windowManager.RegisterThumbnail(this.Handle, this.Id);
- }
-
- private void UpdateThumbnail()
- {
- this._thumbnail.Update();
- }
- #endregion
-
#region Custom Mouse mode
// This pair of methods saves/restores certain window properties
// Methods are used to remove the 'Zoom' effect (if any) when the
diff --git a/Eve-O-Preview/View/Implementation/ThumbnailViewFactory.cs b/Eve-O-Preview/View/Implementation/ThumbnailViewFactory.cs
index 22c8715..83d271e 100644
--- a/Eve-O-Preview/View/Implementation/ThumbnailViewFactory.cs
+++ b/Eve-O-Preview/View/Implementation/ThumbnailViewFactory.cs
@@ -1,20 +1,25 @@
using System;
using System.Drawing;
+using EveOPreview.Configuration;
namespace EveOPreview.View
{
sealed class ThumbnailViewFactory : IThumbnailViewFactory
{
private readonly IApplicationController _controller;
+ private readonly bool _isCompatibilityModeEnabled;
- public ThumbnailViewFactory(IApplicationController controller)
+ public ThumbnailViewFactory(IApplicationController controller, IThumbnailConfiguration configuration)
{
this._controller = controller;
+ this._isCompatibilityModeEnabled = configuration.EnableCompatibilityMode;
}
public IThumbnailView Create(IntPtr id, string title, Size size)
{
- IThumbnailView view = this._controller.Create();
+ IThumbnailView view = this._isCompatibilityModeEnabled
+ ? (IThumbnailView)this._controller.Create()
+ : (IThumbnailView)this._controller.Create();
view.Id = id;
view.Title = title;
diff --git a/Eve-O-Preview/app.manifest b/Eve-O-Preview/app.manifest
index 5fc7044..cc914b0 100644
--- a/Eve-O-Preview/app.manifest
+++ b/Eve-O-Preview/app.manifest
@@ -74,8 +74,8 @@
-->
- True/PM
- PerMonitorV2
+ True
+ PerMonitor
diff --git a/README.md b/README.md
index 03ac55d..5d5997b 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,7 @@ Some of the application options are not exposed in the GUI. They can be adjusted
| Option | Description |
| --- | --- |
| **ActiveClientHighlightThickness** | Thickness of the border used to highlight the active client's thumbnail.
Allowed values are **1**...**6**.
The default value is **3**
For example: **"ActiveClientHighlightThickness": 3** |
+| **CompatibilityMode** | Enables the alternative render mode (see below)
The default value is **false**
For example: **"CompatibilityMode": true** |
| **EnableThumbnailSnap** | Allows to disable thumbnails snap feature by setting its value to **false**
The default value is **true**
For example: **"EnableThumbnailSnap": true** |
| **PriorityClients** | Allows to set a list of clients that are not auto-minimized on inactivity even if the **Minimize inactive EVE clients** option is enabled. Listed clients still can be minimized using Windows hotkeys or via _Ctrl+Click_ on the corresponding thumbnail
The default value is empty list **[]**
For example: **"PriorityClients": [ "EVE - Phrynohyas Tig-Rah", "EVE - Ondatra Patrouette" ]** |
| **ThumbnailMinimumSize** | Minimum thumbnail size that can be set either via GUI or by resizing a thumbnail window. Value is written in the form "width, height"
The default value is **"100, 80"**.
For example: **"ThumbnailMinimumSize": "100, 80"** |
@@ -124,6 +125,14 @@ The following hotkey is described as `modifier+key` where `modifier` can be **Co
**Note:** Do not set hotkeys to use the key combinations already used by EVE. It won't work as "_I set hotkey for my DPS char to F1 and when I'll press F1 it will automatically open the DPS char's window and activate guns_". Key combination will be swallowed by EVE-O Preview and NOT retranslated to EVE window. So it will be only "_it will automatically open the DPS char's window_".
+## Compatibility Mode
+
+This setting allows to enable an alternate thumbnail render. This render doesn't use advanced DWM API to create live previews. Instead it is a screenshot-based render with the following pros and cons:
+* `+` Doesn't require Aero to work
+* `+` Should work even in remote desktop environments
+* `-` Consumes significantly more memory. In the testing environment EVE-O Preview did consume around 180 MB to manage 3 thumbnails using this render. At the same time the primary render did consume around 50 MB when run in the same environment.
+* `-` Thumbnail images are refreshed at 1 FPS rate
+* `-` Possible short mouse cursor freezes
---