Lazarus find control under cursor - controls

I am using the following code from this posting.
Code from Checked Answer
I need to get the Control (Label.Caption) under the mouse cursor from one of several TLabel and it worked fine when the Label was on the Main From. I put the Labels on a Panel on the Main form and now this only finds the Panel. I only want this to work on a select few of the Labels of the many that are on the Panel.
I tried changing the Z-Order for the Labels as "Bring To Front" but it made no difference, still got the Panel. How can I again find a Label under the cursor now that they are on the Panel?
Lazarus does not appear to have FindVCLWindow or ObjectAtPoint.
procedure TForm1.Button1Click(Sender: TObject);
var
ctrl : TControl;
point : TPoint;
begin
point := Mouse.CursorPos; // Mouse pos at screen
Dec(point.X, Left); // Adjust for window.
Dec(point.Y, Top);
Dec(point.Y, GetSystemMetrics(SM_CYCAPTION)); // Adjust to client area.
ctrl := ControlAtPos(point, True, True, True);
// I added the following
tStr:=ctrl.Name; // DEBUG: This now shows "Panel2"
aStr:=(ctrl as TLabel).Caption; // This used to work
end;

Try:
procedure TForm1.Button1Click(Sender: TObject);
var
ctrl: TControl;
pt: TPoint;
begin
pt := ScreenToClient(Mouse.CursorPos);
ctrl := ControlAtPos(pt, [capfRecursive, capfAllowWinControls]);
if Assigned(ctrl) then
Caption := ctrl.Name
else
Caption := Format('%d, %d', [pt.x, pt.y]);
end;

Related

Why no window is being shown despite no error or warnings

I am trying following code for a GUI to show 2 identical windows. I am using show rather than showmodal:
program RnTFormclass;
{$mode delphi}
uses
//cthreads, // for linux only.
Interfaces, Forms, StdCtrls;
type
RnTForm = class(TForm)
private
wnd: TForm;
btn: TButton;
public
constructor create;
procedure showit;
end;
constructor RnTForm.create;
begin
//Application.Initialize; //removed.
wnd := TForm.Create(Application);
with wnd do begin
Height := 300;
Width := 400;
Position:= poDesktopCenter;
Caption := 'LAZARUS WND';
end;
btn := TButton.Create(wnd);
with btn do begin
SetBounds(0, 0, 100, 50);
Caption := 'Click Me';
Parent := wnd;
end;
end;
procedure RnTForm.showit;
begin
wnd.Show;
end;
var
myform1, myform2: RnTForm;
begin
// create windows:
myform1 := RnTForm.Create;
myform2 := RnTForm.Create;
// show windows:
myform1.showit;
myform2.showit;
end.
I want two identical windows to show/open up. Though the program runs without any error or warning, not even one window is shown.
The program just terminates.
Where is the problem and how can it be solved? Thanks for your help.
Edit: As pointed out in the comments, Application.initialize is being called twice and not run. I have commented out Application.initialize and the code still does not open up any of the window. (It does open windows one by one if show is replaced by showModal).
Main question is how to keep window open after show?
Taking suggestions from comments, I got it working by following main method:
begin
Application.Initialize;
// create windows:
myform1 := RnTForm.Create;
myform2 := RnTForm.Create;
// show windows:
myform1.showit;
myform2.showit;
Application.run;
end.
Now both the windows appear and I can click and work on any of them.
However, on closing both the windows, the program still keeps running in background. An exit button with its click function needs to be added.

Set position of InfoTip

My TListView control has ShowHints enabled and handles the OnInfoTip event. The message in the popup InfoTip box is set in the OnInfoTip handler. However, the position of the popup InfoTip box is relative to the position of the mouse when hovering over an item in the list. There doesn't appear to be a way to customise the position.
Is it possible set the position of the hint popup, for example in a specific area of the TListView or even elsewhere on the form outside the bounds of the TListView control? Ideally, I'd like to display the hint popup in such a way to minimise (or eliminate) obscuring any other item in the TListView.
First you have to expose the CMHintShow of the TListView as following:
type
TListView = class(Vcl.ComCtrls.TListView)
private
FPos: TPoint;
protected
procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
published
property MyPos: TPoint read FPos write FPos;
end;
TfrmMain = class(TForm)
...
ListView1: TListView;
Then at the OnInfoTip event you set the desired Position. At my example, I get the coords of the TopLeft Corner of a ScrollBox (sbxFilter - which is located under the TlistView) and pass the Coords to the TListView property MyPos.
procedure TfrmMain.ListView1InfoTip(Sender: TObject; Item: TListItem; var InfoTip: string);
var
p: TPoint;
begin
InfoTip := 'Test';
p := sbxFilter.ClientToScreen(point(0, 0));
ListView1.MyPos := p;
end;
{ TListView }
procedure TListView.CMHintShow(var Message: TCMHintShow);
begin
inherited;
Message.HintInfo.HintPos := FPos;
end;
It is possible to display the hint, that you define in the OnInfoTip() event in e.g. a StatusPanel of a StatusBar (at the bottom of the form).
For example:
procedure TForm1.ListView1InfoTip(Sender: TObject; Item: TListItem;
var InfoTip: string);
begin
InfoTip := '';
StatusBar1.Panels[0].Text := 'ListView Infotip, Item '+IntToStr(Item.Index);
end;

How do I create a window without any frame regardless of user settings?

