How determine that a dragging operation has ended in FireMonkey? - delphi

I want to find out if a dragged control has been released outside any target.
One would think that OnDragEnd should be used, but that event doesn't work (they forgot to call the DragEnd procedure in the FMX.Types unit).
As an alternative, I tried OnMouseUp for the dragged control. Doesn't work. Doesn't get triggered when DragMode=dmAutomatic.
As a last resort, I tried to override the MouseUp procedure of the form itself (since all mouse events go through the form before being handed down to the respective control). Surprise: the MouseUp procedure does not get called when the control's DragMode=dmAutomatic.
Amazing how something this very simple is this extremely hard to achieve, but I'm hoping someone might have found a working solution.

It is possible to override the form's DragLeave method. This method is called for all objects that get dropped outside of a target area.

Related

Strange wm_keydown behaviour with THtmlEdit

This is a strange one: This is the situation
The application builds an edit form at run-time from an XML document. The UI/UX design is such that there are three levels of nested TTabControls and for the final (lowest) level the TTabItem is created at run-time. This has a TVertScrollBox which itself contains a column-grid control of mine and that (at last!) contains the THtmlEditor.
The problem is that when the controls are built and the content loaded, the THtmlEdit initially ignores keydown. It will respond to mouse events, the caret can be positioned. Switching to a different top-level tab, or away from the whole application and back, then cures the issue and the editor responds to keydown messages.
I have tried putting a breakpoint in procedure THtmlEditor.KeyDown() ... and then tracing back up the call-chain. There doesn't seem to be anything behaving differently. I've paid particular attention to input focus, explicitly calling Editor.SetFocus even though that is apparently already called ...
I have tried putting a conditional breakpoint in function TPlatformWin.HandleMessage: Boolean; ... and my only observation is that when in non-working mode, the call to DispatchMessage(Msg) doesn't arrive at the editor, or its parent form.
I have tried to build a MDC for this, replicating the structure outlined above but ... that always works!
What can I try next?
Has anyone seen this behaviour (and fixed it)?
EDIT:
The detail I didn't mention -- didn't think about it -- is that for the error condition to show, the control focused before the THmtlEditor is a TWebBrowser. If I set focus on a TEdit after TWebBrowser and then to THtmlEditor it seems to work.
Grrr!
This is only half an answer, but for future reference, this is what I've done:
It's a bodge :-(
I've put a TEdit on the main form and set it invisible. I've then added a handler to the MainForm OnFocusChanged which sets a boolean trap-flag to track if TWebBrowser has previously been focused.
For the instances of THtmlEditor I've added an OnClick handler which checks the trap-flag and makes the TEdit visible, calls SetFocus on it, re-hide the TEdit and return True so that the THtmlEditor can re-set focus to itself. The trap-flag is to avoid unnecessarily losing focus if the user is just clicking in the THtmlEditor.
The next step -- which I may never get to -- would be to trace through the focus code for TEdit. My suspicion is that it all relates ITextInput which TEdit supports and THtmlEditor doesn't.

How to close an FMX TFrame shown in a TPopup from event handlers inside the TFrame

I use a frame that is shown by calling TPopup.Popup(true); method. If the frame contains a button with the ModalResult property set (to, for example, mrOK) it closes automatically when clicked by the user. But I need to close the frame in an OnClick event of a TListBox in it.
Frame does not have Close method.
I would like to avoid using message posting to the parent form as it might cause future problems when the application is ported to Android as well as I would prefer not to declare the OnClick event handler for the Frame.ListBox in the parent Form because the frame might be shown by several different forms and it will worsen the quality of parent Form code making it heavy and difficult to read.
I would highly appreciate suggestions how to do this.
I found the following way out. I call
(GetParentComponent as TPopup).IsOpen:=false;
When you inherit from TPopUp like explained here (How to make my own dialog component from Firemonkey TPopUp?) then you can call ClosePopup when an event is trigerred in your frame.

Creating a forms editor in Delphi

My goal is to create a simple forms editor like the one that we find on Delphi IDE.
Right now the user can select and add the components making it parent of a TPanel that is the holder of the form. For simplicity, please consider also TPanel as the visual components added to the form.
I have 2 missing parts I want to find out ideas/code to help complete:
1 - how to move the created visual component? The same effect that in IDE for moving the visual component, for example Tpanel, around, chaning its top and left position
2 - how to draw that hooks for the component with focus on the form editor
3 - how to resize using the hooks
I only want the part related to handle the visual part. I am not generating DFM or anything like that.
Simply put your moving code needs to do this:
When the mouse goes down, check if the mouse position is over a control that can be dragged. If so, then set a variable named FDragControl to refer to that control. This code lives in an OnMouseDown event handler.
When the mouse moves, if FDragControl is not nil, move the control. This code lives in an OnMouseMove event handler.
When the mouse goes up, set FDragControl to nil.
That's pretty much all there is to it. The main nuance is that you must also remember the X, Y values of the mouse when the drag commenced. So in your OnMouseDown handler you write:
FStartMousePos := Point(X, Y);
FStartDragControlPos := Point(FDragControl.Left, FDragControl.Top);
And then in the OnMouseMove your position code reads:
FDragControl.Left := FStartDragControlPos.X + (X-FStartX);
FDragControl.Top := FStartDragControlPos.Y + (Y-FStartY);
You will also need to capture the mouse when you start dragging.
The resizing code is similar. Again, you need to decide in the OnMouseDown that you are resizing rather than dragging, but the code still involves handling mouse down, move and up events.
As for painting, you need to force a repaint whenever one of your event handlers changes a property that will influence the visual appearance of your form. You can use the value of FDragControl to decide whether or not to use special drawing of your control and indicate that it is being dragged. And likewise for resizing.
I've not coded up a full working implementation since your question is high level and conceptual. The implementation is down to you.
// I have made this an answer as I have just read your latest update which really should have been made as an edit to your original question but, anyway.
You can download the Cindy Components Pack and use the cyResizer Component which will do pretty much everything you need and is very customisable as well.
You can download it from here: http://sourceforge.net/projects/tcycomponents/
Searching more for an answer I could find these articles:
How to Move and Resize Controls at Run Time
http://delphi.about.com/library/weekly/aa102505a.htm
How to Add Size Handles to Controls being Resized at Run-Time
http://delphi.about.com/library/weekly/aa110105a.htm
Pretty much with all the information to complete this task with source code example.
These articles show how to implement and use a TMover class. I have done it and work correctly.
I have also downloaded the TcyComponents Pack and used the TcyResizer. It is a full featured form editor with pretty much everything that is required for a Delphi like forms editor. I recommend. It comes with source code and works fine with XE2 version.

