0

We have a Delphi app (XE8) that I recently found out runs almost perfectly on wine (OSX 10.6), except for one thing. Say the user selects a menu option on the main form that opens another form (like 'File|New ...'). The new form shows, and partially covers the main form. The problem is there is no way to get the first form to come back to the top. Clicking on the main form does not bring it back on top of the child form like it does in windows. If two child forms are showing, again, clicking on one child does not bring it to the foreground--it seems to be focusable (you can drag it if you can get a hold of it), but whatever part of it is covered by another form remains covered

This behavior occurs both when we use the winebottler and when we use the crossover app.

Are we missing some library in the bottling process? Or maybe just completely misunderstanding how 'windows' works on OSX? Is our windows app poorly designed? Do we need to redesign the app to just use one form (and launch everything using tabs instead of forms)?

Edit: Here is a sample delphi project that illustrates the issue

program ProjectM;

uses
  Vcl.Forms,
  Unit1m in 'Unit1m.pas' {fmain},
  Unit2m in 'Unit2m.pas' {form2m},

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := False;
  Application.CreateForm(Tfmain, fmain);

  Application.CreateForm(Tform2m, form2m);

  Application.Run;
end.

In unit1 we have a button to launch the second form

procedure Tfmain.Button1Click(Sender: TObject);
begin
  form2m.show
end;

Run the .exe, the main form shows; press the button: The new form overlays the main form.

In windows, click the title bar of the main form--it comes back to the foreground.

In winebottler, click the title bar of the main form, it does not come back up; even tho it looks like the title bar gets focused, the rest of the form is still hidden--see screenshot

enter image description here

Johan
  • 74,508
  • 24
  • 191
  • 319
richard
  • 13
  • 1
  • 7
  • Sounds like standard Windows ownership. Owned form always above owner. Normal behaviour. – David Heffernan Mar 25 '16 at 23:12
  • @David: We have mainformOnTaskbar:= False; Clicking a background form always brings it to the top (as described in (http://stackoverflow.com/questions/5393666/make-2-forms-able-to-overlap-each-other) – richard Mar 26 '16 at 15:35
  • I don't know what your code is. There's no [mcve]. – David Heffernan Mar 26 '16 at 15:50
  • @David: I added a simple project to demonstrate. – richard Mar 26 '16 at 22:25
  • That's normal behaviour. Form 2m is owned by form m. Hence 2m is always above m in the z-order. Read my answer from the question to which you linked. I suspect the second half concerning MainFormOnTaskbar is now out of date due to VCL changes. – David Heffernan Mar 26 '16 at 22:29
  • My advice is that you set the window owner explicitly as per the first part of that answer – David Heffernan Mar 26 '16 at 22:32
  • Your project is incomplete. You don't show the crucial line where the `form2m` gets created. – Johan Mar 27 '16 at 15:35
  • @Johan: Corrected, thanks – richard Mar 27 '16 at 15:46
  • Note that this is not a wine/osx/windows issue. It applies to any OS in Delphi. – Johan Mar 27 '16 at 15:53
  • but the question states in Windows it works as intended... – Sertac Akyuz Mar 27 '16 at 15:54
  • @Johan: I changed the creation code as you suggested, but I get the same result: on the mac, the title bar of the main form gets focus (as you can see by the colored circles in the screenshot), but the form itself remains beneath the child – richard Mar 27 '16 at 16:17
  • 1
    @richard - Use GetParent to find out the window owner of second form (if you can't run spy++ or similar in the environment) and compare it to handles of the main form and the application handle. I wonder if you have tried overriding CreateParams as David suggested. – Sertac Akyuz Mar 27 '16 at 16:37
  • @SertacAkyuz, I missed that part. That's really weird. – Johan Mar 27 '16 at 18:24
  • @Sertac: yes, i did try Params.WndParent := 0; same result (I was not able to step thru createParams in the debugger (is that normal?)--thus, not sure that code executed). I will work on your suggestion later, thanks – richard Mar 27 '16 at 22:37
  • Sounds like you implemented it wrongly. – David Heffernan Mar 28 '16 at 07:00
  • @David: true, I forgot to add 'override' – richard Mar 29 '16 at 13:00
  • Setting the child form's parent to 0 seems to resolve the issue in the test project! – richard Mar 29 '16 at 13:10

1 Answers1

1

Ownership
The problem is that your second form is owned by the mainform.
Owned windows always appear on top of their owners.

Set the ownership manually
The solution is to change the ownership of the second form.

First you need to disable the automatic creation of the second form and do everything manually:

In the menu choose:
Project -> Options -> Forms Select your form2 and move it from the auto create to the available forms.

enter image description here

Now you need to create the form:

procedure Tfmain.FormCreate(Sender: TObject);
begin
  Form2:= TForm2.Create(Application); 
end;

Wine naughtyness
You can force the WndParent of the new form to be 0 by interposing a CreateParams like so:

unit Unit2m;
interface

type 
  TForm2Interposer = class(TForm)
    procedure CreateParams(var Params: TCreateParams); override;
  end;

  TForm2m = class(TForm2Interposer)
  ....
implementation

procedure TForm2Interposer.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.WndParent := 0;
end;

Showing the form
Note that the owner is Application not your main form. Application is a hidden form that Delphi uses for bookkeeping. Both your mainform and form2 now have Application as their owner. And they have exactly equal status as far as Z-order is concerned.

procedure Tfmain.Button1Click(Sender: TObject);
begin
  form2m.show
end;

No cleanup needed
You've manually created the form, so your instinct would be to manually destroy the form, however because Application is the owner of the form. It will take care of the cleanup, so you don't have to.

procedure Tfmain.FormDestroy(Sender: TObject);
begin
  //Form2.Free;  <<-- The owner does the cleanup.
end;

Now whatever window you select will come out on top.

Remarks
This has nothing to do with MainFormOnTaskbar it is simple Windows behavior: an owned window always appears on top of its owner.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Johan
  • 74,508
  • 24
  • 191
  • 319
  • MainFormOnTaskbar is closely related to form's window ownership. As can be seen in TCustomForm.CreateParams, when it is false, forms are owned by the application window. Auto creation, manual creation, component ownership (Create(Application) vs. Create(Self) vs. Create(nil) does not have an effect on this. The code in the question leads to two forms window owned by the application window. – Sertac Akyuz Mar 27 '16 at 21:00
  • @Johan: the createParams override did the trick (once I implemented it correctly--thanks, David). Just want to note that the child form is still being auto-created in the .dpr – richard Mar 29 '16 at 13:22
  • you can edit the dpr to fix that issue. After you're done editing you make the dpr readonly (in explorer) so that the IDE does not alter it again. – Johan Mar 29 '16 at 16:03
  • @Johan: any chance of explaining why you inserted a class for the createParams override instead of just entering it in 'protected' section? – richard Mar 29 '16 at 18:26
  • @richard No reason really. – Johan Mar 29 '16 at 18:30