Solved

Screen Sizing

Posted on 1997-08-13
7
516 Views
Last Modified: 2013-12-03
I have a program that I want to accomodate ALL screen sizes/font sizes and am a little lost. I designed my screens at 640x480 and have a routine that "resizes" everything when the screen resolution changes from the original developed size (640x480).

This seems to work pretty good except when I try to change the font sizes (ie. 800x600 Large Fonts). As you can guess it goes crazy. The main window doesn't size correctly at all. and as a result, the new screen looks crazy!!!

Is there a way to accomodate for screen resolution to include font sizes?

Any help would be much appreciated.
0
Comment
Question by:jkb2
  • 3
  • 3
7 Comments
 
LVL 5

Accepted Solution

by:
ronit051397 earned 100 total points
ID: 1341349
This is from Lloyd's Help file. Didn't check it though.

Screen Resolution

When designing forms, it is sometimes helpful to write the code  so that the screen and all of its objects are displayed at the  same size no matter what the screen resolution is.  Here is  some code to show how that is done:

implementation
const
  ScreenWidth: LongInt = 800; {I designed my form in 800x600 mode.}
  ScreenHeight: LongInt = 600;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  i, OldFormWidth: integer;
begin

  scaled := true;
  if (screen.width <> ScreenWidth) then begin
    OldFormWidth := width;
    height := longint(height) * longint(screen.height) DIV ScreenHeight;
    width := longint(width) * longint(screen.width) DIV ScreenWidth;
    scaleBy(screen.width, ScreenWidth);
    font.size := (Width DIV OldFormWidth) * font.size;
  end;
end;


Then, you will want to have something that checks to see that  the font sizes are OK.  Before you change the font's size, you  would need to ensure the object actually has a font property by checking the RTTI.   This can be done as follows:

USES TypInfo;  {Add this to your USES statement.}

var
  i: integer;
begin
  for i := componentCount - 1 downto 0 do
    with components[i] do
    begin
      if GetPropInfo(ClassInfo, 'font') <> nil  then
        font.size := (NewFormWidth DIV OldFormWidth) * font.size;
    end;
end;

{This is the long way to do the same thing.}
var
  i: integer;
  p: PPropInfo;
begin
  for i := componentCount - 1 downto 0 do
    with components[i] do

    begin
      p := GetPropInfo(ClassInfo, 'font');
      if assigned(p) then
        font.size := (NewFormWidth DIV OldFormWidth) * font.size;
    end;
end;


Note:  not all objects have a FONT property.  This should be  enough to get you started.  The font size changes are based on changing the font.size of the form.  To notice small font size changes, try using a true type font.

Note:  The following are issue to bear in mind when scaling  Delphi applications (forms) on different screen resolutions:

  * Decide early on in the form design stage whether you're  going to allow the form to be scaled or not.  The advantage of  not scaling is that nothing changes at runtime.  The  disadvantage of not scaling is that nothing changes at runtime  (your form may be far too small or too large to read on some  systems if it is not scaled).


  * If you're NOT going to scale the form, set Scaled to False.

  * Otherwise, set the Form's Scaled property to True.

  * Set AutoScroll to False.  AutoScroll = True means 'don't  change the form's frame size at runtime' which doesn't look  good when the  form's contents do change size.

  * Set the form's font to a scaleable TrueType font, like  Arial.   MS San Serif is an ok alternate, but remember that it  is still a  bitmapped font.  Only Arial will give you a font  within a pixel of the desired height.  NOTE: If the font used  in an application is not installed on the target computer, then  Windows will select an  alternative font within the same font  family to use instead.  This font may not match the same size  of the original font any may cause problems.

  * Set the form's Position property to something other than  poDesigned.  poDesigned leaves the form where you left it at  design time, which for me always winds up way off to the left  on my 1280x1024 screen -  and completely off the 640x480 screen.

  * Don't crowd controls on the form - leave at least 4 pixels  between  controls, so that a one pixel change in border  locations (due to  scaling) won't show up as ugly overlapping  controls.

  * For single line labels that are alLeft or alRight aligned,  set AutoSize to True.  Otherwise, set AutoSize to False.

  * Make sure there is enough blank space in a label component  to allow for font width changes - a blank space that is 25% of  the length of the current string display length is a little too  much, but safe. (You'll need at least 30% expansion space for  string labels if you  plan to translate your app into other  languages) If AutoSize is  False, make sure you actually set  the label width appropriately.  If AutoSize is True, make sure
there is enough room for the label  to grow on its own.

  * In multi-line, word-wrapped labels, leave at least one line  of blank space at the bottom.  You'll need this to catch the  overflow when the text wraps differently when the font width  changes with scaling. Don't assume that because you're using  large fonts, you don't have to allow for text overflow -  somebody else's large  fonts may be larger than yours!

  * Be careful about opening a project in the IDE at different  resolutions.  The form's PixelsPerInch property will be  modified as soon as the form is opened, and will be saved to  the DFM if you save the project. It's best to test the app by  running it standalone, and edit the form at only one  resolution. Editing at varying resolutions and font sizes  invites component drift  and sizing problems.

  * Speaking of component drift, don't rescale a form multiple  times, at design time or a runtime.  Each rescaling introduces  roundoff errors which accumulate very quickly since coordinates  are  strictly integral.  As fractional amounts are truncated  off control's origins and sizes with each successive  rescaling,  the controls will appear to creep northwest and get  smaller. If you want to allow your users to rescale the form  any number  of times, start with a freshly loaded/created form  before each  scaling, so that scaling errors do not accumulate.

  * Don't change the PixelsPerInch property of the form, period.

  * In general, it is not necessary to design forms at any  particular resolution, but it is crucial that you review their  appearance at 640x480 with small fonts and large, and at a  high-resolution with small fonts and large before releasing  your app.  This should be  part of your regular system  compatibility testing checklist.

  * Pay close attention to any components that are essentially   single-line TMemos - things like TDBLookupCombo.  The Windows   multi-line edit control always shows only whole lines of text  -  if the control is too short for its font, a TMemo will show   nothing at all (a TEdit will show clipped text). For such   components, it's better to make them a few pixels too large  than to be one pixel too small and show not text at all.

  * Keep in mind that all scaling is proportional to the  difference  in the font height between runtime and design time,  NOT the pixel resolution or screen size.  Remember also that  the origins of your controls will be changed when the form is  scaled - you can't very  well make components bigger without  also moving them over a bit.

{This code came from Lloyd's help file!}
-------------------------------------------------------------
Q:  How do I get RTTI (run time type information) from components?

A:

The screen resolution topic shows one way.  Here is another example:

Uses TypInfo;

Function AssignFontProperty( anObj: TObject; Const fname: String ):
  Boolean;
Var
  PInfo: PPropInfo;
  aFont: TFont;
  FontGet: Function( anObj: TObject ): TFont;
Begin
  (* try to get a pointer to the property information for a property with the
     passed name. TObject.ClassInfo returns a pointer to the RTTI table, which

     we need to pass to GetPropInfo *)
  PInfo := GetPropInfo( anObj.ClassInfo, 'font' );
  Result := PInfo <> Nil;
  If result Then
    (* found a property with this name, check if it has the correct type *)
    If (PInfo^.Proptype^.Kind = tkClass) and
        GetTypeData(PInfo^.Proptype)^.ClassType.InheritsFrom(TFont) Then Begin
      { try to get the read proc for this property }
      @FontGet:= PInfo^.GetProc;
      If Assigned( FontGet ) and (PtrRec(PInfo^.GetProc).Seg <> $FFFF) Then

        { hiword of $FFFF seems to signify a property wich reads directly
          from a field instead of via a method! }
      Begin
        aFont := FontGet( anObj );
        If aFont Is TFont Then
          aFont.Name := fname
        Else
          ShowMessage('FontGet barfed');
      End
      Else Begin
        { no read method for the property, get field directly }
        aFont := TFont(GetOrdProp( anObj, PInfo ));
        If aFont Is TFont Then
          aFont.Name := fname

        Else
          ShowMessage('GetOrdProp barfed');
      End;
    End
    Else Begin
      (* nope, wrong type, complain *)
      Result := False;
      ShowMessage( 'Property Font is not of type TFont!');
    End;
End;

procedure TForm1.BtnTestClick(Sender: TObject);
Var
  i: Integer;
begin
  For i:= 0 To ComponentCount-1 Do
    AssignFontProperty( Components[i], 'Symbol' );
end;

Put a few controls on your form, assign the BtnTestClick handler to a buttons OnClick property, run the app, click on the button and all will be greek to you <g>.

Here is a tip or two on using this information:

You already know which properties that one has in most cases. So it would only be a useful method in a base class of a class hierarchy (e.g. in TObject, which you cannot modify). A function that only looks for a property by name would require only one line of code:

 Function HasProperty( anObj: TObject; Const name: String ): Boolean;
 Begin
   Result := GetPropInfo( anObj.ClassInfo, name ) <> Nil;
 End;


But that is not very useful, it still does not solve the problem of how to _use_ the property!

 Var
   aComponent: TComponent;
   i: Integer;
 begin
   For i:= 0 To ComponentCount-1 Do Begin
     aComponent := Components[i];
     If HasProperty( aComponent, 'Font' ) Then
       aComponent.Font.Name := ....    <== will not compile

You still would need to cast aComponent to a class that has Font as public or published property and of course it better be one that is in the ancestry of aComponent. Using the run-time type info for the access solves this problem but is a bit awkward as you saw.

{This code came from Lloyd's help file!}
0
 
LVL 1

Expert Comment

by:millerw
ID: 1341350
I tried good ol' Lloyds example code and ended up with some really weird results.  Eg my form ended up being really small on the screen and the fonts were really large.  I found that most of his text helps but the font stuff is a bunch of bull.  Here is what I did.  First, DON'T SCALE THE FORM.  That only causes problems.  Leave the form the way it was designed and make it DefaultPosOnly or something like that depending on your needs.  DON'T USE THE DESIGNED POSITION--CAN LEAD TO MAJOR PROBLEMS.  Then put this code in a unit that can be shared by all your forms:

procedure ResolutionResolver (Form: TForm);
begin
     Form.Font.Size := (ScreenWidth div LongInt(Screen.Width)) *
                       Form.Font.Size;
     Form.Invalidate;
end;

(Call this in the Create event of a form and give it Self as its parameter.)

Where ScreenWidth is the same thing as in Lloyd's example.  That will fix the fonts.  EXCEPT UNDER ONE CONDITION!

Here is the condition:  If you have a label or something else and it is not visible (eg behind a panel that is poped up later or on a tab that will come up at the user's choice) the object will NOT RESIZE EVEN WITH AUTOSIZE ON.  To fix this, you must reassign its caption after you run the resolution resolver above so that it will resize.  A better way is to use panels instead of labels when you can.  They will not mess up like this at all.  Well, they might but I always give my panels an alignment (eg top, bottom, left, right, or client).  

Panels are the best, especially if you want fully resizable forms.  You can make the objects move smaller and bigger with your panels if they are given some alignment other than None.  Additionally, if the form is fully resizable, it is not afftected by resolution problems.  TLabels are the worst and using a Tpanel solves this (just set its caption to what ever you want and align it to center, left or right then put your TEdit or whatever at least 4 pizels away from the text in the TPanel).  

If you want an example of a fully resizable form that has no problems with resolution, I can supply you with one and let you see how to design forms so they are all not affected by resolution.  (The form I'm designing now doesn't even use the Resolution resolver function and works on any resolution---having the computer handle all your resolution problems automatically is the best way of doint it).  

The main point to making worry free resolution changing forms is to use TPanel extensively and use its OnResize event to resize any TEdits or TLabels (I recommend never using the TLabel anyway) as needed.  Not a whole lot of code is required.  Lloyd's example is good, and don't get me wrong here---his example got me started on making non-resolution specific forms, but uses alot of code to do the job that the forms can do themselves.  

Additionally all components can be made to have the Align property available to you (eg TEdit normally doesn't have it available) by deriving a new component from it and making the Align property published.  I have example code for this too----really, really simple to do.

Let me know if you want example code and example programs on how to do all this stuff.  It is all simple to do, except when you have designed your froms already and have not done them the right way (I'm going to have to go back and take out the TLabel components on one form to make it non-resolution specific).  

One more thing about TLabel, if you must use one, set its Align property to something other than None.  TPanel will do the same thing as a TLabel, that is why I am starting to move away from using a TLabel at all.

Get back to me if you still need help or want the code and example.

Good Luck,
Scott
0
 

Expert Comment

by:miauw
ID: 1341351
Keep it simple when possible!

I use a much simpler method to make my applications
usable with a resolutions and fontsizes. It works fine for all fullscreen applications. 640*480, ..,1600*200, Large fonts
,small fonts, custom fonts, if the design font is not present on target computer, etc.  It just doesn't matter, because
everything needed is adjusted runtime..


This is my method:

I virtually divide the form in e.g. 20 or 40 verticale rows,
and in 5 or 8 horizontal rows (the number doesn't matter,
only depends on the desired layout).

Each screen element (labels, buttons, everything)
then must use exactly 1 or more verticale rows , and 1 or more horizontal columns.

So an element x has four variables defined:
xRowStart, xNumberof Rows, xColummStart, xNumberofColumns

When the application (or the resolution changes) starts,
the applicationt calculates all the needed left, top, width
and height variables for all the screen elements (using
a WinApi call to get e.g. Form.Caption.Height, and
Bordermeasures and beside that only screen.width and
screen.height).

The fontproblem is handled in a similar matter:
before the running program makes a string on the form, a label or whatever visible, I first measure the pixelwidth the string will have (can be done in numerous way, e.g. with a hidden label, with
a canvas property, a WinApi call). I know the pixelwidth
of the screenelement the string must be placed on (see
above). If the string doesnt' fits, I just reduce the fontsize
by one until till it fits (or till it fits on x% if I would want that ). (same goes for the pixelheight) (actuallly I use a smarter and faster algoritme,
but you get the idear.

That's all.
         
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 1

Expert Comment

by:millerw
ID: 1341352
Doesn't sound simple to me.  Sounds like alot of "reinventing the wheel."  If you can get a computer to do it ALL BY ITSELF without a single line of code (which is what I was talking about) that is the better way as far as I'm concerned.  But, that is my opinion.  

About the only thing you have to be concerned about with my method, is to make sure all objects are aligned in some way (redefining components so that the Align property is available takes less than 1 min to do).  Additionally, with Delphi 3, they have expanded label with a few extra properties that make it easier and better to use in some cases than a Panel.  

I'm into letting the computer do all that it can do.  Therefore, if the computer can do it all by itself (and you are not restricted in placement by a virtual grid of some kind) then that is the method for me.  

Your method does sound interesting, and I'm always into learning.  If you want to share the code, it would be interesting to see how someone else did it.  But, if your the kind to keep an idea under wraps, then nevermind.

Good luck,
Scott
0
 

Expert Comment

by:miauw
ID: 1341353
To millerw/Scott:

My method is simple, maybe by lack of knowlegde of delphi
but the amount of administration isn't that large, and the
clearity is nice. Offcourse there will be a better way to
do this. But this works (in any language).

I don't have a clear cut example without sending your a
complete program, which I won't, but some sample code will give your the idear.

Suppose I have a form with a Tbutton and a TEdit

Var YunitsEdit1: integer = 2;  
//number of vertical units Edit1 uses
      YposEdit1: integer = 4 ;  
//vertical unit where Edit1 starts
      YunitsButton1: integer = 1;
//number of vertical units Button1 uses
      YposButton1: Integer =8;
//vertical unit where Button1 starts.

..same for horizontal stuff

    Xtot: integer; // at full screen = screen.width
    Ytot: integer; // at full screen = screen.height
    Xeff: integer;
 // width effectively to be used
    Yeff: integer;
 // height effectively to be used

    Xstart: integer;
// where the leftmost side effectively starts
    Xend: integer;   // ~ ends
..same for the vertical stuff

xnumberofunits: integer;
// the effective area is divided into ~ parts
..same for the vertical stuff

// before any change to the screen and a startup I call

procedure PreCalcScreenLayout(var depends on your program,
but you can maKe it rather generic)
begin
Ytot:=Screen.Height;
Yeff:=Ytot - getsystemMetrics(SM_CYCAPTION) -
             2* getsystemMetrics(SM_CYBorder)-
             ...(depends on your program, e.g. menu height_;
Yunit:=Yeff div ynumberofunits;

Edit1Top:=trunc(YposEdit1 * yunit);
Edit1Height:= trunc(YunitsEdit1 * yunit);
Edit1width:=.. and so on (or use Tobject arrays)
end;

After PreCalcScreenLayout is GetMaxFontSize is called,
this one determines whether
all the text will fit onto the elements using the values
calculated by PreCalcScreenLayout and a proposed
font size: if not for each element the fontsize is reduced until it does.

So, I can runtime change e.g. numberofyunits to get a more
compressed screen, I can change the fontsize, it doesn't
matter, It will always fit, on any screen.
Maybe more intelligent methods will work too, but I dont'
like testing my programs in every possible screenlayout/resolution and so one.
0
 
LVL 1

Expert Comment

by:millerw
ID: 1341354
Glad your happy with your method.  I dislike variables like you are using that only hold a value (even though the optimizer takes care of them if you do no calculations with them).  I personally couldn't do it your way, and enjoy the code.  Sorry to have to admit that.

By the way, how does your method handle form resizing and all?  It looks like your method would require a call to your function in the OnResize event of the form (or a panel thereof).  

Glad you are happy with your method.  To each his own, after all.

Good Luck,
Scott
0
 

Expert Comment

by:miauw
ID: 1341355
To Millerw, to answer your question:

A resize by quickres e.g. I handle in this way:

procedure TForm1.WndProc(var Message: TMessage);
begin
inherited WndProc(Message);
If (Message.Msg = WM_DISPLAYCHANGE)
Then begin
     ProgMetric.PreCalcScreenLayout;
     SetScreenLayout;
     end;
end;

ProgMetric is the object that contains all the values
and functions that determine where a screenelement
should be placed. SetscreenLayout mainly only assigns
these values to the screenelements depending on the value of
the corresponding ProgMetric fields.
A resize of the form is handled in a similar fashion.

I got bored using other methods, when I saw my programs sometimes looking loosy on computers with a non-typical Windows setting
and/or resolution, was trying to fix that and having to test over and over again in all possible settingscombinations. That testing costed me more time then setting it up this way.
Furthermore I can not test how my program is going to look in
a e.g. 1400 by 1280 resolution, with Bloody as the systemfont. Now I know in advance. It will fit just fine.


0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now