Hotspot on different Screen resolution

Hi expert,
I am writing an application in Delphi 5 that have a image that covers the
whole form. I used TShape components in different part of the image to check
for mouse down event. When user press his/her mouse at different part of the
image, the respective codes will be executed. However, I am facing problem
when user were to use different screen resolution. The Image and TShape
won't stretch or resize accordingly. How should I go about it?
Hope to hear from any one of u.
Thank you.

SA/\/\
"The root of happiness is helpfulness."
LVL 1
samchanAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

LischkeCommented:
Hi Sam,

you won't come around to scale the form explicitly. Imaging you start the application. Say you have stored the screen resolution at which you designed the form then use:

const
  OldScreenX = 1024;
  OldcreenY = 768;

procedure TMainForm.ApplicationEvents1Activate(Sender: TObject);
begin
  Width := Round(Width * Screen.Width / OldScreenX);
  Height := Round(Height * Screen.Width / OldScreenY);
end;

Your image is surely aligned with alClient so it will scale automatically. Your shapes need additional work. The easiest way would be to use the Anchor property to let them track changes in form size (set it to [akLeft,akTop,akRight,akBottom]).

Ciao, Mike
0
LischkeCommented:
Ooops, the second line in the event should read:

Height := Round(Height * Screen.Height / OldScreenY);

Ciao, Mike
0
samchanAuthor Commented:
This won't work. TShape will not stretch accordingly with TImage. Thus, the position pinpoint by the mouse cusor will not point to the correct TShape so that I can invoke the right code to execute.
0
Fundamentals of JavaScript

Learn the fundamentals of the popular programming language JavaScript so that you can explore the realm of web development.

kretzschmarCommented:
8^)
no time yet, but will follow this thread and if not solved until i've time, then i bring in some ideas (if i have some)
maybe the scaleby-method helps, and if not supported, then scale byself
0
LischkeCommented:
Meikl, ScaleBy was the first thing I thought about, but it does not allow to separately scale horizontal and vertical.

Sam,

yes, you are right. I forgot that the anchor property behaves a bit different...

Essentially, you already got what you need here. I showed you already how to scale the form according to the screen resolution. Now I'd suggest to set a handler for OnResize of the form. This handler now takes care for scale of the other controls on it. This approach also enables you to let the user scale the form as (s)he likes and still all your stuff will work. In your handler you can then scale/position all controls. In order to avoid rounding errors I suggest that you store positions and size of each control which you want to adjust:

type
  ControlRec = record
    Control: TControl;
    R: TRect;
  end;

const
  DefaultFormHeight = 600;
  DefaultFormWidth = 800;

  OriginalBounds: array[0..ControlsToAdjust - 1] of ControlRec =
    (Control: Edit1; R: (20, 20, 150, 20),
     Control: Edit2; R: (180, 20, 150, 20),
     ...
    );

Here I'd use TRect.TopLeft as position and TRect.BottomRight as Height/Width of the particular control. The Handler would then do:

procedure TMainForm.FormResize(Sender: TObject);

var
  I: Integer;
  ScaleX,
  ScaleY: Single;

begin
  ScaleX := Width / DefaultFormWidth;
  ScaleY := Height / DefaultFormHeight;
  for I := 0 to ControlsToAdjust - 1 do
    with OriginalBounds[I] do
      Control.SetBounds(Round(R.Left * ScaleX),
                       Round(R.Top * ScaleY),
                       Round(R.Right * ScaleX),
                       Round(R.Bottom * ScaleY));
end;



Well, this looks quite easy. If you don't like to create a large constant array which you have to update every time you add/delete/modify a control on your form then you can create and fill it at form creation time (using ControlCount and Controls[I] properties of TForm).

