home *** CD-ROM | disk | FTP | other *** search
/ Delphi Developer's Kit 1996 / Delphi Developer's Kit 1996.iso / power / form3d / form3d.exe / FORM3D.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1995-05-16  |  16.6 KB  |  537 lines

  1. unit Form3d;
  2.  
  3. {
  4.   TForm3D Class
  5.   Copyright ⌐ 1995  Alan Ciemian  All Rights Reserved
  6.  
  7.   The TForm3D class is a descendant of TForm that provides
  8.     3D borders for non-dialog forms. Also allows form sizing to be
  9.     enabled/disabled.
  10.  
  11.   NOTES:
  12.       - Requires that form have bsSizeable border style.
  13.       - Sizing can be enabled/disabled with AllowResize property.
  14.       - Handles all Title bar icon combinations.
  15.       - Handles forms with or without menus(including multiline).
  16.       - Handles all combinations of scroll bars.
  17.       - NOT Designed/Tested for use as MDI Frame or MDI Child
  18.  
  19.   05/01/95 - Initial Release
  20.  
  21.   05/16/95 - Added FEnable3D field to store whether 3D drawing should be used
  22.                or not depending on running Windows version.
  23.            - Added check for iconic state before performing 3D drawing.
  24.            - Modified caption drawing to left align caption if it is too wide
  25.                for the available area, ala windows.
  26. }
  27.  
  28.  
  29. interface
  30.  
  31. uses
  32.   Messages, WinTypes,
  33.   Classes, Controls, Forms;
  34.  
  35.  
  36. const
  37.   CaptionH_STD = 20;
  38.   MenuH_STD    = 18;
  39.  
  40.  
  41. type
  42.   TForm3D_NCPaintMode =
  43.     (
  44.     NCPaint_All,
  45.     NCPaint_Activate,
  46.     NCPaint_Deactivate
  47.     );
  48.  
  49. type
  50.   TForm3D = class(TForm)
  51.   private
  52.     FEnable3D     : Boolean;  { Flag to identify if can use 3D effects }
  53.     FSysMenuW     : Integer;  { Width of system menu,     0 if no sysmenu }
  54.     FMinMaxW      : Integer;  { Width of min/max buttons, 0 if no min/max btns }
  55.     FAllowResize  : Boolean;
  56.     { Private procedures }
  57.     procedure NCPaint3D(const Mode: TForm3D_NCPaintMode);
  58.     procedure ComputeNonClientDimensions;
  59.     function ScrollBarVisible
  60.       (
  61.       const Code    : Word;  { SB_VERT or SB_HORZ }
  62.       const WndRect : TRect
  63.       ): Boolean;
  64.     { Message Handlers }
  65.     procedure WMNCHitTest (var Msg: TWMNCHitTest);  message WM_NCHitTest;
  66.     procedure WMNCPaint   (var Msg: TWMNCPaint);    message WM_NCPaint;
  67.     procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCActivate;
  68.   protected
  69.     procedure CreateWnd; override;
  70.   public
  71.     constructor Create(AOwner: TComponent); override;
  72.     { Properties }
  73.     property AllowResize: Boolean
  74.              read FAllowResize
  75.              write FAllowResize
  76.              default False;
  77.   end;
  78.  
  79.  
  80. implementation
  81.  
  82.  
  83. uses
  84.   WinProcs,
  85.   SysUtils, Graphics,
  86.   SysMet;
  87.  
  88.  
  89. function TForm3D.ScrollBarVisible
  90.   (
  91.   const Code    : Word;  { SB_VERT or SB_HORZ }
  92.   const WndRect : TRect
  93.   ): Boolean;
  94. var
  95.   PtInScroll : TPoint;
  96.   HVis       : Boolean;
  97. begin
  98.   Result := False;
  99.  
  100.   with  WndRect, SysMetrics  do
  101.     begin
  102.     { Determine if Horz scroll bar is visible. Need this for both horz and }
  103.     {   vert scroll bars. }
  104.     { Two checks need to be satisfied, Style identifies scroll bar and }
  105.     {   windows recognizes HitTest in scroll bar. }
  106.     { Hit Test check is required because there are cases when the window }
  107.     {   gets very small that windows decides not to draw the scroll bars }
  108.     {   even though they exist. }
  109.     PtInScroll := Point(Left + Frame.cx + 1, Bottom - Frame.cy - 1);
  110.     HVis := ( ((GetWindowLong(Handle, GWL_STYLE) and WS_HSCROLL) <> 0) and
  111.               (Perform(WM_NCHITTEST, 0, LongInt(PtInScroll)) = HTHSCROLL) );
  112.  
  113.     if ( Code = SB_HORZ ) then
  114.       begin  { Done, return result computed above }
  115.       Result := HVis;
  116.       end
  117.     else
  118.       begin  { Perform same procedure as above for vertical }
  119.       PtInScroll := Point(Right - Frame.cx - 1, Bottom - Frame.cy - 1);
  120.       if ( HVis ) then Dec(PtInScroll.y, HScrollBtn.cy);
  121.       Result := ( ((GetWindowLong(Handle, GWL_STYLE) and WS_VSCROLL) <> 0) and
  122.                   (Perform(WM_NCHITTEST, 0, LongInt(PtInScroll)) = HTVSCROLL) );
  123.       end;
  124.     end;
  125. end;
  126.  
  127.  
  128.  
  129. constructor TForm3D.Create
  130.   (
  131.   AOwner: TComponent
  132.   );
  133. begin
  134.   inherited Create(AOwner);
  135.  
  136.   { Set property defaults }
  137.   FAllowResize  := False;
  138. end;
  139.  
  140.  
  141. {
  142.   CreateWnd is overriden so we can force certain properties before
  143.     the window is created, and compute some parameters needed to
  144.     do the 3D non-client drawing.
  145. }
  146. procedure TForm3D.CreateWnd;
  147. var
  148.   AdjustHeight : Integer;
  149.   Version      : TWindowsVersion;
  150.   VerMajor     : Word;
  151.   VerMinor     : Word;
  152. begin
  153.   { Border Style must be bsSizeable }
  154.   BorderStyle := bsSizeable;
  155.  
  156.   { Compute height adjustments for font caption and menu.      }
  157.   { In large fonts video modes the client area would otherwise }
  158.   {   be reduced. }
  159.   AdjustHeight := 0;
  160.   with  SysMetrics  do
  161.     begin
  162.     Inc(AdjustHeight, CaptionH - CaptionH_STD);
  163.     { Note: Only adjusts for a single line menu bar }
  164.     if ( Menu <> nil ) then Inc(AdjustHeight, MenuH - MenuH_STD);
  165.     end;
  166.  
  167.   { Let Form create }
  168.   inherited CreateWnd;
  169.  
  170.   { Enforce the height adjustment }
  171.   Height := Height + AdjustHeight;
  172.  
  173.   { 3D Drawing requires Win 3.x default behavior. }
  174.   GetWindowsVersion(Version, VerMajor, VerMinor);
  175.   FEnable3D := ( (VerMajor = 3) and (VerMinor = 10) );
  176.  
  177.   { Precompute dimensions of key non-client areas for later use }
  178.   {   in drawing the 3D effects. }
  179.   if ( FEnable3D ) then ComputeNonClientDimensions;
  180. end;
  181.  
  182.  
  183. {
  184. ComputeNonClientDimensions precomputes some dimensions of non-client items
  185.   to avoid doing it repeatedly during painting.
  186. }
  187. procedure TForm3D.ComputeNonClientDimensions;
  188.  
  189.   { We'd like to use the SM_CXSIZE system metrics value for the size of icons }
  190.   {  in the title bar but it is NOT correct for some video drivers/modes }
  191.   function BitmapWidth(const BM_ID: Integer): Integer;
  192.   var
  193.     BM     : THandle;
  194.     BMInfo : WinTypes.TBitmap;
  195.   begin
  196.     BM := LoadBitmap(0, MakeIntResource(BM_ID));
  197.     try
  198.       GetObject(BM, SizeOf(BMInfo), @BMInfo);
  199.       Result := BMInfo.bmWidth;
  200.     finally
  201.       DeleteObject(BM);
  202.     end;
  203.   end;
  204.  
  205. begin
  206.   FSysMenuW := 0;
  207.   if ( biSystemMenu in BorderIcons ) then
  208.     begin
  209.     { Note: Close bitmap contains 2 bitmaps, app close and MDI child close }
  210.     Inc(FSysMenuW, BitmapWidth(OBM_CLOSE) div 2);
  211.     end;
  212.  
  213.   FMinMaxW := 0;
  214.   if ( biMinimize in BorderIcons ) then
  215.     begin
  216.     Inc(FMinMaxW, BitmapWidth(OBM_REDUCE));
  217.     end;
  218.   if ( biMaximize in BorderIcons ) then
  219.     begin
  220.     Inc(FMinMaxW, BitmapWidth(OBM_ZOOM));
  221.     end;
  222. end;
  223.  
  224.  
  225. {
  226. NCPaint3D handles the 3D specific painting for the form.
  227. }
  228. procedure TForm3D.NCPaint3D
  229.   (
  230.   const Mode: TForm3D_NCPaintMode
  231.   );
  232. var
  233.   WndRect     : TRect;
  234.   ClientRect  : TRect;
  235.   ClientH     : Integer;
  236.   ScrollH     : Integer;
  237.   DC          : HDC;
  238.   NCCanvas    : TCanvas;
  239.   Extra       : Integer;
  240.   CaptionRect : TRect;
  241.   CaptionPt   : TPoint;
  242.   TM          : TTextMetric;
  243.   CaptionBuf  : array[0..255] of Char;
  244. begin
  245.   { Get window rect }
  246.   WinProcs.GetWindowRect(Handle, WndRect);
  247.   { Need to know if horz scroll bar present }
  248.   ScrollH := 0;
  249.   if ( ScrollBarVisible(SB_HORZ, WndRect) ) then
  250.     begin
  251.     ScrollH := SysMetrics.HScrollBtn.cy - 1;
  252.     end;
  253.   { Convert window rect to (0, 0) origin }
  254.   with  WndRect  do
  255.     begin
  256.     Right  := Right - Left;
  257.     Left   := 0;
  258.     Bottom := Bottom - Top;
  259.     Top    := 0;
  260.     end;
  261.   WinProcs.GetClientRect(Handle, ClientRect);
  262.   ClientH := ClientRect.Bottom - ClientRect.Top;
  263.   if ( 0 < ClientH ) then Inc(ClientH);
  264.  
  265.   { Get a Window DC and wrap it in a Delphi Canvas }
  266.   DC       := GetWindowDC(Self.Handle);
  267.   NCCanvas := TCanvas.Create;
  268.   NCCanvas.Handle := DC;
  269.   try
  270.     with NCCanvas, WndRect, SysMetrics do
  271.       begin
  272.       if ( Mode = NCPaint_All ) then
  273.         begin
  274.         { Draw Left and Top edges of window frame, outer }
  275.         Pen.Color := clBtnShadow;
  276.         PolyLine([ Point(Left,  Bottom - 1),
  277.                    Point(Left,  Top),
  278.                    Point(Right, Top) ]);
  279.         { Draw Bottom and Right edges of window frame, outer }
  280.         Pen.Color := clWindowFrame;
  281.         PolyLine([ Point(Left,  Bottom - 1),
  282.                    Point(Right - 1, Bottom - 1),
  283.                    Point(Right - 1, Top - 1) ]);
  284.         { Draw Left and Top edges of window frame, 1-pixel in }
  285.         Pen.Color := clBtnHighlight;
  286.         PolyLine([ Point(Left  + 1, Bottom - 2),
  287.                    Point(Left  + 1, Top    + 1),
  288.                    Point(Right - 1, Top    + 1) ]);
  289.         { Draw Right and Bottom edges of window frame, 1-pixel in }
  290.         Pen.Color := clBtnShadow;
  291.         PolyLine([ Point(Left  + 1, Bottom - 2),
  292.                    Point(Right - 2, Bottom - 2),
  293.                    Point(Right - 2, Top) ]);
  294.  
  295.         { Fill Remainder of Sizing border }
  296.         Pen.Color := clBtnFace;
  297.         for Extra := 2 to (Frame.cx - 1) do
  298.           begin
  299.           Brush.Color := clBtnFace;
  300.           FrameRect(Rect(Left + Extra, Top + Extra,
  301.                          Right - Extra, Bottom - Extra));
  302.           end;
  303.  
  304.         { Draw Left and Top Edge of Caption Area }
  305.         Pen.Color := clBtnShadow;
  306.         PolyLine([ Point(Frame.cx - 1, Bottom - 1 - Frame.cy - ClientH - ScrollH),
  307.                    Point(Frame.cx - 1, Frame.cy - 1),
  308.                    Point(Right - Frame.cx, Frame.cy - 1) ]);
  309.         { Draw Bottom and Right Edge of Caption Area }
  310.         Pen.Color := clBtnHighlight;
  311.         PolyLine([ Point(Frame.cx - 1,     Bottom - Frame.cy - ClientH - ScrollH),
  312.                    Point(Right - Frame.cx, Bottom - Frame.cy - ClientH - ScrollH),
  313.                    Point(Right - Frame.cx, Frame.cy - 1) ]);
  314.         end;
  315.  
  316.       { Draw Caption }
  317.       CaptionRect := Rect(Frame.cx + FSysMenuW + 1, Frame.cy,
  318.                           Right - Frame.cx - FMinMaxW,
  319.                           Frame.cy - 1 + CaptionH - 1);
  320.       if ( (Mode = NCPaint_Activate) or
  321.            ((Mode = NCPaint_All) and (GetActiveWindow = Self.Handle)) ) then
  322.         begin  { Need 'Active' Caption }
  323.         Brush.Color := clActiveCaption;
  324.         Font.Color  := clCaptionText;
  325.         end
  326.       else
  327.         begin  { Need 'InActive' Caption }
  328.         Brush.Color := clInactiveCaption;
  329.         Font.Color  := clInactiveCaptionText;
  330.         end;
  331.       FillRect(CaptionRect);
  332.       with  CaptionRect  do
  333.         begin
  334.         { Assume center aligned }
  335.         SetTextAlign(DC, TA_CENTER or TA_TOP);
  336.         GetTextMetrics(DC, TM);
  337.         CaptionPt := Point((Left + Right) div 2,
  338.                            Top + ((CaptionH - 1) - TM.tmHeight) div 2);
  339.         if ( (Right - Left) < TextWidth(Caption) ) then
  340.           begin { Switch caption to left align to mimic windows }
  341.           SetTextAlign(DC, TA_LEFT or TA_TOP);
  342.           CaptionPt.X := Left + 1;
  343.           end;
  344.         TextRect(CaptionRect, CaptionPt.X, CaptionPt.Y, Caption);
  345.         end;
  346.       end;
  347.   finally
  348.     NCCanvas.Free;
  349.     ReleaseDC(Handle, DC);
  350.   end; { try-finally }
  351. end;
  352.  
  353.  
  354. {
  355. WMNCHitTest handles the WM_NCHITTEST message.
  356. Modifies sizing hit codes to support fixed size windows.
  357. }
  358. procedure TForm3D.WMNCHitTest
  359.   (
  360.   var Msg: TWMNCHitTest
  361.   );
  362. var
  363.   HitCode : LongInt;
  364. begin
  365.   inherited;
  366.   HitCode := Msg.Result;
  367.  
  368.   { Lets resurrect the size corner }
  369.   if ( HitCode = HTSIZE ) then HitCode := HTBOTTOMRIGHT;
  370.  
  371.   if ( not AllowResize ) then
  372.     begin
  373.     if ( (HitCode = HTLEFT)     or (HitCode = HTRIGHT)      or
  374.          (HitCode = HTTOP)      or (HitCode = HTBOTTOM)     or
  375.          (HitCode = HTTOPLEFT)  or (HitCode = HTBOTTOMLEFT) or
  376.          (HitCode = HTTOPRIGHT) or (HitCode = HTBOTTOMRIGHT) ) then
  377.       begin
  378.       HitCode := HTNOWHERE;
  379.       end;
  380.     end;
  381.  
  382.   Msg.Result := HitCode;
  383. end;
  384.  
  385.  
  386. {
  387. WMNCPaint handles WM_NCPAINT message.
  388. Calls default handler to paint non-client areas that have standard appearance.
  389. Calls NCPaint3D to paint modified non-client areas
  390. NOTE: Uses undocumented aspect of WM_NCPAINT message which allows a clipping
  391.       region handle to be passed in the wParam of the message.
  392.       This is used to avoid seeing the standard non-client areas flash before
  393.       they are repainted by the 3D code.
  394.       Ref. Undocumented Windows pg. 527, Thanks Andrew.
  395. }
  396. procedure TForm3D.WMNCPaint
  397.   (
  398.   var Msg: TWMNCPaint
  399.   );
  400. var
  401.   WndRect    : TRect;
  402.   ClientRect : TRect;
  403.   ClientH    : Integer;
  404.   ScrollH    : Integer;
  405.   ClipRect   : TRect;
  406.   ClipRgn    : THandle;
  407.   HScrollVis : Boolean;
  408.   VScrollVis : Boolean;
  409. begin
  410.   if ( FEnable3D and (not IsIconic(Handle)) ) then
  411.     begin
  412.     { Let Windows draw the non-client areas that will not change }
  413.     { Form props for window pos and size incorrect during resize here. }
  414.     { Get Position directly from windows }
  415.     WinProcs.GetWindowRect(Handle, WndRect);
  416.     WinProcs.GetClientRect(Handle, ClientRect);
  417.     ClientH := ClientRect.Bottom - ClientRect.Top;
  418.     if ( 0 < ClientH ) then Inc(ClientH);
  419.  
  420.     HScrollVis := ScrollBarVisible(SB_HORZ, WndRect);
  421.     VScrollVis := ScrollBarVisible(SB_VERT, WndRect);
  422.  
  423.     ScrollH := 0;
  424.     if ( HScrollVis ) then ScrollH := SysMetrics.HScrollBtn.cy - 1;
  425.  
  426.     with  WndRect, SysMetrics  do
  427.       begin
  428.       { System Menu }
  429.       if ( biSystemMenu in BorderIcons ) then
  430.         begin
  431.         ClipRect := Rect(Left + Frame.cx, Top + Frame.cy,
  432.                          Left + Frame.cx + TitleBitmap.cx + 1,
  433.                          Top  + Frame.cy + TitleBitmap.cy);
  434.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  435.         TMessage(Msg).wParam := ClipRgn;
  436.         (self as TWinControl).DefaultHandler(Msg);
  437.         DeleteObject(ClipRgn);
  438.         end;
  439.       { Min/Max buttons }
  440.       if ( 0 < FMinMaxW ) then
  441.         begin
  442.         ClipRect := Rect(Right - Frame.cx - FMinMaxW, Top  + Frame.cy,
  443.                          Right - Frame.cx, Top + Frame.cy + TitleBitmap.cy);
  444.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  445.         TMessage(Msg).wParam := ClipRgn;
  446.         (self as TWinControl).DefaultHandler(Msg);
  447.         DeleteObject(ClipRgn);
  448.         end;
  449.       { Menubar }
  450.       if ( Menu <> nil ) then
  451.         begin
  452.         ClipRect := Rect(Left + Frame.cx, Top + Frame.cy + CaptionH - Border.cy - 1,
  453.                          Right - Frame.cx, Bottom - Frame.cy - ClientH - ScrollH);
  454.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  455.         TMessage(Msg).wParam := ClipRgn;
  456.         (self as TWinControl).DefaultHandler(Msg);
  457.         DeleteObject(ClipRgn);
  458.         end;
  459.       end;
  460.  
  461.     { Paint 3-D parts of nonclient area in 3-D style }
  462.     NCPaint3D(NCPaint_All);
  463.  
  464.     { Now let windows paint scroll bars. Need to wait until here because scroll }
  465.     {   bars take advantage of normal borders for their outer edges and they    }
  466.     {   our trounced in NCPaint3D. }
  467.     with  WndRect, SysMetrics  do
  468.       begin
  469.       if ( HScrollVis ) then
  470.         begin { Let Windows draw horz scroll bar }
  471.         ClipRect := Rect(Left  + (Frame.cx - 1), Bottom - (Frame.cy - 1) - HScrollBtn.cy,
  472.                          Right - (Frame.cx - 1), Bottom - (Frame.cy - 1));
  473.         if ( VScrollVis ) then Dec(ClipRect.Right, VScrollBtn.cx - 1);
  474.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  475.         TMessage(Msg).wParam := ClipRgn;
  476.         (self as TWinControl).DefaultHandler(Msg);
  477.         DeleteObject(ClipRgn);
  478.         end;
  479.       if ( VScrollVis ) then
  480.         begin { Let Windows draw vert scroll bar }
  481.         ClipRect := Rect(Right - (Frame.cx - 1) - VScrollBtn.cx, Bottom - Frame.cy - ClientH - ScrollH,
  482.                          Right - (Frame.cx - 1),                 Bottom - (Frame.cy - 1));
  483.         if ( HScrollVis ) then Dec(ClipRect.Bottom, HScrollBtn.cy - 1);
  484.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  485.         TMessage(Msg).wParam := ClipRgn;
  486.         (self as TWinControl).DefaultHandler(Msg);
  487.         DeleteObject(ClipRgn);
  488.         end;
  489.       if ( HScrollVis and VScrollVis ) then
  490.         begin { Let Windows draw little box in corner }
  491.         ClipRect := Rect(Right - (Frame.cx - 1) - (VScrollBtn.cx - 1),
  492.                          Bottom - (Frame.cy - 1) - (HScrollBtn.cy - 1),
  493.                          Right - (Frame.cx - 1) - 1, Bottom - (Frame.cy - 1) - 1);
  494.         ClipRgn := CreateRectRgnIndirect(ClipRect);
  495.         TMessage(Msg).wParam := ClipRgn;
  496.         (self as TWinControl).DefaultHandler(Msg);
  497.         DeleteObject(ClipRgn);
  498.         end;
  499.       end;
  500.     Msg.Result := 0;
  501.     end
  502.   else
  503.     begin
  504.     { Use whatever behavior is standard for this system }
  505.     DefaultHandler(Msg);
  506.     end;
  507. end;
  508.  
  509.  
  510. {
  511. WMNCActivate handles the WM_NCACTIVATE message.
  512. Calls NCPaint3D to repaint the caption.
  513. Can NOT let windows have this message or it will trash our 3D borders.
  514. }
  515. procedure TForm3D.WMNCActivate
  516.   (
  517.   var Msg: TWMNCActivate
  518.   );
  519. begin
  520.   if ( FEnable3D and (not IsIconic(Handle)) ) then
  521.     begin
  522.     if ( Msg.Active ) then
  523.       NCPaint3D(NCPaint_Activate)
  524.     else
  525.       NCPaint3D(NCPaint_Deactivate);
  526.  
  527.     Msg.Result := 1;
  528.     end
  529.   else
  530.     begin
  531.     DefaultHandler(Msg);
  532.     end;
  533. end;
  534.  
  535.  
  536. end.
  537.