GUI/GadgetWindow.cs
changeset 183 3096735e99b2
parent 180 d40f49d45614
child 202 551243a66b32
     1.1 --- a/GUI/GadgetWindow.cs	Wed Sep 08 19:29:58 2010 +0000
     1.2 +++ b/GUI/GadgetWindow.cs	Mon Sep 13 22:34:08 2010 +0000
     1.3 @@ -42,10 +42,11 @@
     1.4  using System.Windows.Forms;
     1.5  
     1.6  namespace OpenHardwareMonitor.GUI {
     1.7 +
     1.8    public class GadgetWindow : NativeWindow {
     1.9  
    1.10      private bool visible = false;
    1.11 -    private bool lockPosition = false;
    1.12 +    private bool lockPositionAndSize = false;
    1.13      private bool alwaysOnTop = false;
    1.14      private byte opacity = 255;
    1.15      private Point location = new Point(100, 100);
    1.16 @@ -100,9 +101,9 @@
    1.17  
    1.18      protected virtual CreateParams CreateParams {
    1.19        get {
    1.20 -        CreateParams cp = new CreateParams();
    1.21 -        cp.Width = size.Width;
    1.22 -        cp.Height = size.Height;
    1.23 +        CreateParams cp = new CreateParams();        
    1.24 +        cp.Width = 4096;
    1.25 +        cp.Height = 4096;
    1.26          cp.X = location.X;
    1.27          cp.Y = location.Y;
    1.28          cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW;
    1.29 @@ -112,57 +113,92 @@
    1.30  
    1.31      protected override void WndProc(ref Message message) {
    1.32        switch (message.Msg) {
    1.33 -        case WM_COMMAND:
    1.34 -          // need to dispatch the message for the context menu
    1.35 -          if (message.LParam == IntPtr.Zero) 
    1.36 -            commandDispatch.Invoke(null, new object[] { 
    1.37 -              message.WParam.ToInt32() & 0xFFFF });          
    1.38 -          break;
    1.39 -        case WM_NCHITTEST:           
    1.40 -          // all pixels of the form belong to the caption
    1.41 -          message.Result = HTCAPTION; 
    1.42 -          break;
    1.43 -        case WM_NCLBUTTONDBLCLK:  
    1.44 -          message.Result = IntPtr.Zero; break;
    1.45 -        case WM_NCRBUTTONDOWN:
    1.46 -          message.Result = IntPtr.Zero; break;
    1.47 -        case WM_NCRBUTTONUP:
    1.48 -          if (contextMenu != null)
    1.49 -            ShowContextMenu(new Point(
    1.50 -              (int)((uint)message.LParam & 0xFFFF), 
    1.51 -              (int)(((uint)message.LParam >>16) & 0xFFFF)));          
    1.52 -          message.Result = IntPtr.Zero; 
    1.53 -          break;
    1.54 -        case WM_WINDOWPOSCHANGING:         
    1.55 -          WindowPos wp = (WindowPos)Marshal.PtrToStructure(
    1.56 -            message.LParam, typeof(WindowPos));
    1.57 +        case WM_COMMAND: {
    1.58 +            // need to dispatch the message for the context menu
    1.59 +            if (message.LParam == IntPtr.Zero)
    1.60 +              commandDispatch.Invoke(null, new object[] { 
    1.61 +              message.WParam.ToInt32() & 0xFFFF });
    1.62 +          } break;
    1.63 +        case WM_NCHITTEST: {
    1.64 +            message.Result = (IntPtr)HitResult.Caption;
    1.65 +            if (HitTest != null) {
    1.66 +              Point p = new Point(
    1.67 +                (int)((uint)message.LParam & 0xFFFF) - location.X,
    1.68 +                (int)(((uint)message.LParam >> 16) & 0xFFFF) - location.Y);
    1.69 +              HitTestEventArgs e = new HitTestEventArgs(p, HitResult.Caption);
    1.70 +              HitTest(this, e);
    1.71 +              message.Result = (IntPtr)e.HitResult;
    1.72 +            }
    1.73 +          } break;
    1.74 +        case WM_NCLBUTTONDBLCLK: {
    1.75 +            message.Result = IntPtr.Zero;
    1.76 +          } break;
    1.77 +        case WM_NCRBUTTONDOWN: {
    1.78 +            message.Result = IntPtr.Zero;
    1.79 +          } break;
    1.80 +        case WM_NCRBUTTONUP: {
    1.81 +            if (contextMenu != null)
    1.82 +              ShowContextMenu(new Point(
    1.83 +                (int)((uint)message.LParam & 0xFFFF),
    1.84 +                (int)(((uint)message.LParam >> 16) & 0xFFFF)));
    1.85 +            message.Result = IntPtr.Zero;
    1.86 +          } break;
    1.87 +        case WM_WINDOWPOSCHANGING: {
    1.88 +            WindowPos wp = (WindowPos)Marshal.PtrToStructure(
    1.89 +              message.LParam, typeof(WindowPos));
    1.90 +            
    1.91 +            if (!lockPositionAndSize) {
    1.92 +              // prevent the window from leaving the screen
    1.93 +              if ((wp.flags & SWP_NOMOVE) == 0) {
    1.94 +                Rectangle rect = Screen.GetWorkingArea(new Point(wp.x, wp.y));
    1.95 +                const int margin = 16;
    1.96 +                wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
    1.97 +                wp.x = Math.Min(wp.x, rect.Right - margin);
    1.98 +                wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
    1.99 +                wp.y = Math.Min(wp.y, rect.Bottom - margin);
   1.100 +              }
   1.101  
   1.102 -          // add the nomove flag if position is locked
   1.103 -          if (lockPosition)
   1.104 -            wp.flags |= SWP_NOMOVE;
   1.105 +              // update location and fire event
   1.106 +              if ((wp.flags & SWP_NOMOVE) == 0) {
   1.107 +                if (location.X != wp.x || location.Y != wp.y) {
   1.108 +                  location = new Point(wp.x, wp.y);
   1.109 +                  if (LocationChanged != null)
   1.110 +                    LocationChanged(this, EventArgs.Empty);
   1.111 +                }
   1.112 +              }
   1.113  
   1.114 -          // prevent the window from leaving the screen
   1.115 -          if ((wp.flags & SWP_NOMOVE) == 0) {            
   1.116 -            Rectangle rect = Screen.GetWorkingArea(new Point(wp.x, wp.y));
   1.117 -            const int margin = 20;
   1.118 -            wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
   1.119 -            wp.x = Math.Min(wp.x, rect.Right - margin);
   1.120 -            wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
   1.121 -            wp.y = Math.Min(wp.y, rect.Bottom - margin);
   1.122 +              // update size and fire event
   1.123 +              if ((wp.flags & SWP_NOSIZE) == 0) {
   1.124 +                if (size.Width != wp.cx || size.Height != wp.cy) {
   1.125 +                  size = new Size(wp.cx, wp.cy);
   1.126 +                  if (SizeChanged != null)
   1.127 +                    SizeChanged(this, EventArgs.Empty);
   1.128 +                }
   1.129 +              } 
   1.130  
   1.131 -            // raise the event if location changed
   1.132 -            if (location.X != wp.x || location.Y != wp.y) {
   1.133 -              location = new Point(wp.x, wp.y);
   1.134 -              if (LocationChanged != null)
   1.135 -                LocationChanged(this, EventArgs.Empty);
   1.136 +              // update the size of the layered window
   1.137 +              if ((wp.flags & SWP_NOSIZE) == 0) {
   1.138 +                NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero,
   1.139 +                  IntPtr.Zero, ref size, IntPtr.Zero, IntPtr.Zero, 0,
   1.140 +                  IntPtr.Zero, 0);                
   1.141 +              }
   1.142 +
   1.143 +              // update the position of the layered window
   1.144 +              if ((wp.flags & SWP_NOMOVE) == 0) {
   1.145 +                NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 
   1.146 +                  location.X, location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | 
   1.147 +                  SWP_NOZORDER | SWP_NOSENDCHANGING);
   1.148 +              }
   1.149              }
   1.150 -          }          
   1.151 -
   1.152 -          Marshal.StructureToPtr(wp, message.LParam, false);
   1.153 -          message.Result = IntPtr.Zero;
   1.154 -          break;           
   1.155 -        default:
   1.156 -          base.WndProc(ref message); break;
   1.157 +            
   1.158 +            // do not forward any move or size messages
   1.159 +            wp.flags |= SWP_NOSIZE | SWP_NOMOVE;            
   1.160 +            Marshal.StructureToPtr(wp, message.LParam, false);                      
   1.161 +            message.Result = IntPtr.Zero;
   1.162 +          } break;
   1.163 +        default: {
   1.164 +            base.WndProc(ref message);
   1.165 +          } break;
   1.166        }      
   1.167      }
   1.168  
   1.169 @@ -185,20 +221,24 @@
   1.170          newHBitmap = bitmap.GetHbitmap(Color.Black);
   1.171          oldHBitmap = NativeMethods.SelectObject(memory, newHBitmap);
   1.172  
   1.173 -        Size size = bitmap.Size;
   1.174          Point pointSource = Point.Empty;
   1.175 -        Point topPos = Location;
   1.176 +        BlendFunction blend = CreateBlendFunction();
   1.177  
   1.178 -        BlendFunction blend = CreateBlendFunction();
   1.179 -        NativeMethods.UpdateLayeredWindow(Handle, screen, ref topPos,
   1.180 +        NativeMethods.UpdateLayeredWindow(Handle, screen, IntPtr.Zero,
   1.181            ref size, memory, ref pointSource, 0, ref blend, ULW_ALPHA);
   1.182 -      } finally {
   1.183 -        NativeMethods.ReleaseDC(IntPtr.Zero, screen);
   1.184 +
   1.185 +        // make sure the window is at the right location
   1.186 +        NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 
   1.187 +          location.X, location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | 
   1.188 +          SWP_NOZORDER | SWP_NOSENDCHANGING);
   1.189 +
   1.190 +      } finally {        
   1.191          if (newHBitmap != IntPtr.Zero) {
   1.192            NativeMethods.SelectObject(memory, oldHBitmap);
   1.193            NativeMethods.DeleteObject(newHBitmap);
   1.194          }
   1.195          NativeMethods.DeleteDC(memory);
   1.196 +        NativeMethods.ReleaseDC(IntPtr.Zero, screen);
   1.197        }
   1.198      }
   1.199  
   1.200 @@ -237,13 +277,13 @@
   1.201        }
   1.202      }
   1.203  
   1.204 -    // if locked, the window can not be moved
   1.205 -    public bool LockPosition {
   1.206 +    // if locked, the window can not be moved or resized
   1.207 +    public bool LockPositionAndSize {
   1.208        get {
   1.209 -        return lockPosition;
   1.210 +        return lockPositionAndSize;
   1.211        }
   1.212        set {
   1.213 -        lockPosition = value;
   1.214 +        lockPositionAndSize = value;
   1.215        }
   1.216      }
   1.217  
   1.218 @@ -274,23 +314,29 @@
   1.219        set {
   1.220          if (size != value) {
   1.221            size = value;
   1.222 -          NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, size.Width,
   1.223 -            size.Height, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | 
   1.224 -            SWP_NOSENDCHANGING);
   1.225 +          NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
   1.226 +            ref size, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, 0);                    
   1.227 +          if (SizeChanged != null)
   1.228 +            SizeChanged(this, EventArgs.Empty);
   1.229          }
   1.230        }
   1.231      }
   1.232  
   1.233 +    public event EventHandler SizeChanged;
   1.234 +
   1.235      public Point Location {
   1.236        get {
   1.237          return location;
   1.238        }
   1.239        set {
   1.240 -        NativeMethods.SetWindowPos(Handle, IntPtr.Zero, value.X, value.Y, 0,
   1.241 -          0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
   1.242 -        location = value;
   1.243 -        if (LocationChanged != null)
   1.244 -          LocationChanged(this, EventArgs.Empty);
   1.245 +        if (location != value) {
   1.246 +          location = value;
   1.247 +          NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 
   1.248 +            location.X, location.Y, 0, 0, 
   1.249 +            SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);          
   1.250 +          if (LocationChanged != null)
   1.251 +            LocationChanged(this, EventArgs.Empty);
   1.252 +        }
   1.253        }
   1.254      }
   1.255  
   1.256 @@ -305,6 +351,8 @@
   1.257        }
   1.258      }
   1.259  
   1.260 +    public event HitTestEventHandler HitTest;
   1.261 +
   1.262      [StructLayout(LayoutKind.Sequential, Pack = 1)]
   1.263      private struct BlendFunction {
   1.264        public byte BlendOp;
   1.265 @@ -357,8 +405,6 @@
   1.266      public const int TPM_RIGHTBUTTON = 0x0002;
   1.267      public const int TPM_VERTICAL = 0x0040;
   1.268  
   1.269 -    public readonly IntPtr HTCAPTION = (IntPtr)2;
   1.270 -
   1.271      private enum WindowAttribute : int {
   1.272        DWMWA_NCRENDERING_ENABLED = 1,
   1.273        DWMWA_NCRENDERING_POLICY,
   1.274 @@ -382,8 +428,14 @@
   1.275  
   1.276        [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
   1.277        [return: MarshalAs(UnmanagedType.Bool)]
   1.278 +      public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
   1.279 +        IntPtr pptDst, ref Size psize, IntPtr hdcSrc, IntPtr pprSrc,
   1.280 +        int crKey, IntPtr pblend, int dwFlags);
   1.281 +
   1.282 +      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
   1.283 +      [return: MarshalAs(UnmanagedType.Bool)]
   1.284        public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, 
   1.285 -        ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, 
   1.286 +        IntPtr pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, 
   1.287          int crKey, ref BlendFunction pblend, int dwFlags);
   1.288  
   1.289        [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
   1.290 @@ -425,4 +477,31 @@
   1.291          WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);
   1.292      }    
   1.293    }
   1.294 +
   1.295 +  public enum HitResult {
   1.296 +    Transparent = -1,
   1.297 +    Nowhere = 0,
   1.298 +    Client = 1,
   1.299 +    Caption = 2,
   1.300 +    Left = 10,
   1.301 +    Right = 11,
   1.302 +    Top = 12,
   1.303 +    TopLeft = 13,
   1.304 +    TopRight = 14,
   1.305 +    Bottom = 15,
   1.306 +    BottomLeft = 16,
   1.307 +    BottomRight = 17,
   1.308 +    Border = 18
   1.309 +  }
   1.310 +
   1.311 +  public delegate void HitTestEventHandler(object sender, HitTestEventArgs e);
   1.312 +
   1.313 +  public class HitTestEventArgs : EventArgs {
   1.314 +    public HitTestEventArgs(Point location, HitResult hitResult) {
   1.315 +      Location = location;
   1.316 +      HitResult = hitResult;
   1.317 +    }
   1.318 +    public Point Location { get; private set; }
   1.319 +    public HitResult HitResult { get; set; }
   1.320 +  }
   1.321  }