I have a TForm and TRichEdit inside it (alClient, no borders). I need to adjust the width & height of the TForm depending on the TRichEdit contents.
Ok. I have the code which calculates the accurate WIDTH and also gets into account the possibility of TRichEdit to have different formating, like BOLD, etc.
But I made some overlook in the part which calculates the HEIGHT...
In fact I used a *constant* 14, for Arial of size 8. (Yes, I know that's a bad idea :) That worked fine untill I tried the program on a PC on which some standard Windows view settings were changed. I don't know what was exactly changed but I noticed HIGH screen resolution with BIG caption bar, and BIG minimize, maximize, close gadgets.
Can you please tell me:
1) What can I do to avoid that Windows settings affect my app? (What is the property Scaled really for?)
2) Which of the three routines below should I use to calculate the hight in my case, and why the Button1 & Button3 returns the result of 14, and the Button2 the result of 13 (the both TForm & TRichEdit hah the Arial size 10).
> I don't know what was exactly changed but I noticed HIGH > screen resolution with BIG caption bar, and BIG minimize, > maximize, close gadgets.
The changed *thing* was DPI resolution, which was changed from 96 to 120.
An now, I'm more confused than before:
When I change the resolution from 96 to 120, and reopen my project, I can see that TRichEdit::Font::Size was changed in my IDE from 8 to 7 (?!).
Then, when I change the resolution again from 120 to 96, and reopen my project, I can see that TRichEdit::Font::Size was changed in my IDE from 7 to 8 (?!).
The TRichEdit::Font::Height is the same: -11.
In the BCB Help, I was found this formula: Font.Size = - Font.Height * 72 / Font.PixelsPerInch ...which can explane how FontSize changed:
> Ok. I have the code which calculates the accurate WIDTH > and also gets into account the possibility of TRichEdit to have > different formating, like BOLD, etc.
> But I made some overlook in the part which calculates the > HEIGHT...
An alternative way to calculate the dimensions of the TRichEdit content is to not involve any Canvas at all. Use the EM_GETLINECOUNT, EM_LINEINDEX, EM_LINELENGTH, and EM_POSFROMCHAR messages instead. Given particular character indexes, you can calculate absolute pixel offsets for any character in the content, regardless of the font, style, dpi, etc.
For example (untested):
int lines = RichEdit1->Perform(EM_GETLINECOUNT, 0, 0); if( lines > 0 ) { POINTL pt; int idx, len;
int width = 0; int height = 0;
for(int x = 0; x < lines; ++x) { idx = RichEdit1->Perform(EM_LINEINDEX, x, 0); len = RichEdit1->Perform(EM_LINELENGTH, idx, 0);
> The code finds width OK, but the height remains 5.
I forgot that EM_POSFROMCHAR returns the coordinates of the top-level corner of a character. Use EM_GETCHARFORMAT to get the width and height of a character as well. For example (again, untested):
int num_lines = RichEdit1->Perform(EM_GETLINECOUNT, 0, 0); if( num_lines > 0 ) { POINTL pt; int line_idx, line_len;
int width = 0; int height = 0;
CHARFORMAT fmt; CHARRANGE cr;
// these are so that changes to the current selection later // on will not be displayed onscreen while looping... RichEdit1->Perform(WM_SETREDRAW, FALSE, 0); int oldmask = RichEdit1->Perform(EM_SETEVENTMASK, 0, 0);
> I'v chaged the existing part of code like this, and I > hope it's OK now... (there were also some typos ; -> ,)
> for(int x = 0; x < lines; ++x)
That was not the correct thing to do with that loop. I was intentionally looping through the characters of the last line only, not looping through all of the lines a second time. I was attempting to find the largest character in the last line, as that would dicate the height of the RichEdit.
Because it was not accurate to begin with. EM_POSFROMCHAR returns the pixel offset of the top-left corner of a character. I incorrectly assumed that the y offset would include the actual height of the character, which it does not. EM_GETCHARFORMAT, on the other hand, can return the actual height of a character, which I then add to the top-left offset of the character.
> One of the problems is: > RichEdit1->Perform(EM_GETLINECOUNT, 0, 0); > which returns the undesired lines count. I've elaborated > that in the code below.
> __fastcall TForm1::TForm1(TComponent* Owner) > : TForm(Owner) > { > // make it small because of testing > RichEdit1->Width=60; > RichEdit1->Height=60;
> // clear the default (RichEdit1) contents > RichEdit1->Lines->Clear();
> // remarks "x : y" are "line_idx : line_len" by the ShowMessage() in > the loop
> The last block of code which calculates the height still does > not work accutare. Do you see what could be wrong with it?
It does not convert the yHeight value. The documentation for the CHARFORMAT structure does not state the unit of measurement that is used. It turns out that the yHeight (and yOffset) value is in twips, not pixels. So you have to convert the value to pixels manually. With that in mind, the following code gets much closer to the truer sizes for me:
int num_lines = RichEdit1->Perform(EM_GETLINECOUNT, 0, 0); if( num_lines > 0 ) { if( RichEdit1->Perform(EM_LINELENGTH, RichEdit1->Perform(EM_LINEINDEX, num_lines-1, 0), 0) == 0 ) --num_lines; } if( num_lines > 0 ) { // this is so that changes to the current selection later // on will not be displayed onscreen while looping... RichEdit1->Perform(WM_SETREDRAW, FALSE, 0); try { // this is to speed up access to the RichEdit by disabling notifications... int oldmask = RichEdit1->Perform(EM_SETEVENTMASK, 0, 0); try { // this is to preserve the current selection so that it can be restored later... CHARRANGE oldrange = {0}; RichEdit1->Perform(EM_EXGETSEL, 0, (int)&oldrange); try { POINTL pt; int line_idx, line_len;
int width = 0; int height = 0; int char_height;
CHARFORMAT fmt; CHARRANGE cr;
// this is used in the twips->pixels conversion HDC hdc = GetDC(RichEdit1->Handle); int LogicalPixelsY = GetDeviceCaps(hdc, LOGPIXELSY); ReleaseDC(RichEdit1->Handle, hdc);
// divide by 20 to convert twips to points, and // then use MulDiv() to convert points to pixels... char_height = MulDiv(fmt.yHeight / 20, LogicalPixelsY, 72);
The only problem left is that the lowercase 'g' and 'y' characters are still not calculated properly. The pixel offset of the top-left corner is correct but the height reported by EM_GETCHARFORMAT seems to be missing some extra spacing that actually exists in those characters. I don't have a solution for that.