How to let Parent component get click events of child components? - delphi

I have a FireMonkey application with multiple buttons on it (actually, rectangles). I want to have one procedure called on any click on the Form, besides the specific action of each button.
Since the HitTest of each child component is set to True, the parent's HitTest is automatically false.
So what is the right way to deal with this?
A silly workaround would be to assign this procedure to each button's OnClick event, but this will not make any sense when I have a Form with hundreds of buttons on it.

I found solution for vcl.
you can find component base of mouse position by this code:
var
ctrl : TWinControl;
begin
ctrl := FindVCLWindow(Mouse.CursorPos);
if ctrl.Name = '' then
ShowMessage(ctrl.Owner.Name);
For FireMonkey no result founded but You can get mouse position on form and analyze component base on result of that point and find name of it then proceed to your event base of that component.

A Simple Interceptor/Interposer Class of TRectangle did the job!
Thanks to everyone for their input.

Related

FMX: How can I get a forms mousemove events outside of the form

I have a form which I'm painting on (OnPaint). I give the user grips to click on and change the shape. However, I would like to allow the user to move past the edge of the form. How can I get the mouse moments when the mouse goes past the edge. I know this can be done because it works on a TrackBar. And while that is a Component and not a Form, I assume I can change the forms style or register something to make it act in a similar manor.
I plan on making the form background transparent so ideally, the same solution would allow me to get mousemoves over the transparent areas.
FMX uses the AutoCapture property for that. Then the mouse will be captured while the mouse button is down.
constructor TMyGrip.Create(AOwner: TComponent);
begin
inherited;
AutoCapture := True;
CanFocus := True;
end;
This does not work for a form. But you could add a TLayout on the form that fills the entire client.
FMX internally uses FWinService.SetCapture(Self); and FWinService.ReleaseCapture(Self); for this, and this accepts a form. I haven't tested this, but it could happen that you'll confuse the FMX framework quite a bit if you call the internal functions like that.

Is it possible to drag a TPanel to outside the TForm?

I'm wondering if it's possible to make something like this:
but without creating a second TForm.
I'm using Delphi 7, but if a newer version make it possible just tell me.
I've always thought the DockEx demo was over-complicated for learning the basics of docking.
The following is the simplest example I know of:
Add a TPanel to a blank form and set its DragKind property to dkDock, DragMode to dmAutomatic and its Align property to alTop.
Drop a TButton on the TPanel
Add the code below to the form:
Run the project and manually drag the panel off the form.
Click Button1.
The above shows how Delphi can undock a Panel (or TEdit, etc) without you
needing to create a second form to host it while undocked, like Remy said in a comment. The Button1 click-handler shows a way (admittedly imperfect) of re-docking the panel. Next:
Undock the panel again, but this time, click the Close button on its auto-created host.
Then, read the OLH and figure out a) how to get the now-hidden panel visible again and b) to re-position & re-align it on the form as it was prior to undocking,
type
TMyClass = TControl;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyClass(Panel1).ManualDock(Self, Nil, alNone);
end;

What control should I use to create this UI in Delphi Firemonkey