I need to write an application that displays two different pictures in two instances of the application. These pictures must look as if they were put side by side on the canvas of the same window but for internal reasons it must be two different applications not a single one. Is there any way to turn off the window frame regardless of what the user's Windows settings are? I still want to retain the title bar and the close/minimize/maximize buttons.
Bonus points if the two (or multiple) windows look and react like a single one to the user.
A Delphi example would be nice but I can probably do with a hint on which flags or whatever to set using Win32 API (no dotNET please).
Since windows with title bars always have borders, your next option is to make a borderless window and then paint a title bar at the top of the window yourself. That means handling mouse messages, too. Start with wm_NCHitTest. To make a borderless window, override your form's CreateParams method and set the Style field so there's no border.
This creates a Form without side or bottom borders:
type
TForm1 = class(TForm)
private
FBorderWidth: Integer;
FTitleHeight: Integer;
procedure AppRestored(Sender: TObject);
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
protected
procedure Resize; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.AppRestored(Sender: TObject);
begin
Repaint;
end;
procedure TForm1.Resize;
begin
inherited Resize;
if FBorderWidth = 0 then
begin
FBorderWidth := (Width - ClientWidth) div 2;
FTitleHeight := Height - ClientHeight - FBorderWidth;
Application.OnRestore := AppRestored;
end;
Invalidate;
end;
procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
inherited;
with Message.CalcSize_Params^ do
begin
Dec(rgrc[0].Left, FBorderWidth);
Inc(rgrc[0].Right, FBorderWidth);
Inc(rgrc[0].Bottom, FBorderWidth);
end;
end;
procedure TForm1.WMNCPaint(var Message: TWMNCPaint);
begin
DeleteObject(Message.RGN);
Message.RGN := CreateRectRgn(Left, Top, Left + Width, Top + FTitleHeight);
inherited;
end;

Using FindVCLWindow to call WinHelp32 (WinXP Pro SP3 32bit) in Delphi

what is wrong there?
procedure TForm1.VCLHelpClick(Sender: TObject);
var Ctrl : TWinControl;
begin
Ctrl := FindVCLWindow(Mouse.CursorPos);
if Ctrl <> nil then
if Form2.Cursor = crHelp then begin
if Ctrl = CreatorEdit then Application.HelpCommand(HELP_CONTEXT,001);
if Ctrl = EditorEdit then Application.HelpCommand(HELP_CONTEXT,002);
if Ctrl = UpdaterEdit then Application.HelpCommand(HELP_CONTEXT,003);
if Ctrl = IdeaEdit then Application.HelpCommand(HELP_CONTEXT,004);
if Ctrl = PorterEdit then Application.HelpCommand(HELP_CONTEXT,005);
end;
end;
The idea is simple - i have form border icons for Help button and when i click it, cursors changes to crHelp. If i click under control for any of IFs, it invokes Help System and Opens associated help file with context from command. But it doesnt work ... Is this because I have not added support for KLink / ELinks in Help file itself?
For help authoring and developing I am using ShalomHelpMaker Software.
Have you tried debugging the code? And can you tell us what part went wrong.
Besides, why don't you use the helpcontext like:
procedure TForm1.VCLHelpClick(Sender: TObject);
var Ctrl : TWinControl;
begin
if Form2.Cursor <> crHelp then // Are you sure this is Form2???
Exit;
Ctrl := FindVCLWindow(Mouse.CursorPos);
if Ctrl = nil then Exit;
Application.HelpCommand(HELP_CONTEXT, Ctrl.HelpoContext);
end;
Looks like FindVCLControl does some other things. But the following code works:
procedure TForm1.Button1Click(Sender: TObject);
var
ctrl : TControl;
point : TPoint;
begin
point := Mouse.CursorPos; // Mouse pos at screen
Dec(point.X, Left); // Adjust for window.
Dec(point.Y, Top);
Dec(point.Y, GetSystemMetrics(SM_CYCAPTION)); // Adjust to client area.
ctrl := ControlAtPos(point, True, True, True);
// Do something with the control
end;
You probably need some more tweaking, but this works to get the control of a window from the position.
Working code:
procedure TForm1.VCLHelpClick(Sender: TObject);
var WCtrl : TWinControl;
begin
WCtrl := FindVCLWindow(Mouse.CursorPos);
if WCtrl <> nil then
Application.HelpCommand(HELP_CONTEXT, wCtrl.HelpContext);
end;
P.S. all previous code probobly was ok too, but i rechecked my event handlers and found that in one tlabel it was missing ( althought when I clicked to the ones that had onclick, it did not work). Plus ... problem probobly was the faulty cursor check.
Ok, thanks for support, guys!

Controlling where (x,y) of a newly opened window in Delphi 2006

I'm trying to control the coordinates of where my program opens a new window because currently they're opening ontop of each other. Does anyone have a working example of how to do this?
You can always set the .Top and .Left properties manually, like this:
procedure TForm1.Button1Click(Sender: TObject);
var
frm : TForm;
begin
frm := TForm.Create(Self);
frm.Left := 100; //replace with some integer variable
frm.Top := 100; //replace with some integer variable
frm.Show;
end;
However, Windows has a default window placement algorithm that tries to keep the title bars of each window visible. On my computer, repeated clicks to this Button1 procedure give nicely stacked windows:
procedure TForm1.Button1Click(Sender: TObject);
var
frm : TForm;
begin
frm := TForm.Create(Self);
frm.Show;
end;
Also, don't forget that you can use the built-in set of TPosition locations:
procedure TForm1.Button1Click(Sender: TObject);
var
frm : TForm;
begin
frm := TForm.Create(Self);
frm.Position := poOwnerFormCenter;
{
Other possible values:
TPosition = (poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly,
poScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter);
//}
frm.Show;
end;
This type of functionality has been explained for C# in another question on SO.
Also, for Delphi, check out Understanding and Using Windows Callback Functions in Delphi which describes getting handles for windows that are currently open. And see Shake a window (form) from Delphi code which describes how to move a window once you've got its handle.

Resources