From a6f5e2edec7a09d72821a04c061fc4a1f189be10 Mon Sep 17 00:00:00 2001 From: Anton Kasyanov Date: Sat, 14 May 2016 19:50:11 +0300 Subject: [PATCH] Provide better Preview API --- .../DwmAPI/DWM_THUMBNAIL_PROPERTIES.cs | 8 +- Eve-O-Preview/DwmAPI/DWM_TNP_CONSTANTS.cs | 11 + Eve-O-Preview/Eve-O-Preview.csproj | 23 +- Eve-O-Preview/GUI/MainForm.cs | 93 ++-- Eve-O-Preview/Preview/Preview.cs | 383 --------------- Eve-O-Preview/Thumbnail/IThumbnail.cs | 31 ++ Eve-O-Preview/Thumbnail/ThumbnailFactory.cs | 13 + .../ThumbnailOverlay.Designer.cs} | 4 +- .../ThumbnailOverlay.cs} | 4 +- .../ThumbnailOverlay.resx} | 0 .../ThumbnailWindow.Designer.cs} | 11 +- Eve-O-Preview/Thumbnail/ThumbnailWindow.cs | 437 ++++++++++++++++++ .../ThumbnailWindow.resx} | 0 13 files changed, 556 insertions(+), 462 deletions(-) create mode 100644 Eve-O-Preview/DwmAPI/DWM_TNP_CONSTANTS.cs delete mode 100644 Eve-O-Preview/Preview/Preview.cs create mode 100644 Eve-O-Preview/Thumbnail/IThumbnail.cs create mode 100644 Eve-O-Preview/Thumbnail/ThumbnailFactory.cs rename Eve-O-Preview/{Preview/PreviewOverlay.Designer.cs => Thumbnail/ThumbnailOverlay.Designer.cs} (95%) rename Eve-O-Preview/{Preview/PreviewOverlay.cs => Thumbnail/ThumbnailOverlay.cs} (79%) rename Eve-O-Preview/{Preview/PreviewOverlay.resx => Thumbnail/ThumbnailOverlay.resx} (100%) rename Eve-O-Preview/{Preview/Preview.Designer.cs => Thumbnail/ThumbnailWindow.Designer.cs} (89%) create mode 100644 Eve-O-Preview/Thumbnail/ThumbnailWindow.cs rename Eve-O-Preview/{Preview/Preview.resx => Thumbnail/ThumbnailWindow.resx} (100%) diff --git a/Eve-O-Preview/DwmAPI/DWM_THUMBNAIL_PROPERTIES.cs b/Eve-O-Preview/DwmAPI/DWM_THUMBNAIL_PROPERTIES.cs index b5d270a..5c12e31 100644 --- a/Eve-O-Preview/DwmAPI/DWM_THUMBNAIL_PROPERTIES.cs +++ b/Eve-O-Preview/DwmAPI/DWM_THUMBNAIL_PROPERTIES.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace EveOPreview { [StructLayout(LayoutKind.Sequential)] - internal class DWM_THUMBNAIL_PROPERTIES + class DWM_THUMBNAIL_PROPERTIES { public uint dwFlags; public RECT rcDestination; @@ -13,11 +13,5 @@ namespace EveOPreview public bool fVisible; [MarshalAs(UnmanagedType.Bool)] public bool fSourceClientAreaOnly; - - public const uint DWM_TNP_RECTDESTINATION = 0x00000001; - public const uint DWM_TNP_RECTSOURCE = 0x00000002; - public const uint DWM_TNP_OPACITY = 0x00000004; - public const uint DWM_TNP_VISIBLE = 0x00000008; - public const uint DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010; } } \ No newline at end of file diff --git a/Eve-O-Preview/DwmAPI/DWM_TNP_CONSTANTS.cs b/Eve-O-Preview/DwmAPI/DWM_TNP_CONSTANTS.cs new file mode 100644 index 0000000..7042770 --- /dev/null +++ b/Eve-O-Preview/DwmAPI/DWM_TNP_CONSTANTS.cs @@ -0,0 +1,11 @@ +namespace EveOPreview +{ + static class DWM_TNP_CONSTANTS + { + public const uint DWM_TNP_RECTDESTINATION = 0x00000001; + public const uint DWM_TNP_RECTSOURCE = 0x00000002; + public const uint DWM_TNP_OPACITY = 0x00000004; + public const uint DWM_TNP_VISIBLE = 0x00000008; + public const uint DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010; + } +} \ No newline at end of file diff --git a/Eve-O-Preview/Eve-O-Preview.csproj b/Eve-O-Preview/Eve-O-Preview.csproj index f88d2a7..9e8f21c 100644 --- a/Eve-O-Preview/Eve-O-Preview.csproj +++ b/Eve-O-Preview/Eve-O-Preview.csproj @@ -105,6 +105,7 @@ + @@ -117,11 +118,13 @@ MainForm.cs - + + + Form - - PreviewOverlay.cs + + ThumbnailOverlay.cs @@ -129,17 +132,17 @@ Designer MainForm.cs - - PreviewOverlay.cs + + ThumbnailOverlay.cs ResXFileCodeGenerator Resources.Designer.cs Designer - + Designer - Preview.cs + ThumbnailWindow.cs True @@ -158,11 +161,11 @@ Settings.settings True - + Form - - Preview.cs + + ThumbnailWindow.cs diff --git a/Eve-O-Preview/GUI/MainForm.cs b/Eve-O-Preview/GUI/MainForm.cs index 3aa95aa..52f7249 100644 --- a/Eve-O-Preview/GUI/MainForm.cs +++ b/Eve-O-Preview/GUI/MainForm.cs @@ -12,12 +12,14 @@ namespace EveOPreview { public partial class MainForm : Form { + private readonly ThumbnailFactory _thumbnailFactory; + public event EventHandler Minimized; public event EventHandler Maximized; public event EventHandler Restored; - private readonly Dictionary _previews; + private readonly Dictionary _previews; private DispatcherTimer _dispatcherTimer; private IntPtr _activeClientHandle; @@ -43,7 +45,7 @@ namespace EveOPreview this._activeClientHandle = (IntPtr)0; this._activeClientTitle = ""; - _previews = new Dictionary(); + this._previews = new Dictionary(); _xmlBadToOkChars = new Dictionary(); _xmlBadToOkChars["<"] = "---lt---"; @@ -71,6 +73,8 @@ namespace EveOPreview _dispatcherTimer.Interval = new TimeSpan(0, 0, 1); _dispatcherTimer.Start(); + this._thumbnailFactory = new ThumbnailFactory(); + _isInitialized = true; previews_check_listbox.DisplayMember = "Text"; @@ -151,11 +155,11 @@ namespace EveOPreview if (!_previews.ContainsKey(process.MainWindowHandle) && process.MainWindowTitle != "") { - _previews[process.MainWindowHandle] = new Preview(this, process.MainWindowHandle, "...", sync_size); + _previews[process.MainWindowHandle] = this._thumbnailFactory.Create(this, process.MainWindowHandle, "...", sync_size); // apply more thumbnail specific options - _previews[process.MainWindowHandle].MakeTopMost(Properties.Settings.Default.always_on_top); - set_thumbnail_frame_style(_previews[process.MainWindowHandle], Properties.Settings.Default.show_thumb_frames); + _previews[process.MainWindowHandle].SetTopMost(Properties.Settings.Default.always_on_top); + _previews[process.MainWindowHandle].SetWindowFrames(Properties.Settings.Default.show_thumb_frames); // add a preview also previews_check_listbox.BeginUpdate(); @@ -165,14 +169,14 @@ namespace EveOPreview refresh_client_window_locations(process); } - else if (_previews.ContainsKey(process.MainWindowHandle) && process.MainWindowTitle != _previews[process.MainWindowHandle].Text) //or update the preview titles + else if (_previews.ContainsKey(process.MainWindowHandle) && process.MainWindowTitle != _previews[process.MainWindowHandle].GetLabel()) //or update the preview titles { _previews[process.MainWindowHandle].SetLabel(process.MainWindowTitle); - string key = _previews[process.MainWindowHandle].Text; + string key = _previews[process.MainWindowHandle].GetLabel(); string value; if (_flatLayoutShortcuts.TryGetValue(key, out value)) { - _previews[process.MainWindowHandle].registerShortcut(value); + _previews[process.MainWindowHandle].RegisterShortcut(value); } refresh_client_window_locations(process); } @@ -201,7 +205,7 @@ namespace EveOPreview previews_check_listbox.Items.Remove(_previews[processHandle]); previews_check_listbox.EndUpdate(); - _previews[processHandle].Close(); + _previews[processHandle].CloseThumbnail(); _previews.Remove(processHandle); } @@ -362,27 +366,27 @@ namespace EveOPreview el3.Save("client_layout.xml"); } - private void handle_unique_layout(Preview preview, string last_known_active_window) + private void handle_unique_layout(IThumbnail thumbnailWindow, string last_known_active_window) { Dictionary layout; if (_uniqueLayouts.TryGetValue(last_known_active_window, out layout)) { Point new_loc; - if (Properties.Settings.Default.unique_layout && layout.TryGetValue(preview.Text, out new_loc)) + if (Properties.Settings.Default.unique_layout && layout.TryGetValue(thumbnailWindow.GetLabel(), out new_loc)) { - preview.doMove(new_loc); + thumbnailWindow.SetLocation(new_loc); } else { // create inner dict - layout[preview.Text] = preview.Location; + layout[thumbnailWindow.GetLabel()] = thumbnailWindow.GetLocation(); } } else if (last_known_active_window != "") { // create outer dict _uniqueLayouts[last_known_active_window] = new Dictionary(); - _uniqueLayouts[last_known_active_window][preview.Text] = preview.Location; + _uniqueLayouts[last_known_active_window][thumbnailWindow.GetLabel()] = thumbnailWindow.GetLocation(); } } @@ -417,34 +421,34 @@ namespace EveOPreview } - public void preview_did_switch() + public void NotifyPreviewSwitch() { update_client_locations(); store_layout(); //todo: check if it actually changed ... - foreach (KeyValuePair entry in _previews) + foreach (KeyValuePair entry in _previews) { - entry.Value.MakeTopMost(Properties.Settings.Default.always_on_top); + entry.Value.SetTopMost(Properties.Settings.Default.always_on_top); } } - private void handle_flat_layout(Preview preview) + private void handle_flat_layout(IThumbnail thumbnailWindow) { Point layout; - if (_flatLayout.TryGetValue(preview.Text, out layout)) + if (_flatLayout.TryGetValue(thumbnailWindow.GetLabel(), out layout)) { - preview.doMove(layout); + thumbnailWindow.SetLocation(layout); } - else if (preview.Text != "") + else if (thumbnailWindow.GetLabel() != "") { - _flatLayout[preview.Text] = preview.Location; + _flatLayout[thumbnailWindow.GetLabel()] = thumbnailWindow.GetLocation(); } } private bool window_is_preview_or_client(IntPtr window) { bool active_window_is_right_type = false; - foreach (KeyValuePair entry in _previews) + foreach (KeyValuePair entry in _previews) { if (entry.Key == window || entry.Value.IsPreviewHandle(window)) { @@ -461,19 +465,19 @@ namespace EveOPreview IntPtr active_window = DwmApiNativeMethods.GetForegroundWindow(); // hide, show, resize and move - foreach (KeyValuePair entry in _previews) + foreach (KeyValuePair entry in _previews) { if (!window_is_preview_or_client(active_window) && Properties.Settings.Default.hide_all) { - entry.Value.Hide(); + entry.Value.HideThumbnail(); } else if (entry.Key == _activeClientHandle && Properties.Settings.Default.hide_active) { - entry.Value.Hide(); + entry.Value.HideThumbnail(); } else { - entry.Value.Show(); + entry.Value.ShowThumbnail(); if (Properties.Settings.Default.unique_layout) { handle_unique_layout(entry.Value, _activeClientTitle); @@ -483,19 +487,16 @@ namespace EveOPreview handle_flat_layout(entry.Value); } } - entry.Value.hover_zoom = Properties.Settings.Default.zoom_on_hover; - entry.Value.show_overlay = Properties.Settings.Default.show_overlay; - if (!entry.Value.is_hovered_over) - { - entry.Value.Opacity = Properties.Settings.Default.opacity; - } + entry.Value.IsZoomEnabled = Properties.Settings.Default.zoom_on_hover; + entry.Value.IsOverlayEnabled = Properties.Settings.Default.show_overlay; + entry.Value.SetOpacity(Properties.Settings.Default.opacity); } DwmApiNativeMethods.DwmIsCompositionEnabled(); } - public void syncronize_preview_size(Size sync_size) + public void SyncPreviewSize(Size sync_size) { if (!_isInitialized) { return; } @@ -508,9 +509,9 @@ namespace EveOPreview option_sync_size_x.Text = sync_size.Width.ToString(); option_sync_size_y.Text = sync_size.Height.ToString(); - foreach (KeyValuePair entry in _previews) + foreach (KeyValuePair entry in _previews) { - if (entry.Value.Handle != DwmApiNativeMethods.GetForegroundWindow()) + if (entry.Value.IsPreviewHandle(DwmApiNativeMethods.GetForegroundWindow())) { entry.Value.SetSize(sync_size); } @@ -521,7 +522,7 @@ namespace EveOPreview } - public void register_preview_position(string preview_title, Point position) + public void UpdatePreviewPosition(string preview_title, Point position) { if (Properties.Settings.Default.unique_layout) @@ -621,7 +622,7 @@ namespace EveOPreview Properties.Settings.Default.Save(); // resize - syncronize_preview_size(new Size((int)Properties.Settings.Default.sync_resize_x, + SyncPreviewSize(new Size((int)Properties.Settings.Default.sync_resize_x, (int)Properties.Settings.Default.sync_resize_y)); } @@ -646,18 +647,6 @@ namespace EveOPreview } - void set_thumbnail_frame_style(Preview preview, bool show_frames) - { - if (show_frames) - { - preview.FormBorderStyle = FormBorderStyle.SizableToolWindow; - } - else - { - preview.FormBorderStyle = FormBorderStyle.None; - } - } - private void option_show_thumbnail_frames_CheckedChanged(object sender, EventArgs e) { Properties.Settings.Default.show_thumb_frames = option_show_thumbnail_frames.Checked; @@ -672,7 +661,7 @@ namespace EveOPreview foreach (var thumbnail in _previews) { - set_thumbnail_frame_style(thumbnail.Value, Properties.Settings.Default.show_thumb_frames); + thumbnail.Value.SetWindowFrames(Properties.Settings.Default.show_thumb_frames); } } @@ -763,7 +752,7 @@ namespace EveOPreview private void checkedListBox1_SelectedIndexChanged2(object sender, EventArgs e) { System.Windows.Forms.ItemCheckEventArgs arg = (System.Windows.Forms.ItemCheckEventArgs)e; - ((Preview)this.previews_check_listbox.Items[arg.Index]).MakeHidden(arg.NewValue == System.Windows.Forms.CheckState.Checked); + ((ThumbnailWindow)this.previews_check_listbox.Items[arg.Index]).IsPreviewEnabled = (arg.NewValue != System.Windows.Forms.CheckState.Checked); refresh_thumbnails(); } diff --git a/Eve-O-Preview/Preview/Preview.cs b/Eve-O-Preview/Preview/Preview.cs deleted file mode 100644 index 5e2e654..0000000 --- a/Eve-O-Preview/Preview/Preview.cs +++ /dev/null @@ -1,383 +0,0 @@ -using System; -using System.Drawing; -using System.Windows.Forms; - -namespace EveOPreview -{ - public partial class Preview : Form - { - #region Private fields - private readonly bool _isInitializing; - private readonly IntPtr _sourceWindow; - private readonly MainForm _parentForm; - private readonly PreviewOverlay _overlay; - - private Size _normalSize; - private Point _normalPosition; - - private bool _isThumbnailSetUp; - private DWM_THUMBNAIL_PROPERTIES _Thumbnail; - private IntPtr _ThumbnailHandle; - #endregion - - // TODO Hide the parent form behind an interface - public Preview(MainForm parent, IntPtr sourceWindow, String title, Size size) - { - this._isInitializing = true; - - this._sourceWindow = sourceWindow; - this._parentForm = parent; - - this._isThumbnailSetUp = false; - - InitializeComponent(); - - this.Text = title; - - this._overlay = new PreviewOverlay(this.render_area_Click); - - this._isInitializing = false; - - this.SetSize(size); - } - - public bool IsPreviewHandle(IntPtr handle) - { - return (this.Handle == handle) || (this._overlay.Handle == handle); - } - - public void SetSize(Size size) - { - this.Size = size; - this._normalSize = this.Size; - this._normalPosition = this.Location; - } - - public void MakeTopMost(bool topmost) - { - if (!this.hide) - { - return; - } - - this.TopMost = topmost; - this.MakeOverlayTopMost(); - } - - protected override CreateParams CreateParams - { - get - { - var Params = base.CreateParams; - Params.ExStyle |= (int)DwmApiNativeMethods.WS_EX_TOOLWINDOW; - return Params; - } - } - - private void MakeOverlayTopMost() - { - this._overlay.TopMost = true; - } - - private void RefreshPreview() - { - if (this._isInitializing) - { - return; - } - - if (DwmApiNativeMethods.DwmIsCompositionEnabled()) - { - if (this._isThumbnailSetUp == false) - { - this.SetUpThumbnail(); - } - - this._Thumbnail.rcDestination = new RECT(0, 0, this.ClientRectangle.Right, this.ClientRectangle.Bottom); - DwmApiNativeMethods.DwmUpdateThumbnailProperties(this._ThumbnailHandle, this._Thumbnail); - } - else - { - this._isThumbnailSetUp = false; - } - - Size overlaySize = this.RenderAreaPictureBox.Size; - overlaySize.Width -= 2 * 5; - overlaySize.Height -= 2 * 5; - - Point overlayLocation = this.Location; - overlayLocation.X += 5 + (this.Size.Width - this.RenderAreaPictureBox.Size.Width) / 2; - overlayLocation.Y += 5 + (this.Size.Height - this.RenderAreaPictureBox.Size.Height) - (this.Size.Width - this.RenderAreaPictureBox.Size.Width) / 2; - - this._overlay.Size = overlaySize; - this._overlay.Location = overlayLocation; - } - - private void SetUpThumbnail() - { - // TODO Remove this API call on Win 8+ hosts - if (this._isThumbnailSetUp || !DwmApiNativeMethods.DwmIsCompositionEnabled()) - { - return; - } - - this._ThumbnailHandle = DwmApiNativeMethods.DwmRegisterThumbnail(this.Handle, this._sourceWindow); - - this._Thumbnail = new DWM_THUMBNAIL_PROPERTIES(); - this._Thumbnail.dwFlags = DWM_THUMBNAIL_PROPERTIES.DWM_TNP_VISIBLE - + DWM_THUMBNAIL_PROPERTIES.DWM_TNP_OPACITY - + DWM_THUMBNAIL_PROPERTIES.DWM_TNP_RECTDESTINATION - + DWM_THUMBNAIL_PROPERTIES.DWM_TNP_SOURCECLIENTAREAONLY; - this._Thumbnail.opacity = 255; - this._Thumbnail.fVisible = true; - this._Thumbnail.fSourceClientAreaOnly = true; - this._Thumbnail.rcDestination = new RECT(0, 0, ClientRectangle.Right, ClientRectangle.Bottom); - - DwmApiNativeMethods.DwmUpdateThumbnailProperties(_ThumbnailHandle, _Thumbnail); - - _isThumbnailSetUp = true; - } - - - public bool show_overlay = true; - public bool hover_zoom = true; - public bool is_zoomed = false; - public bool is_hovered_over = false; - - private bool mouse_over_lock = false; - - - private Hotkey hotkey; - - private bool hide = false; - - public void MakeHidden(bool wha) - { - hide = wha; - } - - public override string ToString() - { - return this.Text; - } - - public void preview_MouseHover(object sender, System.EventArgs e) - { - if (!mouse_over_lock) - { - mouse_over_lock = true; - if (hover_zoom) - doZoom(); - - this.MakeTopMost(true); - } - this.Opacity = 1.0f; - this.is_hovered_over = true; - RefreshPreview(); - } - - public void registerShortcut(string shortcut) - { - if (shortcut == "") - return; - var cvt = new KeysConverter(); - var key = (Keys)cvt.ConvertFrom(shortcut); - - Hotkey hotkey = new Hotkey(); - - if ((key & Keys.Shift) == Keys.Shift) - { - hotkey.Shift = true; - } - if ((key & Keys.Alt) == Keys.Alt) - { - hotkey.Alt = true; - } - if ((key & Keys.Control) == Keys.Control) - { - hotkey.Control = true; - } - - key = key & ~Keys.Shift & ~Keys.Alt & ~Keys.Control; - hotkey.KeyCode = key; - hotkey.Register(this); - // TODO Make this a method - hotkey.Pressed += (s, e) => { bring_client_to_foreground(); this._parentForm.preview_did_switch(); }; - - this.hotkey = hotkey; - } - - public void doZoom() - { - if (is_zoomed) - return; - - is_zoomed = true; - - float hover_zoom_factor = Properties.Settings.Default.zoom_amount; - - _normalSize = Size; - _normalPosition = Location; - - Size = new Size((int)(hover_zoom_factor * (float)Size.Width), (int)(hover_zoom_factor * (float)Size.Height)); - - switch ((ZoomAnchor)Properties.Settings.Default.zoom_anchor) - { - case (ZoomAnchor.NW): - break; - case (ZoomAnchor.N): - Location = new Point(Location.X - Size.Width / 2 + _normalSize.Width / 2, Location.Y); - break; - case (ZoomAnchor.NE): - Location = new Point(Location.X - Size.Width + _normalSize.Width, Location.Y); - break; - - case (ZoomAnchor.W): - Location = new Point(Location.X, Location.Y - Size.Height / 2 + _normalSize.Height / 2); - break; - case (ZoomAnchor.C): - Location = new Point(Location.X - Size.Width / 2 + _normalSize.Width / 2, Location.Y - Size.Height / 2 + _normalSize.Height / 2); - break; - case (ZoomAnchor.E): - Location = new Point(Location.X - Size.Width + _normalSize.Width, Location.Y - Size.Height / 2 + _normalSize.Height / 2); - break; - - case (ZoomAnchor.SW): - Location = new Point(Location.X, Location.Y - Size.Height + _normalSize.Height); - break; - case (ZoomAnchor.S): - Location = new Point(Location.X - Size.Width / 2 + _normalSize.Width / 2, Location.Y - Size.Height + _normalSize.Height); - break; - case (ZoomAnchor.SE): - Location = new Point(Location.X - Size.Width + _normalSize.Width, Location.Y - Size.Height + _normalSize.Height); - break; - } - } - - public void restoreZoom() - { - if (!is_zoomed) - return; - - Size = _normalSize; - Location = _normalPosition; - is_zoomed = false; - } - - public void preview_MouseLeave(object sender, System.EventArgs e) - { - if (mouse_over_lock) - { - if (hover_zoom) - { - restoreZoom(); - } - mouse_over_lock = false; - } - this.is_hovered_over = false; - this.Opacity = Properties.Settings.Default.opacity; - RefreshPreview(); - } - - protected override void OnResize(EventArgs e) - { - RefreshPreview(); - base.OnResize(e); - - if (!this._isInitializing && !mouse_over_lock) - this._parentForm.syncronize_preview_size(this.Size); - } - - protected override void OnMove(EventArgs e) - { - base.OnMove(e); - if (!this._isInitializing && !mouse_over_lock) - this._parentForm.register_preview_position(this.Text, this.Location); - - RefreshPreview(); - } - - public void doMove(Point position) - { - if (!this._isInitializing && !mouse_over_lock) - Location = position; - - RefreshPreview(); - } - - public void SetLabel(String label) - { - this.Text = label; - this._overlay.SetOverlayLabel(label); - } - - new public void Show() - { - if (!hide) - { - base.Show(); - if (show_overlay) - { - this._overlay.Show(); - this.MakeOverlayTopMost(); - } - else - { - this._overlay.Hide(); - } - } - else - { - this.Hide(); - } - } - - new public void Hide() - { - base.Hide(); - this._overlay.Hide(); - } - - new public void Close() - { - this._overlay.Close(); - base.Close(); - } - - private void Preview_Load(object sender, EventArgs e) - { - - } - - public void bring_client_to_foreground() - { - DwmApiNativeMethods.SetForegroundWindow(this._sourceWindow); - int style = DwmApiNativeMethods.GetWindowLong(this._sourceWindow, DwmApiNativeMethods.GWL_STYLE); - if ((style & DwmApiNativeMethods.WS_MAXIMIZE) == DwmApiNativeMethods.WS_MAXIMIZE) - { - //It's maximized - } - else if ((style & DwmApiNativeMethods.WS_MINIMIZE) == DwmApiNativeMethods.WS_MINIMIZE) - { - DwmApiNativeMethods.ShowWindowAsync(this._sourceWindow, DwmApiNativeMethods.SW_SHOWNORMAL); - } - } - - public void render_area_Click(object sender, MouseEventArgs e) - { - if (e.Button == MouseButtons.Left) - { - bring_client_to_foreground(); - this._parentForm.preview_did_switch(); - } - if (e.Button == MouseButtons.Right) - { - // do smth cool? - } - if (e.Button == MouseButtons.Middle) - { - // do smth cool? - } - } - } -} \ No newline at end of file diff --git a/Eve-O-Preview/Thumbnail/IThumbnail.cs b/Eve-O-Preview/Thumbnail/IThumbnail.cs new file mode 100644 index 0000000..1b396c4 --- /dev/null +++ b/Eve-O-Preview/Thumbnail/IThumbnail.cs @@ -0,0 +1,31 @@ +using System; +using System.Drawing; + +namespace EveOPreview +{ + public interface IThumbnail + { + bool IsZoomEnabled { get; set; } + bool IsPreviewEnabled { get; set; } + bool IsOverlayEnabled { get; set; } + + bool IsPreviewHandle(IntPtr handle); + + void ShowThumbnail(); + void HideThumbnail(); + void CloseThumbnail(); + + void RegisterShortcut(string shortcut); + + void SetLabel(string label); + string GetLabel(); + + void SetLocation(Point location); + Point GetLocation(); + + void SetOpacity(double opacity); + void SetTopMost(bool topmost); + void SetWindowFrames(bool enable); + void SetSize(Size size); + } +} \ No newline at end of file diff --git a/Eve-O-Preview/Thumbnail/ThumbnailFactory.cs b/Eve-O-Preview/Thumbnail/ThumbnailFactory.cs new file mode 100644 index 0000000..a603043 --- /dev/null +++ b/Eve-O-Preview/Thumbnail/ThumbnailFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Drawing; + +namespace EveOPreview +{ + public class ThumbnailFactory + { + public IThumbnail Create(MainForm parent, IntPtr sourceWindow, string title, Size size) + { + return new ThumbnailWindow(parent, sourceWindow, title, size); + } + } +} \ No newline at end of file diff --git a/Eve-O-Preview/Preview/PreviewOverlay.Designer.cs b/Eve-O-Preview/Thumbnail/ThumbnailOverlay.Designer.cs similarity index 95% rename from Eve-O-Preview/Preview/PreviewOverlay.Designer.cs rename to Eve-O-Preview/Thumbnail/ThumbnailOverlay.Designer.cs index d49b54f..fee5bc5 100644 --- a/Eve-O-Preview/Preview/PreviewOverlay.Designer.cs +++ b/Eve-O-Preview/Thumbnail/ThumbnailOverlay.Designer.cs @@ -1,6 +1,6 @@ namespace EveOPreview { - partial class PreviewOverlay + partial class ThumbnailOverlay { /// /// Required designer variable. @@ -71,7 +71,7 @@ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.MaximizeBox = false; this.MinimizeBox = false; - this.Name = "PreviewOverlay"; + this.Name = "ThumbnailOverlay"; this.ShowIcon = false; this.ShowInTaskbar = false; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; diff --git a/Eve-O-Preview/Preview/PreviewOverlay.cs b/Eve-O-Preview/Thumbnail/ThumbnailOverlay.cs similarity index 79% rename from Eve-O-Preview/Preview/PreviewOverlay.cs rename to Eve-O-Preview/Thumbnail/ThumbnailOverlay.cs index a2aec56..434b648 100644 --- a/Eve-O-Preview/Preview/PreviewOverlay.cs +++ b/Eve-O-Preview/Thumbnail/ThumbnailOverlay.cs @@ -3,11 +3,11 @@ using System.Windows.Forms; namespace EveOPreview { - public partial class PreviewOverlay : Form + public partial class ThumbnailOverlay : Form { private readonly Action _areaClickAction; - public PreviewOverlay(Action areaClickAction) + public ThumbnailOverlay(Action areaClickAction) { this._areaClickAction = areaClickAction; InitializeComponent(); diff --git a/Eve-O-Preview/Preview/PreviewOverlay.resx b/Eve-O-Preview/Thumbnail/ThumbnailOverlay.resx similarity index 100% rename from Eve-O-Preview/Preview/PreviewOverlay.resx rename to Eve-O-Preview/Thumbnail/ThumbnailOverlay.resx diff --git a/Eve-O-Preview/Preview/Preview.Designer.cs b/Eve-O-Preview/Thumbnail/ThumbnailWindow.Designer.cs similarity index 89% rename from Eve-O-Preview/Preview/Preview.Designer.cs rename to Eve-O-Preview/Thumbnail/ThumbnailWindow.Designer.cs index d33bce3..0134590 100644 --- a/Eve-O-Preview/Preview/Preview.Designer.cs +++ b/Eve-O-Preview/Thumbnail/ThumbnailWindow.Designer.cs @@ -1,6 +1,6 @@ namespace EveOPreview { - partial class Preview + partial class ThumbnailWindow { /// /// Required designer variable. @@ -31,9 +31,9 @@ namespace EveOPreview this.RenderAreaPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.RenderAreaPictureBox.TabIndex = 0; this.RenderAreaPictureBox.TabStop = false; - this.RenderAreaPictureBox.MouseLeave += new System.EventHandler(this.preview_MouseLeave); - this.RenderAreaPictureBox.MouseHover += new System.EventHandler(this.preview_MouseHover); - this.RenderAreaPictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.render_area_Click); + this.RenderAreaPictureBox.MouseLeave += new System.EventHandler(this.Preview_MouseLeave); + this.RenderAreaPictureBox.MouseHover += new System.EventHandler(this.Preview_MouseHover); + this.RenderAreaPictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Preview_Click); // // Preview // @@ -47,13 +47,12 @@ namespace EveOPreview this.MaximizeBox = false; this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(64, 64); - this.Name = "Preview"; + this.Name = "ThumbnailWindow"; this.Opacity = 0.1D; this.ShowIcon = false; this.ShowInTaskbar = false; this.Text = "Preview"; this.TopMost = true; - this.Load += new System.EventHandler(this.Preview_Load); ((System.ComponentModel.ISupportInitialize)(this.RenderAreaPictureBox)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/Eve-O-Preview/Thumbnail/ThumbnailWindow.cs b/Eve-O-Preview/Thumbnail/ThumbnailWindow.cs new file mode 100644 index 0000000..71c05c9 --- /dev/null +++ b/Eve-O-Preview/Thumbnail/ThumbnailWindow.cs @@ -0,0 +1,437 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace EveOPreview +{ + public partial class ThumbnailWindow : Form, IThumbnail + { + #region Private fields + private readonly bool _isInitializing; + private readonly IntPtr _sourceWindow; + private readonly MainForm _parentForm; + private readonly ThumbnailOverlay _overlay; + private Hotkey _hotkey; // This field stores the hotkey reference + + private Size _baseSize; + private Point _basePosition; + + private bool _isThumbnailSetUp; + private DWM_THUMBNAIL_PROPERTIES _Thumbnail; + private IntPtr _ThumbnailHandle; + + private bool _ignoreMouseOverEvent; + private bool _isHoverEffectActive; + private bool _isZoomActive; + #endregion + + // This constructor should never be used directly + public ThumbnailWindow(MainForm parent, IntPtr sourceWindow, string title, Size size) + { + this._isInitializing = true; + + this.IsPreviewEnabled = true; + this.IsOverlayEnabled = true; + + this._sourceWindow = sourceWindow; + this._parentForm = parent; + + this._isThumbnailSetUp = false; + this._ignoreMouseOverEvent = false; + this._isHoverEffectActive = false; + this._isZoomActive = false; + + InitializeComponent(); + + this.Text = title; + + this._overlay = new ThumbnailOverlay(this.Preview_Click); + + this._isInitializing = false; + + this.SetSize(size); + } + + public bool IsZoomEnabled { get; set; } + + public bool IsPreviewEnabled { get; set; } + + public bool IsOverlayEnabled { get; set; } + + public bool IsPreviewHandle(IntPtr handle) + { + return (this.Handle == handle) || (this._overlay.Handle == handle); + } + + public void ShowThumbnail() + { + if (this.IsPreviewEnabled) + { + this.Show(); + if (this.IsOverlayEnabled) + { + this._overlay.Show(); + this.MakeOverlayTopMost(); + } + else + { + this._overlay.Hide(); + } + } + else + { + this.HideThumbnail(); + } + } + + public void HideThumbnail() + { + this.Hide(); + this._overlay.Hide(); + } + + public void CloseThumbnail() + { + this._overlay.Close(); + this.Close(); + } + + public void SetLabel(string label) + { + this.Text = label; + this._overlay.SetOverlayLabel(label); + } + + public string GetLabel() + { + return this.Text; + } + + public void SetSize(Size size) + { + this.Size = size; + this._baseSize = this.Size; + this._basePosition = this.Location; + } + + public void SetLocation(Point location) + { + if (!(this._isInitializing || this._ignoreMouseOverEvent)) + { + this.Location = location; + } + + this.RefreshPreview(); + } + + public Point GetLocation() + { + return this.Location; + } + + public void SetOpacity(double opacity) + { + if (this._isHoverEffectActive) + { + return; + } + + this.Opacity = opacity; + } + + public void RegisterShortcut(string shortcut) + { + if (String.IsNullOrEmpty(shortcut)) + { + return; + } + + KeysConverter converter = new KeysConverter(); + object keysObject = converter.ConvertFrom(shortcut); + if (keysObject == null) + { + return; + } + + Keys key = (Keys)keysObject; + + Hotkey hotkey = new Hotkey(); + + if ((key & Keys.Shift) == Keys.Shift) + { + hotkey.Shift = true; + } + + if ((key & Keys.Alt) == Keys.Alt) + { + hotkey.Alt = true; + } + + if ((key & Keys.Control) == Keys.Control) + { + hotkey.Control = true; + } + + key = key & ~Keys.Shift & ~Keys.Alt & ~Keys.Control; + hotkey.KeyCode = key; + hotkey.Pressed += Hotkey_Pressed; + hotkey.Register(this); + + this._hotkey = hotkey; + } + + public void SetTopMost(bool topmost) + { + if (!this.IsPreviewEnabled) + { + return; + } + + this.TopMost = topmost; + this.MakeOverlayTopMost(); + } + + public void SetWindowFrames(bool enable) + { + this.FormBorderStyle = enable ? FormBorderStyle.SizableToolWindow : FormBorderStyle.None; + } + + protected override CreateParams CreateParams + { + get + { + var Params = base.CreateParams; + Params.ExStyle |= (int)DwmApiNativeMethods.WS_EX_TOOLWINDOW; + return Params; + } + } + + private void Preview_MouseHover(object sender, EventArgs e) + { + if (!this._ignoreMouseOverEvent) + { + this._ignoreMouseOverEvent = true; + if (this.IsZoomEnabled) + { + this.ZoomIn(); + } + + this.SetTopMost(true); + } + + this.Opacity = 1.0f; + this._isHoverEffectActive = true; + + this.RefreshPreview(); + } + + private void Preview_MouseLeave(object sender, EventArgs e) + { + if (this._ignoreMouseOverEvent) + { + if (this.IsZoomEnabled) + { + this.ZoomOut(); + } + + this._ignoreMouseOverEvent = false; + } + + this._isHoverEffectActive = false; + this.Opacity = Properties.Settings.Default.opacity; // TODO Use local object + + this.RefreshPreview(); + } + + private void Preview_Click(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + this.ActivateClient(); + this._parentForm.NotifyPreviewSwitch(); + } + + if (e.Button == MouseButtons.Right) + { + // do smth cool? + } + + if (e.Button == MouseButtons.Middle) + { + // do smth cool? + } + } + + private void Hotkey_Pressed(Object sender, EventArgs e) + { + this.ActivateClient(); + this._parentForm.NotifyPreviewSwitch(); + } + + protected override void OnResize(EventArgs e) + { + this.RefreshPreview(); + + base.OnResize(e); + + if (!(this._isInitializing || this._ignoreMouseOverEvent)) + { + this._parentForm.SyncPreviewSize(this.Size); + } + } + + protected override void OnMove(EventArgs e) + { + base.OnMove(e); + + if (!(this._isInitializing || this._ignoreMouseOverEvent)) + { + this._parentForm.UpdatePreviewPosition(this.Text, this.Location); + } + + this.RefreshPreview(); + } + + private void MakeOverlayTopMost() + { + this._overlay.TopMost = true; + } + + private void RefreshPreview() + { + if (this._isInitializing) + { + return; + } + + if (DwmApiNativeMethods.DwmIsCompositionEnabled()) + { + if (this._isThumbnailSetUp == false) + { + this.SetUpThumbnail(); + } + + this._Thumbnail.rcDestination = new RECT(0, 0, this.ClientRectangle.Right, this.ClientRectangle.Bottom); + DwmApiNativeMethods.DwmUpdateThumbnailProperties(this._ThumbnailHandle, this._Thumbnail); + } + else + { + this._isThumbnailSetUp = false; + } + + Size overlaySize = this.RenderAreaPictureBox.Size; + overlaySize.Width -= 2 * 5; + overlaySize.Height -= 2 * 5; + + Point overlayLocation = this.Location; + overlayLocation.X += 5 + (this.Size.Width - this.RenderAreaPictureBox.Size.Width) / 2; + overlayLocation.Y += 5 + (this.Size.Height - this.RenderAreaPictureBox.Size.Height) - (this.Size.Width - this.RenderAreaPictureBox.Size.Width) / 2; + + this._overlay.Size = overlaySize; + this._overlay.Location = overlayLocation; + } + + private void SetUpThumbnail() + { + if (this._isThumbnailSetUp || !DwmApiNativeMethods.DwmIsCompositionEnabled()) + { + return; + } + + this._ThumbnailHandle = DwmApiNativeMethods.DwmRegisterThumbnail(this.Handle, this._sourceWindow); + + this._Thumbnail = new DWM_THUMBNAIL_PROPERTIES(); + this._Thumbnail.dwFlags = DWM_TNP_CONSTANTS.DWM_TNP_VISIBLE + + DWM_TNP_CONSTANTS.DWM_TNP_OPACITY + + DWM_TNP_CONSTANTS.DWM_TNP_RECTDESTINATION + + DWM_TNP_CONSTANTS.DWM_TNP_SOURCECLIENTAREAONLY; + this._Thumbnail.opacity = 255; + this._Thumbnail.fVisible = true; + this._Thumbnail.fSourceClientAreaOnly = true; + this._Thumbnail.rcDestination = new RECT(0, 0, this.ClientRectangle.Right, this.ClientRectangle.Bottom); + + DwmApiNativeMethods.DwmUpdateThumbnailProperties(this._ThumbnailHandle, this._Thumbnail); + + this._isThumbnailSetUp = true; + } + + private void ZoomIn() + { + if (this._isZoomActive) + { + return; + } + + this._isZoomActive = true; + + // TODO Use global settings object + float zoomFactor = Properties.Settings.Default.zoom_amount; + + this._baseSize = this.Size; + this._basePosition = this.Location; + + this.Size = new Size((int)(zoomFactor * this.Size.Width), (int)(zoomFactor * this.Size.Height)); + + // TODO Use global settings object + switch ((ZoomAnchor)Properties.Settings.Default.zoom_anchor) + { + case ZoomAnchor.NW: + break; + case ZoomAnchor.N: + this.Location = new Point(this.Location.X - this.Size.Width / 2 + this._baseSize.Width / 2, this.Location.Y); + break; + case ZoomAnchor.NE: + this.Location = new Point(this.Location.X - this.Size.Width + this._baseSize.Width, this.Location.Y); + break; + + case ZoomAnchor.W: + this.Location = new Point(this.Location.X, this.Location.Y - this.Size.Height / 2 + this._baseSize.Height / 2); + break; + case ZoomAnchor.C: + this.Location = new Point(this.Location.X - this.Size.Width / 2 + this._baseSize.Width / 2, this.Location.Y - this.Size.Height / 2 + this._baseSize.Height / 2); + break; + case ZoomAnchor.E: + this.Location = new Point(this.Location.X - this.Size.Width + this._baseSize.Width, this.Location.Y - this.Size.Height / 2 + this._baseSize.Height / 2); + break; + + case ZoomAnchor.SW: + this.Location = new Point(this.Location.X, this.Location.Y - this.Size.Height + this._baseSize.Height); + break; + case ZoomAnchor.S: + this.Location = new Point(this.Location.X - this.Size.Width / 2 + this._baseSize.Width / 2, this.Location.Y - this.Size.Height + this._baseSize.Height); + break; + case ZoomAnchor.SE: + this.Location = new Point(this.Location.X - this.Size.Width + this._baseSize.Width, this.Location.Y - this.Size.Height + this._baseSize.Height); + break; + } + } + + private void ZoomOut() + { + if (!this._isZoomActive) + { + return; + } + + this.Size = this._baseSize; + this.Location = this._basePosition; + + this._isZoomActive = false; + } + + private void ActivateClient() + { + DwmApiNativeMethods.SetForegroundWindow(this._sourceWindow); + + int style = DwmApiNativeMethods.GetWindowLong(this._sourceWindow, DwmApiNativeMethods.GWL_STYLE); + if ((style & DwmApiNativeMethods.WS_MAXIMIZE) == DwmApiNativeMethods.WS_MAXIMIZE) + { + // Client is already maximized, no action is required + } + else if ((style & DwmApiNativeMethods.WS_MINIMIZE) == DwmApiNativeMethods.WS_MINIMIZE) + { + DwmApiNativeMethods.ShowWindowAsync(this._sourceWindow, DwmApiNativeMethods.SW_SHOWNORMAL); + } + } + } +} \ No newline at end of file diff --git a/Eve-O-Preview/Preview/Preview.resx b/Eve-O-Preview/Thumbnail/ThumbnailWindow.resx similarity index 100% rename from Eve-O-Preview/Preview/Preview.resx rename to Eve-O-Preview/Thumbnail/ThumbnailWindow.resx