I am developing an application for mobile (android and ios) by Delphi xe5.
I am willing to create this UI:
I tried TListBox but image on left and right cant be set.
I tried TListView but same problem as TListBox
I tried TGrid with custom column, The problem of texts and images is solved but I can't create headers of each rows (it hasn't something like colspan)
What I need is to create a custom control and repeat it.
What is the best solution?
Any solution or guide line will be appreciated.
Solution
Thanks #Mike Sutton for answer, this is the result
The style here is so different from a standard TListBoxItem style that is probably makes sense to start from scratch, in which case the issues with accessing the default styles become immaterial.
Add a TStyleBook to your form.
Set the StyleBook property of the form to point to it.
Double click the icon to open the editor.
Drag a TLayout to the structure panel and drop it on the only item which will be there.
Set the StyleName property of the TLayout (e.g. ScoreListBoxItemStyle).
Drag/drop other components to build up the layout you want (remember TLayouts for 'hidden' positioning).
Set the StyleName property of any components you want reference from your code.
Subclass TLIstBoxItem to TScoreListBoxItem (if using the StyleName suggested above).
Add properties for your text, images etc.
In the setter methods for each of these, cache the data and call a method such as:
procedure SetFlag1;
var O: TFMXObject;
begin
O := FindStyleResource('flag1'); //StyleName of the item
if O is TImage then
TImage(O).Bitmap.Assign(FFlag1);
end;
Override the ApplyStyle method and call all of your methods that set the data in the style.
Now create your items in code:
Item := TScoreListBoxItem.Create(Self);
ListBox1.AddObject(Item);
Item.Flag1.LoadFromReource ...
...
Here's an idea that I don't have time to test:
Create a descendant of a TListBoxItem and in that add you two images as normal TImages. I'm pretty sure that a TListBoxItem can parent an object. You'll have to place the images on the listbox item where you want them. Then whenever you add an item to the listbox item just pass in your own descendant.
(If this doesn't work someone let me know and I'll delete this.)

Can Windows manage multiple focus points on a single control?

I'm building a custom control which is to have multiple focus points. For example, within 1 control, let's say there's 3 regions (could be defined as a rect on the canvas) which are to be able to have focus. Depending on which one has focus, I don't want the default Windows dotted line around it, but some special handling. I know nothing about how to even give 1 custom control its own focus.
The original project was a single TPanel with a few VCL controls on it, each of course being its own window, thus having its own focus. But now I'm putting it into a custom class of its own, which these 3 controls will no longer exist (they were only there in version 1 as a prototype) and I need to now somehow mimic the focus in these different regions.
I guess similar to something as simple as a TListBox, where certain items within that control get the focus instead of the entire control its self.
Here's a picture to help demonstrate what I'm making...
The one on the top is the original with the buttons. But the one on the bottom is the new one I'm building which is all custom drawn.
To elaborate, I'd like to see if Windows already has special handling for this type of scenario before I go and re-invent the wheel.
I'm not looking for the "Easiest" way to accomplish this. And I also do not want recommendations to revert back to how I had it before, because there's many reasons I don't want 1 control with multiple other controls within. I just need a yes or no, with an explanation.
More
I just realized the main concern is the use of the tab key. The control has to first get the focus, start the focus on whichever sub-item it's supposed to, then respond to tab on my command until it reaches the end, then pass the tabbing over to the next control. Then it also needs shift+tab as well. How on earth would I implement this tabbing? That's where I got stuck, but it just struck me that this is the main reason I'm asking.
About the handling of the TAB key - it should be something like this: you handle the WM_GETDLGCODE message to indicate that you want to proccess the TAB key, ie
TMyControl = ...
protected
procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
...
procedure TMyControl.WMGetDlgCode(var Msg: TMessage);
begin
inherited;
Msg.Result:= Msg.Result or DLGC_WANTTAB;
end;
and the in the overriden KeyDown method you decide what to do in response of it, something like
procedure TMyControl.KeyDown(var Key: Word; Shift: TShiftState);
begin
if(Key = VK_TAB)then begin
if(ssShift in Shift)then begin
if(first subcontrol is focused) set focus to previous control on parent
else set focus to previous child area
end else begin
if(last subcontrol is focused) set focus to next control on parent
else set focus to next child area
end;
end else inherited;
end;
No you can't get windows to recognize multiple points of keyboard focus inside the same window handle, since each control with a window handle either has, or does not have, keyboard focus. The "inner focus" between multiple controls is up to you to sort out.
As you already knew, the most simple way to accomplish this is to have multiple sub-controls with their own window-handles, which is what you said you are doing:
TMyThreeEditControls = class(TControl) // parent has no window handle!!!!
protected
FEdit1:TEdit;
FEdit2:TEdit;
FEdit3:TEdit;
...
end
In the situation above, the parent control is a TControl, it creates several sub controls, in my example above, all three have their own window handles, and thus Windows can display keyboard focus when you hit tab, and handle mouse focus as part of Windows's common controls library's functionality.
In short, the "composite" approach where you include sub-objects (other controls) in your main control, which you are trying to move away from is in fact, the only way to let Windows do most of the work.
On the other hand, what you might be looking for is not a way to have a control paint itself, but some code to make something look like it is focused, in your own custom painting routines, if that is what you are looking for, you should look into the VCL source code or this link on about.com for examples on how to tell Windows to draw a focus rectangle, etc. The about.com link is an imitation and does not use real windows code to draw focus in a Windows-theme aware way.
Update: it is possible that what you are also looking for is the way to determine if mouse co-ordinates are within a specified rectangle (the rectangle represents a button in your case) and if so, to draw a "hot state" for the button. There are more sub-tasks than this to accomplish if you wish to build a control yourself, I recommend you study existing controls such as TStringGrid and TCategoryButtons in the VCL source code, to see the MouseMove, MouseDown, and MouseUp handling code you will need to do the things you are trying to do. In particular, StringGrid source code is the way to see how the "tab key" can be used within a single control with a single window handle, because in that control the tab key can be used (if the right options are turned on) to navigate among all the cells inside the string grid, as if each one was a separate control, even though it is all one control.
Another way to achieve this is to use one edit box that you re-use for each region that you want to have "focused". This is essentially how Delphi's grids work.
When the user clicks in that area (or hits the tab key into your control) you set the edit controls text to the data in that area, set the edit controls bounds to the area and make it visible. When the user exits the control (by clicking out of it or tabbing) you hide the edit control. If you make your control accept the tab key, you can make it "edit" the next area when they hit tab and exit it when they are in the last area.
Then it's just house keeping to make sure you store the entered data in the right spot in your component.

Why doesn't OnUpdate trigger for invisible components [duplicate]

This question already has answers here:
How can I use an action to determine a control's visibility?
(3 answers)
Closed 8 years ago.
When I make a component invisible by setting the connected TAction to invisible, the onupdate event will not trigger anymore. To recreate, do the following.
Create a new VCL forms application
Drop a button, a checkbox and an actionlist on the form.
Create a new action, and connect the button to it.
Write the following code for the actions OnExecute and OnUpdate event:
procedure TForm1.Action1Execute(Sender: TObject);
begin
ShowMessage('Test');
end;
procedure TForm1.Action1Update(Sender: TObject);
begin
TAction(Sender).Enabled := not CheckBox1.Checked;
TAction(Sender).Visible := TAction(Sender).Enabled;
end;
Run the application. The button is visible, and works properly. Check the checkbox, and the button disappears. Uncheck the checkbox. The button doesn't appear. In fact, if you put a breakpoint in Action1Update, you'll never get to it. Why is this, and how do I fix it?
No need to fix this, it works as designed. Only visible controls need to update their state, so only actions whose linked controls are visible are updated. When you hide the button there's no more reason to update the action.
Have the OnUpdate only call a separate routine that does what is required. Then you can call that routine from other places. Action lists were designed for that.
I understand what you're trying to do, and it makes sense that you would want it to work that way. However, here's a workaround for the way it does work.
You can update other controls in an OnUpdate also. You're not limited to updating the control that receives the notification. So, in the action for the control that determines visibility, you can set the visibility of the other controls there. In your case, that's the checkbox:
Create a new action (Action2) and assign it to Checkbox1.
Then in the checkbox action's OnUpdate:
procedure TForm1.Action2Update(Sender: TObject);
begin
Button1.Visible := TAction(Sender).Checked;
end;
Be sure to assign an OnExecute to the checkbox as well. Something as simple as this is fine:
procedure TForm1.Action2Execute(Sender: TObject);
begin
TAction(Sender).Checked := not TAction(Sender).Checked;
end;
To me, this still makes logical sense. You'll be able to look in one spot to see all of the controls whose visibility relies on that checkbox being set.
You can override the InitiateAction method on the form. This will happen whenever the application goes idle, just as on OnUpdate event does for each action.

Resources