pyscripter / SynEdit

SynEdit is a syntax highlighting edit control, not based on the Windows common controls.
26 stars 11 forks source link

CompletionProposal window focus #52

Closed vincentparrett closed 1 year ago

vincentparrett commented 1 year ago

When the completion proposal form is show, it takes away the focus from the editor host window - so it looks like the application has lost focus. Neither VS or RS do this. This also (when themes are enabled) sometimes results in a white flash before the form paints. I tried to capture a video/gif of this but even with 60fps it didn't show it.

Of course the issue is that we want the form to accept key strokes, mouse clicks - but it os possible to do this without the form having focus. It would require some refactoring - and perhaps some tighter integration between the component and the editor.

FWIW - I did this the variable completion stuff in FinalBuilder - a bit more complicated there as it has to hook multiple controls on the form and they are not known at design time (many of the forms/frames are dynamically generated at runtime).

If there is interest in this I would like to have a crack at it.

The plan would be for the completion proposal component to tap into the editor key events - if the proposal form is showing then consume the events, otherwise pass them back to the control. The form would be shown without activation, and would handle the WM_MOUSEACTIVATE message, eg

procedure TFBVariableHintWindow.WMMouseActivate(var message: TWMMouseActivate);
begin
  message.Result := MA_NOACTIVATE;
end;

One unknown with this would be whether the scrollbar would play ball with this technique.

Any thoughts on the best approach to hook into the editor key events?

vincentparrett commented 1 year ago

Oh, just realised the TSynCompletionProposal already hooks the key events so could probably deal with it from there. Will give it a go.

pyscripter commented 1 year ago

From the source code:

      if not Form.Visible then
      begin
        //ShowWindow(Form.Handle, SW_SHOWNOACTIVATE);
        ShowWindow(Form.Handle, SW_SHOWNA);
        Form.Visible := True;
      end;
      Form.Invalidate;

SW_SHOWNOACTIVATE: Displays a window in its most recent size and position. This value is similar to SW_SHOWNORMAL, except that the window is not activated.

SW_SHOWNA: Displays the window in its current size and position. This value is similar to SW_SHOW, except that the window is not activated.

Not exactly sure what is the difference.

vincentparrett commented 1 year ago

Yeah I have been playing with those and I still can't see what the diff is. The trouble is Form.Visible causes activation anyway. Looks like it's going to be a lot more complicated than I thought.

Also, in the demo I don't see the focus change, but in my application I do - I have no idea why as I'm not doing anything different.

pyscripter commented 1 year ago

I compared to VS Code.

Otherwise they are not very different.

If we can make it so that the main form appears activated without any side-effects, I would be happy to incorporate it.

pyscripter commented 1 year ago

Looked at the demo again. You are right. The main form does not appear deactivated. In PyScripter the main form gets deactivated. Not sure what is the difference. If you find out please let me know.

vincentparrett commented 1 year ago

No closer to figuring out why the demo doesn't show the activation change.

My gut feeling is this needs to not be a form but a TCustomControl. That is how I implemented it in finalbuilder's variablesense feature and don't have any issues with focus etc. I'll come back to this in the future when I have more time available.

pyscripter commented 1 year ago

Changed WMActivate and that did the trick. The containing form is not really active, but shows as active. See https://learn.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate