Recent

Author Topic: [SOLVED] How to query the outer size of a 'TForm' ?  (Read 1860 times)

Bart

  • Hero Member
  • *****
  • Posts: 5465
    • Bart en Mariska's Webstek
Re: How to query the outer size of a 'TForm' ?
« Reply #15 on: October 12, 2024, 11:02:30 pm »
Ok, weird, I never knew that  :-[

Yes, it seems Height is the same as what ClientHeight is in Delphi.

It's even in the FAQ.

Bart

rvk

  • Hero Member
  • *****
  • Posts: 6572
Re: How to query the outer size of a 'TForm' ?
« Reply #16 on: October 13, 2024, 12:10:43 am »
It's even in the FAQ.
::)

BTW. I expected this piece of crucial information to be in the normal docs, where there is no mention of it. Especially because it's different behavior from Delphi.
« Last Edit: October 13, 2024, 12:20:02 am by rvk »

jamie

  • Hero Member
  • *****
  • Posts: 6733
Re: How to query the outer size of a 'TForm' ?
« Reply #17 on: October 13, 2024, 01:20:47 am »
Yes, it sure is different behavior, I am glad I am not the only one that notices.

The only true wisdom is knowing you know nothing

dsiders

  • Hero Member
  • *****
  • Posts: 1280
Re: How to query the outer size of a 'TForm' ?
« Reply #18 on: October 13, 2024, 02:49:27 am »
It's even in the FAQ.
::)

BTW. I expected this piece of crucial information to be in the normal docs, where there is no mention of it. Especially because it's different behavior from Delphi.

Fair enough.

I have topics updates that's are pending for 4.0. Let me review what it would take to apply them for 3.8.
Preview the next Lazarus documentation release at: https://dsiders.gitlab.io/lazdocsnext

rvk

  • Hero Member
  • *****
  • Posts: 6572
Re: How to query the outer size of a 'TForm' ?
« Reply #19 on: October 13, 2024, 12:30:03 pm »
Win10:
Code: [Select]
SM_CYCAPTION 23 (but correct would be 31)
SM_CXBORDER  1  (correct)
SM_CYHSCROLL 17 (dont' know if correct)
SM_CXHSCROLL 17 (dont' know if correct)
Why would it SM_CYCAPTION need to be 31 on Windows 10?
23 + 8 + 8 (8 for 1+7 shadowborder) = 39. 39 would be the correct answer for the height difference of Height and ClientHeight on (my) Windows 10.

You can test this simply on Lazarus:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   SetWindowPos(Self.Handle, 0, Left, Top, Width, 300, SWP_NOZORDER + SWP_NOACTIVATE);
  4.   ShowMessage(Format('Height diff = %d  ClientHeight diff = %d', [Height - 300, ClientHeight - 300]));
  5. end;

On Lazarus the result
Height diff = -39  ClientHeight diff = -39

On Delphi the result
Height diff = 0  ClientHeight diff = -39

So SM_CYCAPTION  is correct on Windows 10 but it's not the only element of the difference between Height and ClientHeight.

Same goes for SM_CXBORDER on Windows 7. You see it should be 4, not 1. But SM_CXBORDER is for a border without 3D look. There is another SM_CXEDGE which would retrieve the value for the border in 3D look. Maybe that's the one you were looking for.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics

But I understand that it might not be easy to retrieve these values on all platforms reliably.

Hartmut

  • Hero Member
  • *****
  • Posts: 843
Re: How to query the outer size of a 'TForm' ?
« Reply #20 on: October 13, 2024, 01:56:44 pm »
However, you're using the form's top in the calculation of the title height.  I would use GetClientRect's top in the calculation instead.  (Note that the presence of a menu affects the result.)

The documentation of function Windows.GetClientRect() https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect says:
Quote
Retrieves the coordinates of a window's client area. The client coordinates specify the upper-left and lower-right corners of the client area. Because client coordinates are relative to the upper-left corner of a window's client area, the coordinates of the upper-left corner are (0,0).
I tested it and the upper-left corner was (0,0). So I don't understand how to use this function. What did you mean please?

Basically, the first thing I'd do is match the result of GetWindowRect and GetClientRect to their equivalent in the form.  That way you know what the form's top, bottom, right and left actually mean (presuming they match the results of one of the Windows Get... functions.)

I don't know an equivalent in the form for function Windows.GetWindowRect(). And the equivalent in the form for function Windows.GetClientRect() seems to be Form.Width and Form.Height, because the upper-left corner is always (0,0). So I don't understand what you mean... please explain more detailed. Thanks.

I will answer the other replies later...

440bx

  • Hero Member
  • *****
  • Posts: 4727
Re: How to query the outer size of a 'TForm' ?
« Reply #21 on: October 13, 2024, 02:31:53 pm »
I tested it and the upper-left corner was (0,0). So I don't understand how to use this function. What did you mean please?
That's correct.  You need to select a coordinate system, in this case, I'd choose screen coordinates (seems the more convenient one in this case.) This means you'll need to use, ClientToScreen to convert the client coordinates to screen coordinates.  GetWindowRect, returns screen coordinates, therefore once the conversion is done, you have apples and apples instead of apples and oranges.  You can subtract the tops obtained and get meaningful results because they are both relative to the same coordinate system.

I don't know an equivalent in the form for function Windows.GetWindowRect(). And the equivalent in the form for function Windows.GetClientRect() seems to be Form.Width and Form.Height, because the upper-left corner is always (0,0). So I don't understand what you mean... please explain more detailed. Thanks.
I meant the values.  My guess is that Form's top/left/bottom/right match the values returned by GetClientRect.  While that makes sense, I'd verify that.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Hartmut

  • Hero Member
  • *****
  • Posts: 843
Re: How to query the outer size of a 'TForm' ?
« Reply #22 on: October 13, 2024, 03:09:50 pm »
I tested it and the upper-left corner was (0,0). So I don't understand how to use this function. What did you mean please?
That's correct.  You need to select a coordinate system, in this case, I'd choose screen coordinates (seems the more convenient one in this case.) This means you'll need to use, ClientToScreen to convert the client coordinates to screen coordinates.  GetWindowRect, returns screen coordinates, therefore once the conversion is done, you have apples and apples instead of apples and oranges.  You can subtract the tops obtained and get meaningful results because they are both relative to the same coordinate system.

I think now I understood: I will call function Windows.GetClientRect() to return the upper-left corner, which should be always (0,0). With this upper-left corner I call function Windows.ClientToScreen() to convert this Point in screen coordinates and then subtract this Point from the upper-left corner returned by function Windows.GetWindowRect(). Correct?

I don't know an equivalent in the form for function Windows.GetWindowRect(). And the equivalent in the form for function Windows.GetClientRect() seems to be Form.Width and Form.Height, because the upper-left corner is always (0,0). So I don't understand what you mean... please explain more detailed. Thanks.
I meant the values.  My guess is that Form's top/left/bottom/right match the values returned by GetClientRect.  While that makes sense, I'd verify that.

Still not sure if I understood: do you want to compare the Width and Height values returned by function Windows.GetClientRect() directly with Form.Width and Form.Height?

440bx

  • Hero Member
  • *****
  • Posts: 4727
Re: How to query the outer size of a 'TForm' ?
« Reply #23 on: October 13, 2024, 03:30:23 pm »
I think now I understood: I will call function Windows.GetClientRect() to return the upper-left corner, which should be always (0,0). With this upper-left corner I call function Windows.ClientToScreen() to convert this Point in screen coordinates and then subtract this Point from the upper-left corner returned by function Windows.GetWindowRect(). Correct?
Correct.

Still not sure if I understood: do you want to compare the Width and Height values returned by function Windows.GetClientRect() directly with Form.Width and Form.Height?
Yes.  That's correct.  It's just a "prophylactic" step, to ensure assumptions/beliefs are valid.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Hartmut

  • Hero Member
  • *****
  • Posts: 843
Re: How to query the outer size of a 'TForm' ?
« Reply #24 on: October 13, 2024, 05:22:26 pm »
Win10:
Code: [Select]
SM_CYCAPTION 23 (but correct would be 31)
SM_CXBORDER  1  (correct)
SM_CYHSCROLL 17 (dont' know if correct)
SM_CXHSCROLL 17 (dont' know if correct)
Why would it SM_CYCAPTION need to be 31 on Windows 10?
23 + 8 + 8 (8 for 1+7 shadowborder) = 39. 39 would be the correct answer for the height difference of Height and ClientHeight on (my) Windows 10.

From my understanding 'SM_CYCAPTION' means the complete height of the title bar of a form. I measured it's number of pixels and on my Win10 it is 31 (for the complete title bar, including any border, excluding any shadow). This is the same value which my 1st demo in my 1st post returns.

Quote
You can test this simply on Lazarus:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   SetWindowPos(Self.Handle, 0, Left, Top, Width, 300, SWP_NOZORDER + SWP_NOACTIVATE);
  4.   ShowMessage(Format('Height diff = %d  ClientHeight diff = %d', [Height - 300, ClientHeight - 300]));
  5. end;
On Lazarus the result
Height diff = -39  ClientHeight diff = -39

I ran this code on Win10 with Lazarus and got the same result. If I interpret your code correctly, you change with SetWindowPos() the Height of the current Form to 300. In the documentation of SetWindowPos() in https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos I found nothing, if this height is the outer or inner height. From the results I assume, it is the outer height. Is this correct? Thanks for your help.

@440bx:
Thanks for your answer. I will test it and report the result (maybe tomorrow).

rvk

  • Hero Member
  • *****
  • Posts: 6572
Re: How to query the outer size of a 'TForm' ?
« Reply #25 on: October 13, 2024, 06:15:33 pm »
I ran this code on Win10 with Lazarus and got the same result. If I interpret your code correctly, you change with SetWindowPos() the Height of the current Form to 300. In the documentation of SetWindowPos() in https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos I found nothing, if this height is the outer or inner height. From the results I assume, it is the outer height. Is this correct? Thanks for your help.
Yes, SetWindowPos will set the outside. Or at least, it sets it at the same height as Delphi does when changing TForm.Height. I think that Delphi itself even uses the SetWindowsPos for this. And that is 39 pixels different from when setting that 300 for ClientHeight.

Small snippet to get all the values from GetSystemMetrics.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics

Code: Pascal  [Select][+][-]
  1. uses Windows;
  2.  
  3. procedure DoAction(Action: Integer; ActionStr: String);
  4. begin
  5.   Form1.Memo1.Lines.Add(Format('%5d  %s', [GetSystemMetrics(Action), ActionStr]));
  6. end;
  7.  
  8. procedure TForm1.Button1Click(Sender: TObject);
  9. begin
  10.   Memo1.Font.Name := 'Consolas';
  11.   Memo1.Clear;
  12.   DoAction(SM_ARRANGE                    ,'SM_ARRANGE');
  13.   DoAction(SM_CLEANBOOT                  ,'SM_CLEANBOOT');
  14.   DoAction(SM_CMONITORS                  ,'SM_CMONITORS');
  15.   DoAction(SM_CMOUSEBUTTONS              ,'SM_CMOUSEBUTTONS');
  16.   DoAction(SM_CONVERTIBLESLA]"]>BlockedDE       ,'SM_CONVERTIBLESLA]"]>BlockedDE');
  17.   DoAction(SM_CXBORDER                   ,'SM_CXBORDER');
  18.   DoAction(SM_CXCURSOR                   ,'SM_CXCURSOR');
  19.   DoAction(SM_CXDLGFRAME                 ,'SM_CXDLGFRAME');
  20.   DoAction(SM_CXDOUBLECLK                ,'SM_CXDOUBLECLK');
  21.   DoAction(SM_CXDRAG                     ,'SM_CXDRAG');
  22.   DoAction(SM_CXEDGE                     ,'SM_CXEDGE');
  23.   DoAction(SM_CXFIXEDFRAME               ,'SM_CXFIXEDFRAME');
  24.   DoAction(SM_CXFOCUSBORDER              ,'SM_CXFOCUSBORDER');
  25.   DoAction(SM_CXFRAME                    ,'SM_CXFRAME');
  26.   DoAction(SM_CXFULLSCREEN               ,'SM_CXFULLSCREEN');
  27.   DoAction(SM_CXHSCROLL                  ,'SM_CXHSCROLL');
  28.   DoAction(SM_CXHTHUMB                   ,'SM_CXHTHUMB');
  29.   DoAction(SM_CXICON                     ,'SM_CXICON');
  30.   DoAction(SM_CXICONSPACING              ,'SM_CXICONSPACING');
  31.   DoAction(SM_CXMAXIMIZED                ,'SM_CXMAXIMIZED');
  32.   DoAction(SM_CXMAXTRACK                 ,'SM_CXMAXTRACK');
  33.   DoAction(SM_CXMENUCHECK                ,'SM_CXMENUCHECK');
  34.   DoAction(SM_CXMENUSIZE                 ,'SM_CXMENUSIZE');
  35.   DoAction(SM_CXMIN                      ,'SM_CXMIN');
  36.   DoAction(SM_CXMINIMIZED                ,'SM_CXMINIMIZED');
  37.   DoAction(SM_CXMINSPACING               ,'SM_CXMINSPACING');
  38.   DoAction(SM_CXMINTRACK                 ,'SM_CXMINTRACK');
  39.   DoAction(SM_CXPADDEDBORDER             ,'SM_CXPADDEDBORDER');
  40.   DoAction(SM_CXSCREEN                   ,'SM_CXSCREEN');
  41.   DoAction(SM_CXSIZE                     ,'SM_CXSIZE');
  42.   DoAction(SM_CXSIZEFRAME                ,'SM_CXSIZEFRAME');
  43.   DoAction(SM_CXSMICON                   ,'SM_CXSMICON');
  44.   DoAction(SM_CXSMSIZE                   ,'SM_CXSMSIZE');
  45.   DoAction(SM_CXVIRTUALSCREEN            ,'SM_CXVIRTUALSCREEN');
  46.   DoAction(SM_CXVSCROLL                  ,'SM_CXVSCROLL');
  47.   DoAction(SM_CYBORDER                   ,'SM_CYBORDER');
  48.   DoAction(SM_CYCAPTION                  ,'SM_CYCAPTION');
  49.   DoAction(SM_CYCURSOR                   ,'SM_CYCURSOR');
  50.   DoAction(SM_CYDLGFRAME                 ,'SM_CYDLGFRAME');
  51.   DoAction(SM_CYDOUBLECLK                ,'SM_CYDOUBLECLK');
  52.   DoAction(SM_CYDRAG                     ,'SM_CYDRAG');
  53.   DoAction(SM_CYEDGE                     ,'SM_CYEDGE');
  54.   DoAction(SM_CYFIXEDFRAME               ,'SM_CYFIXEDFRAME');
  55.   DoAction(SM_CYFOCUSBORDER              ,'SM_CYFOCUSBORDER');
  56.   DoAction(SM_CYFRAME                    ,'SM_CYFRAME');
  57.   DoAction(SM_CYFULLSCREEN               ,'SM_CYFULLSCREEN');
  58.   DoAction(SM_CYHSCROLL                  ,'SM_CYHSCROLL');
  59.   DoAction(SM_CYICON                     ,'SM_CYICON');
  60.   DoAction(SM_CYICONSPACING              ,'SM_CYICONSPACING');
  61.   DoAction(SM_CYKANJIWINDOW              ,'SM_CYKANJIWINDOW');
  62.   DoAction(SM_CYMAXIMIZED                ,'SM_CYMAXIMIZED');
  63.   DoAction(SM_CYMAXTRACK                 ,'SM_CYMAXTRACK');
  64.   DoAction(SM_CYMENU                     ,'SM_CYMENU');
  65.   DoAction(SM_CYMENUCHECK                ,'SM_CYMENUCHECK');
  66.   DoAction(SM_CYMENUSIZE                 ,'SM_CYMENUSIZE');
  67.   DoAction(SM_CYMIN                      ,'SM_CYMIN');
  68.   DoAction(SM_CYMINIMIZED                ,'SM_CYMINIMIZED');
  69.   DoAction(SM_CYMINSPACING               ,'SM_CYMINSPACING');
  70.   DoAction(SM_CYMINTRACK                 ,'SM_CYMINTRACK');
  71.   DoAction(SM_CYSCREEN                   ,'SM_CYSCREEN');
  72.   DoAction(SM_CYSIZE                     ,'SM_CYSIZE');
  73.   DoAction(SM_CYSIZEFRAME                ,'SM_CYSIZEFRAME');
  74.   DoAction(SM_CYSMCAPTION                ,'SM_CYSMCAPTION');
  75.   DoAction(SM_CYSMICON                   ,'SM_CYSMICON');
  76.   DoAction(SM_CYSMSIZE                   ,'SM_CYSMSIZE');
  77.   DoAction(SM_CYVIRTUALSCREEN            ,'SM_CYVIRTUALSCREEN');
  78.   DoAction(SM_CYVSCROLL                  ,'SM_CYVSCROLL');
  79.   DoAction(SM_CYVTHUMB                   ,'SM_CYVTHUMB');
  80.   DoAction(SM_DBCSENABLED                ,'SM_DBCSENABLED');
  81.   DoAction(SM_DEBUG                      ,'SM_DEBUG');
  82.   DoAction(SM_DIGITIZER                  ,'SM_DIGITIZER');
  83.   DoAction(SM_IMMENABLED                 ,'SM_IMMENABLED');
  84.   DoAction(SM_MAXIMUMTOUCHES             ,'SM_MAXIMUMTOUCHES');
  85.   DoAction(SM_MEDIACENTER                ,'SM_MEDIACENTER');
  86.   DoAction(SM_MENUDROPALIGNMENT          ,'SM_MENUDROPALIGNMENT');
  87.   DoAction(SM_MIDEASTENABLED             ,'SM_MIDEASTENABLED');
  88.   DoAction(SM_MOUSEPRESENT               ,'SM_MOUSEPRESENT');
  89.   DoAction(SM_MOUSEHORIZONTALWHEELPRESENT,'SM_MOUSEHORIZONTALWHEELPRESENT');
  90.   DoAction(SM_MOUSEWHEELPRESENT          ,'SM_MOUSEWHEELPRESENT');
  91.   DoAction(SM_NETWORK                    ,'SM_NETWORK');
  92.   DoAction(SM_PENWINDOWS                 ,'SM_PENWINDOWS');
  93.   DoAction(SM_REMOTECONTROL              ,'SM_REMOTECONTROL');
  94.   DoAction(SM_REMOTESESSION              ,'SM_REMOTESESSION');
  95.   DoAction(SM_SAMEDISPLAYFORMAT          ,'SM_SAMEDISPLAYFORMAT');
  96.   DoAction(SM_SECURE                     ,'SM_SECURE');
  97.   DoAction(SM_SERVERR2                   ,'SM_SERVERR2');
  98.   DoAction(SM_SHOWSOUNDS                 ,'SM_SHOWSOUNDS');
  99.   DoAction(SM_SHUTTINGDOWN               ,'SM_SHUTTINGDOWN');
  100.   DoAction(SM_SLOWMACHINE                ,'SM_SLOWMACHINE');
  101.   DoAction(SM_STARTER                    ,'SM_STARTER');
  102.   DoAction(SM_SWAPBUTTON                 ,'SM_SWAPBUTTON');
  103.   DoAction(SM_SYSTEMDOCKED               ,'SM_SYSTEMDOCKED');
  104.   DoAction(SM_TABLETPC                   ,'SM_TABLETPC');
  105.   DoAction(SM_XVIRTUALSCREEN             ,'SM_XVIRTUALSCREEN');
  106.   DoAction(SM_YVIRTUALSCREEN             ,'SM_YVIRTUALSCREEN');
  107. end;

On my Windows 10:

Code: [Select]
    8  SM_ARRANGE
    0  SM_CLEANBOOT
    3  SM_CMONITORS
    5  SM_CMOUSEBUTTONS
    0  SM_CONVERTIBLESLA]"]>BlockedDE
    1  SM_CXBORDER
   32  SM_CXCURSOR
    3  SM_CXDLGFRAME
    4  SM_CXDOUBLECLK
    4  SM_CXDRAG
    2  SM_CXEDGE
    3  SM_CXFIXEDFRAME
    1  SM_CXFOCUSBORDER
    8  SM_CXFRAME
 1920  SM_CXFULLSCREEN
   17  SM_CXHSCROLL
   17  SM_CXHTHUMB
   32  SM_CXICON
   75  SM_CXICONSPACING
 1936  SM_CXMAXIMIZED
 5780  SM_CXMAXTRACK
   15  SM_CXMENUCHECK
   19  SM_CXMENUSIZE
  136  SM_CXMIN
  160  SM_CXMINIMIZED
  160  SM_CXMINSPACING
  136  SM_CXMINTRACK
    0  SM_CXPADDEDBORDER
 1920  SM_CXSCREEN
   36  SM_CXSIZE
    8  SM_CXSIZEFRAME
   16  SM_CXSMICON
   22  SM_CXSMSIZE
 5760  SM_CXVIRTUALSCREEN
   17  SM_CXVSCROLL
    1  SM_CYBORDER
   23  SM_CYCAPTION
   32  SM_CYCURSOR
    3  SM_CYDLGFRAME
    4  SM_CYDOUBLECLK
    4  SM_CYDRAG
    2  SM_CYEDGE
    3  SM_CYFIXEDFRAME
    1  SM_CYFOCUSBORDER
    8  SM_CYFRAME
 1137  SM_CYFULLSCREEN
   17  SM_CYHSCROLL
   32  SM_CYICON
   75  SM_CYICONSPACING
    0  SM_CYKANJIWINDOW
 1176  SM_CYMAXIMIZED
 1226  SM_CYMAXTRACK
   20  SM_CYMENU
   15  SM_CYMENUCHECK
   19  SM_CYMENUSIZE
   39  SM_CYMIN
   28  SM_CYMINIMIZED
   28  SM_CYMINSPACING
   39  SM_CYMINTRACK
 1200  SM_CYSCREEN
   22  SM_CYSIZE
    8  SM_CYSIZEFRAME
   23  SM_CYSMCAPTION
   16  SM_CYSMICON
   22  SM_CYSMSIZE
 1206  SM_CYVIRTUALSCREEN
   17  SM_CYVSCROLL
   17  SM_CYVTHUMB
    0  SM_DBCSENABLED
    0  SM_DEBUG
    0  SM_DIGITIZER
    1  SM_IMMENABLED
    0  SM_MAXIMUMTOUCHES
    0  SM_MEDIACENTER
    0  SM_MENUDROPALIGNMENT
    0  SM_MIDEASTENABLED
    1  SM_MOUSEPRESENT
    0  SM_MOUSEHORIZONTALWHEELPRESENT
    1  SM_MOUSEWHEELPRESENT
    3  SM_NETWORK
    0  SM_PENWINDOWS
    0  SM_REMOTECONTROL
    0  SM_REMOTESESSION
    1  SM_SAMEDISPLAYFORMAT
    0  SM_SECURE
    0  SM_SERVERR2
    0  SM_SHOWSOUNDS
    0  SM_SHUTTINGDOWN
    0  SM_SLOWMACHINE
    0  SM_STARTER
    0  SM_SWAPBUTTON
    0  SM_SYSTEMDOCKED
    0  SM_TABLETPC
-1920  SM_XVIRTUALSCREEN
    0  SM_YVIRTUALSCREEN

From https://stackoverflow.com/a/28524464/1037511
SM_CYCAPTION + CYFRAME + 2x SM_CXPADDEDBORDER = 31

Then another thing I found (https://stackoverflow.com/a/21614755/1037511):
Quote
In unthemed Windows, GetSystemMetrics(SM_CYCAPTION) is the height of the text in the title bar; you need to add in the size of the frame and border padding (GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYEDGE) * 2).

For themed Windows (which is the default these days), GetThemeSysSize is most likely the function you're looking for; in particular, GetThemeSysSize(SM_CXBORDER) for the border width, and GetThemeSysSize(SM_CYSIZE) + GetThemeSysSize(SM_CXPADDEDBORDER) * 2 for the title bar.

So the SM_CYCAPTION (23) needs to be plussed with SM_CYSIZEFRAME + 2x SM_CYEDGE which is 8+4. 23 + 12 = 35.
Still not the 39 I get. But that's for unthemed Windows. We are using Themes  %)

(And then we didn't even take DPI into account  ;) )

Hartmut

  • Hero Member
  • *****
  • Posts: 843
Re: How to query the outer size of a 'TForm' ?
« Reply #26 on: October 14, 2024, 11:20:56 am »
Here comes an improved version of my demo in reply # 10. It includes the improvements suggested by 440bx from his replies # 14 and 21 and some debug infos and improved error handling. The main function is in line 70:

Code: Pascal  [Select][+][-]
  1. {determines the outer size of a 'TForm'; WINDOWS ONLY! 12.10.24}
  2.  
  3. unit Unit1;
  4.  
  5. {$mode objfpc}{$H+}
  6. {$apptype console} {neccessary for writeln}
  7.  
  8. interface
  9.  
  10. uses
  11.  Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs;
  12.  
  13. type
  14.  
  15.  { TForm1 }
  16.  
  17.  TForm1 = class(TForm)
  18.   procedure FormActivate(Sender: TObject);
  19.  private
  20.  public
  21.  end;
  22.  
  23. var
  24.  Form1: TForm1;
  25.  
  26. implementation
  27.  
  28. {$R *.lfm}
  29.  
  30. // some common subroutines:
  31.  
  32. procedure show_TRect(R: TRect; msg: string = '');
  33.    {shows the contents of a 'TRect'}
  34.    begin
  35.    if msg <> '' then write(msg);
  36.    writeln('X1=', R.Left, ' Y1=', R.Top, ' X2=', R.Right, ' Y2=', R.Bottom);
  37.    end;
  38.  
  39. procedure show_TPoint(P: TPoint; msg: string = '');
  40.    {shows the contents of a 'TPoint'}
  41.    begin
  42.    if msg <> '' then write(msg);
  43.    writeln('X=', P.X, ' Y=', P.Y);
  44.    end;
  45.  
  46. function winGetWindowRect(h: HWND; var R: TRect): boolean;
  47.    {returns the OUTER coordinates of the window with Handle 'h'}
  48.    var ok: boolean;
  49.    begin
  50.    ok:=windows.GetWindowRect(h, R);
  51.    exit(ok);
  52.    end;
  53.  
  54. function winGetClientRect(h: HWND; var R: TRect): boolean;
  55.    {returns the INNER coordinates of the window with Handle 'h'}
  56.    var ok: boolean;
  57.    begin
  58.    ok:=windows.GetClientRect(h, R);
  59.    exit(ok);
  60.    end;
  61.  
  62. function winClientToScreen(h: HWND; var P: TPoint): boolean;
  63.    {converts INNER coordinates of the window with Handle 'h' to screen coordinates}
  64.    var ok: boolean;
  65.    begin
  66.    ok:=windows.ClientToScreen(h, P);
  67.    exit(ok);
  68.    end;
  69.  
  70. function queryFormOuterSizeWin(out TitelHeight,BorderWidth: integer): integer;
  71.    {determines the Height of the TitleBar and the Width of the left, right and
  72.     bottom Border of a 'TForm' on WINDOWS only.
  73.     Result: Error-Code (0=OK)}
  74.    var F: TForm;
  75.        R1,R2: TRect;
  76.        P: TPoint;
  77.        e: integer;
  78.    begin
  79.    TitelHeight:=-1; {if an error occurs}
  80.    BorderWidth:=-1;
  81.    e:=0; {no error until now}
  82.  
  83.    F:=TForm.CreateNew(nil);
  84.    F.SetBounds(150,160,170,180); {different values and avoids negative coordinates}
  85. // F.Visible:=true; {Form MUST NOT be visible!!}
  86.    writeln('Handle=', F.Handle);
  87.                                 {returns the OUTER coordinates of the window: }
  88.    if not winGetWindowRect(F.Handle, R1) then e:=1;
  89.    show_TRect(R1, 'GetWindowRect: ');
  90.  
  91.                                 {returns the INNER coordinates of the window: }
  92.    if not winGetClientRect(F.Handle, R2) then
  93.       if e=0 then e:=2;
  94.    show_TRect(R2, 'GetClientRect: ');
  95.  
  96.                    {converts topleft INNER coordinates to screen coordinates: }
  97.    P:=R2.TopLeft;
  98.    if not winClientToScreen(F.Handle, P) then
  99.       if e=0 then e:=3;
  100.    show_TPoint(P, 'ClientToScreen: ');
  101.  
  102.    F.Free;
  103.    if e <> 0 then exit(e);
  104.                                                     {do some validity checks: }
  105.    if P.X <> F.Left then exit(4);
  106.    if P.Y <> F.Top  then exit(5);
  107.    if R2.Right  <> F.Width  then exit(6);
  108.    if R2.Bottom <> F.Height then exit(7);
  109.  
  110.    TitelHeight:=P.Y-R1.Top; {only if everything is OK: }
  111.    BorderWidth:=P.X-R1.Left;
  112.    exit(0);
  113.    end; {queryFormOuterSizeWin}
  114.  
  115. { TForm1 }
  116.  
  117. procedure TForm1.FormActivate(Sender: TObject);
  118.    var TitelHeight,Border_Width,error: integer;
  119.    begin
  120.    error:=queryFormOuterSizeWin(TitelHeight,Border_Width);
  121.    writeln('Error=', error, ' TitelHeight=', TitelHeight, ' BorderWidth=', Border_Width);
  122.    Close;
  123.    end;
  124.  
  125. end.

On my Win7 the result is:
Code: [Select]
Handle=132550
GetWindowRect: X1=146 Y1=136 X2=324 Y2=344
GetClientRect: X1=0 Y1=0 X2=170 Y2=180
ClientToScreen: X=150 Y=160
Error=0 TitelHeight=24 BorderWidth=4

On my Win10 the last line changes to:
Code: [Select]
Error=0 TitelHeight=31 BorderWidth=8

If you have Win11: could you please run this code (attached as project) and report the result? Thanks a lot.

@rvk: thanks a lot for your new post. I will check/test it and report later.

dseligo

  • Hero Member
  • *****
  • Posts: 1406
Re: How to query the outer size of a 'TForm' ?
« Reply #27 on: October 14, 2024, 11:31:20 am »
If you have Win11: could you please run this code (attached as project) and report the result? Thanks a lot.

Code: Text  [Select][+][-]
  1. Handle=9114076
  2. GetWindowRect: X1=150 Y1=160 X2=336 Y2=379
  3. GetClientRect: X1=0 Y1=0 X2=170 Y2=180
  4. ClientToScreen: X=158 Y=191
  5. Error=4 TitelHeight=-1 BorderWidth=-1

rvk

  • Hero Member
  • *****
  • Posts: 6572
Re: How to query the outer size of a 'TForm' ?
« Reply #28 on: October 14, 2024, 03:11:59 pm »
Even on my Windows 10 it gives:
Quote
Handle=199312
GetWindowRect: X1=150 Y1=160 X2=336 Y2=379
GetClientRect: X1=0 Y1=0 X2=170 Y2=180
ClientToScreen: X=158 Y=191
Error=4 TitelHeight=-1 BorderWidth=-1

But that's because you have a validity check which always fails.
Code: Pascal  [Select][+][-]
  1.  if P.X <> F.Left then exit(4);
150 <> 158

BTW If I add this after the SetBounds it gives me this.
Code: Pascal  [Select][+][-]
  1.    F.SetBounds(150,160,170,180); {different values and avoids negative coordinates}
  2.    SetWindowPos(F.Handle, 0, 150, 160, 170, 180, SWP_NOZORDER + SWP_NOACTIVATE);

Quote
Handle=1051222
GetWindowRect: X1=150 Y1=160 X2=320 Y2=340
GetClientRect: X1=0 Y1=0 X2=154 Y2=141
ClientToScreen: X=158 Y=191
Error=4 TitelHeight=-1 BorderWidth=-1
But that's again including the shadow borders.

« Last Edit: October 14, 2024, 03:34:02 pm by rvk »

Hartmut

  • Hero Member
  • *****
  • Posts: 843
Re: How to query the outer size of a 'TForm' ?
« Reply #29 on: October 14, 2024, 06:06:42 pm »
If you have Win11: could you please run this code (attached as project) and report the result? Thanks a lot.

Code: Text  [Select][+][-]
  1. Handle=9114076
  2. GetWindowRect: X1=150 Y1=160 X2=336 Y2=379
  3. GetClientRect: X1=0 Y1=0 X2=170 Y2=180
  4. ClientToScreen: X=158 Y=191
  5. Error=4 TitelHeight=-1 BorderWidth=-1

Even on my Windows 10 it gives:
Quote
Handle=199312
GetWindowRect: X1=150 Y1=160 X2=336 Y2=379
GetClientRect: X1=0 Y1=0 X2=170 Y2=180
ClientToScreen: X=158 Y=191
Error=4 TitelHeight=-1 BorderWidth=-1

My code in reply #26 works perfectly on my Win7 and my Win10, but absolutely not on the Win11 of dseligo (thank you for testing) and not on the Win10 of rvk (thank you too for testing). So I see no way (with reliable effort) to get this approach with WIN-API-functions to work trusty.



But GetSystemMetrics() is an usable approach:

Small snippet to get all the values from GetSystemMetrics.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
...

Thank you rvk for this code, it was usefull for me.

Quote
From https://stackoverflow.com/a/28524464/1037511
SM_CYCAPTION + CYFRAME + 2x SM_CXPADDEDBORDER = 31

Then another thing I found (https://stackoverflow.com/a/21614755/1037511):
Quote
In unthemed Windows, GetSystemMetrics(SM_CYCAPTION) is the height of the text in the title bar; you need to add in the size of the frame and border padding (GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYEDGE) * 2).

For themed Windows (which is the default these days), GetThemeSysSize is most likely the function you're looking for; in particular, GetThemeSysSize(SM_CXBORDER) for the border width, and GetThemeSysSize(SM_CYSIZE) + GetThemeSysSize(SM_CXPADDEDBORDER) * 2 for the title bar.

So the SM_CYCAPTION (23) needs to be plussed with SM_CYSIZEFRAME + 2x SM_CYEDGE which is 8+4. 23 + 12 = 35.
Still not the 39 I get. But that's for unthemed Windows. We are using Themes  %)

Thank you very much for this info, this was my solution. I decided to use GetSystemMetrics() with SM_CYCAPTION + SM_CYSIZEFRAME + 2x SM_CYEDGE. The results are maybe not "perfect", but absolutely usable for my purpose.

Many thanks to all who helped me.

 

TinyPortal © 2005-2018