Ciao, Mike
0
samchanAuthor Commented:
Nope. It still doesn't work. Maybe the stretch algorithm is not so simple as Width/DefaultFormWidth because it doesn't work even for the same resolution when i resize the form. The image will stretch whereas TShape won't and applying just the above algorithm won't work i think. :(
0
LischkeCommented:
Here's the exact code and it DOES work:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    OD: TOpenDialog;
    Image1: TImage;
    Button2: TButton;
    Button3: TButton;
    SD: TSaveDialog;
    Shape1: TShape;
    procedure Button1Click(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    FBitmap: TBitmap;
  public
  end;

var
  Form1: TForm1;

implementation

uses
  GraphicEx;

{$R *.DFM}

type
  ControlRec = record
    Control: TControl;
    R: TRect;
  end;

const
  DefaultFormHeight = 700;
  DefaultFormWidth = 800;
  ControlsToAdjust = 2;

  OriginalBounds: array[0..ControlsToAdjust - 1] of ControlRec =
    ((Control: nil; R: (left: 60; top: 460; right: 160; bottom: 160)),
     (Control: nil; R: (left: 148; top: 48; right: 589; bottom: 385))
    );

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.Button1Click(Sender: TObject);

begin
  with OD do
    if Execute then
    begin
      FBitmap.LoadFromFile(FileName);
      Image1.Picture.Bitmap.Assign(FBitmap);
    end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin
  if Key = #27 then
  begin
    Key:=#0;
    Close;
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormCreate(Sender: TObject);
begin
  OD.Filter := GraphicFilter(TGraphic);
  FBitmap := TBitmap.Create;
  OriginalBounds[0].Control := Shape1;
  OriginalBounds[1].Control := Image1;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FBitmap.Free;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.Button3Click(Sender: TObject);
begin
  with SD do
  begin
    if Execute then Image1.Picture.SaveToFile(FileName);
  end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormResize(Sender: TObject);

var
  I: Integer;
  ScaleX,
  ScaleY: Single;

begin
  ScaleX := Width / DefaultFormWidth;
  ScaleY := Height / DefaultFormHeight;
  for I := 0 to ControlsToAdjust - 1 do
    with OriginalBounds[I] do
    begin
      Control.SetBounds(Round(R.Left * ScaleX),
                       Round(R.Top * ScaleY),
                       Round(R.Right * ScaleX),
                       Round(R.Bottom * ScaleY));
    end;
end;

//----------------------------------------------------------------------------------------------------------------------

end.


Ciao, Mike
0
samchanAuthor Commented:
Well, how could I sent the whole project (mock-up version) to u. Then you could try it out. It just won't work :(
0
LischkeCommented:
You can send the code to public@lischke-online.de.

Please specify what you mean with "it does not work". Is it that no control is resized or are your controls resized to wrong sizes?

Ciao, Mike
0
samchanAuthor Commented:
thank you. I have just sent the code to your e-mail. The control won't resize correctly and the position is wrong as well. Hope to hear from you soon. Once again. Thank you.
0
LischkeCommented:
Well, I got your code and found it almost working. I have changed a few things to make it better, but all in all it worked perfect for me. I don't know what the problem is. However I change the form's size all four shapes just follow and react exactly on the mouse down as if there were never a resize event... Here's the code:

// Well, please try resizing this form (sorry, for giving only partial image because of copyright reason)
// You can also try changing your screen resolution
// The mousedown event will not be trigger because of TShape is at wrong
// position after resizing

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Shape1: TShape;
    Shape2: TShape;
    Shape3: TShape;
    Shape4: TShape;
    procedure FormResize(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure Shape1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Shape2MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Shape3MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure Shape4MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
    //FBitmap: TBitmap;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

type
  ControlRec = record
    Control: TControl;
    R: TRect;
  end;

var
  DefaultFormHeight: Integer;
  DefaultFormWidth: Integer;

  OriginalBounds: array of ControlRec;
 
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin

end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormCreate(Sender: TObject);
begin
//FBitmap := TBitmap.Create;
  DefaultFormHeight := Height;
  DefaultFormWidth := Width;
  SetLength(OriginalBounds, 4);
  with OriginalBounds[0] do begin Control := Shape1; R := Shape1.BoundsRect; end;
  with OriginalBounds[1] do begin Control := Shape2; R := Shape2.BoundsRect; end;
  with OriginalBounds[2] do begin Control := Shape3; R := Shape3.BoundsRect; end;
  with OriginalBounds[3] do begin Control := Shape4; R := Shape4.BoundsRect; end;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormDestroy(Sender: TObject);
begin
//FBitmap.Free;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.Button3Click(Sender: TObject);
begin

end;

//----------------------------------------------------------------------------------------------------------------------

procedure TForm1.FormResize(Sender: TObject);
var
  I: Integer;
  ScaleX,
  ScaleY: Single;
begin
  ScaleX := Width / DefaultFormWidth;
  ScaleY := Height / DefaultFormHeight;
  for I := 0 to High(OriginalBounds) do
    with OriginalBounds[I], R do
    begin
      Control.SetBounds(Round(Left * ScaleX),
                       Round(Top * ScaleY),
                       Round((Right - Left)* ScaleX),
                       Round((Bottom - Top)* ScaleY));
    end;
end;

procedure TForm1.Shape1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  showmessage('clock is clicked');
end;

procedure TForm1.Shape2MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  showmessage('time card is clicked');
end;

procedure TForm1.Shape3MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  showmessage('door knob is clicked');
end;

procedure TForm1.Shape4MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  showmessage('switch is clicked');
end;

end.

This code has also been compiled with D5 (C/S).

Ciao, Mike
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
samchanAuthor Commented:
You almost got it. Try these step: run the code, maximize the form by double-clicking the caption bar and then double-click to restore back to the original size. The hotspot will be resized and out of position. Hmm... maybe something to do with rounding errors.
0
LischkeCommented:
No problem here. I have even changed PixelsPerInch and Scaled to see what happens and all runs fine here. Btw: I took rounding errors into account when I developed the algo and came therefor up with the array of control bounds. With this array you can never get rounding errors, as there's no accumulation.

Sam, you should perhaps set a break point into the OnResize event (set its pass count to 2 or more to avoid getting it each time). The check which values are actually calculated...

Ciao, Mike
0
samchanAuthor Commented:
Hmm.. It really puzzled me. How come you do not have the problem. Anyway, I do notice that the rightmost of the image is not able to show totally (it is partially clipped) in the form when the form caption bar is double-clicked the second time to restore back to the original size after maximized. Well, could this be a bug in Windows 98 or Delphi.
0
LischkeCommented:
I'll check the program tomorrow on Win95 in my office as I have only WinNT at home (but I don't expect large differences). If all fails you should perhaps send me the entire project (I'll delete all afterwards, so don't care about legal issues).

Ciao, Mike
0
samchanAuthor Commented:
Whew.. found the reason why it is not working at time. The reason was because the Archors (akleft and aktop) for Shape4 was set. Everything working fine now. Thank you.
0
samchanAuthor Commented:
Thank you expert!
0
LischkeCommented:
:-) Finally...
0
samchanAuthor Commented:
However, FYI, the bound is still not very accurate when switch to different screen resolution although it works pretty well for the same screen resolutions.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.