Is there a workaround for something like "Form1.MousePreview:=true"?

I have two 100% overlapping panels with different contents on a form.
The first (static display of information) should be visible by default, but the other (user interaction) should replace it if the user moves the mouse near the two - and if the mouse moves away, it should switch back.
Something like this:
if (*the mouse is near*) then
begin
Panel1.Hide;
Panel2.Show;
end
else
begin
Panel2.Hide;
Panel1.Show;
end;
My problem is: where to capture mouse movement?
Each component has its own OnMouseMove handler - of course I could forward each of them to the forms handler, but I'd rather have something a bit more elegant (and easier to maintain).
The perfect solution would be something like Form1.MousePreview := true;.
Another solution would be assigning a generic handler that translates coordinates and calls the forms handler; The assigning could be done in FormCreate.
But that's not as easy as it seems, because one TImage already has its own mouse event handlers.
I have tried OnMouseEnter and OnMouseLeave of the two panels but it didn't work; #1 disappeared, but #2 did not appear. I guess that's because if the mouse leaves Panel2, it should disappear - but it also leaves it if it enters a button on it.
That's why I'd like to use a coordinate based approach to make the check more reliable.
Maybe the two panel method is completely wrong?
Update: Yes, it is, as Remy said.
I am now using a TJVPageList because a TPageControl has visual tabs.
The OnMouseEnter handler of the PageList sets one page, the OnMouseLeave sets the other; But once the mouse moves over the PageList, both pages start flickering.
I have tried adding the event handlers to each page, too, but that made no difference.
Should I check all OnMouseEnter / Leave events to filter out the PageList, the two pages and all components sitting on the pages?
Another update: I am using a TPageControl now, but the behaviour is similar.
It doesn't flicker, but if I move the mouse ontop the TPageControl, no TTabSheet is displayed at all.
Only if I press down the left mousebutton, the UI sheet is displayed. The other sheet is displayed normally if I move away the mouse. (The TJVPageList displays the UI page if I press the left mousebutton, too.)
I have used the mouse event handlers (enter/leave) of the TPageControl and both TTabSheets.
Update 3:
Done it.
The static Panel / TabSheet / JvStandardPage (#1) must not trigger the OnMouseLeave handler.
What you describe might be better served using a single TPageControl instead of two TPanel controls. Use the TPageControl's own OnMouseEnter/Leave events (or intercept the CM_MOUSEENTER/LEAVE messages) to switch the TPageControl.ActivePage as needed.

How to Detect that the Mouse is unmoved and button still pressed?

In Delphi, I've added a Scrollbar component (oriented vertical) to the right side of my form.
I've added a Scrollbar OnChange event so I can change the view of the form and the position of the scrollbar thumb when the user clicks on the UpArrow or DownArrow button with his mouse, and this works fine.
But the OnChange event only seems to get triggered when the mouse button is initially pressed on the arrow.
I notice all scrollbar controls repeat the command and continue scrolling while the mouse remains pressed on the arrow, and I'd like to implement this behavior.
So how can I easily detect if the user has not moved the mouse and continues to press the mouse button while the mouse remains over the arrow?
Conclusion. Somehow something in the scrollbar in my project got corrupted. After I deleted the ScrollBar, and added it again, the problem vanished.
This is one of those tricky ones that took me a lot of time to solve. Thanks for your help. I'm closing this question.
Use the OnScroll event.
The following code adds 'xxx' to a memo as long as the mouse is held down on the scrollbar arrow button. Tested with Delphi 6.
procedure TForm1.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
var ScrollPos: Integer);
begin
Memo1.Lines.Add( 'xxx' );
end;
The usual way to handle auto-repeating is to enable a TTimer and check in the OnTimer() event handler whether the action needs to be performed again, and to deactivate the timer if not. If you need sample code, I seem to remember that the SynEdit control used a similar technique for autoscrolling in drag and drop operations.
If a component does not encapsulate the behaviour you are looking for and you can't easily simulate the behaviour with the methods available you should really subclass the closest component that does most of what you need and add the behaviours that are missing.
I know that some extra work is involved but it really is the better way to go. Now with Delphi, I seem to recall that subclassed components needed a bit of extra work as well to be able to be used from the IDE for form design, maybe this has changed since version 7.

Resources