How to create “No Activate” form in Firemonkey

后端 未结 2 1423
温柔的废话
温柔的废话 2021-01-30 11:56

In XCode by adding these methods to your NSView subclass can prevent the window from becoming active when clicking on it:

-         


        
相关标签:
2条回答
  • 2021-01-30 12:34

    You can turn off the forms mouse handling to prevent it being focused. Assuming your form is called myform:

    uses fmx.platform.mac, macapi.appkit;
    .
    .
    Var nswin:nswindow;
    .
    .  
    NSWin:= NSWindow(NSWindowFromObjC(FmxHandleToObjC(myform.Handle))); { get the NSWindow }
    NSWin.setIgnoresMouseEvents(true);                                 { ignore mouse events }
    NSWin.setAcceptsMouseMovedEvents(false);
    

    There is a slight problem in that it doesn't stop a right mouse click. If that's a problem, you will have to respond to the mousedown event in the form and call the main forms mousedown so it doesn't lose the mouse event. Since the right mouse down will then capture the mouse events, you also then need to respond to mouse move and mouse up events too - forwarding them to your main form. Although it captures the mouse on right click, it will still not focus the form.

    Dave Peters DP Software

    0 讨论(0)
  • 2021-01-30 12:42

    It is possible using NSPanel with NSNonactivatingPanelMask flag. The NSView of fmx form should become child of NSPanel. I have written a helper class which works for both Windows and Mac platforms (Works on XE4):

    unit NoActivateForm;
    
    interface
    
    uses Fmx.Forms, Fmx.Types
    {$IFDEF POSIX}
        , Macapi.AppKit
    {$ENDIF}
        ;
    
    type TNoActivateForm = class
    private
        form: TForm;
    {$IFDEF POSIX}
        panel: NSPanel;
        timer: TTimer;  // for simulating mouse hover event
    {$ENDIF}
        procedure SetPosition(const x, y: Integer);
        procedure GetPosition(var x, y: Integer);
        procedure SetDimensions(const width, height: Integer);
        procedure SetLeft(const Value: Integer);
        procedure SetTop(const Value: Integer);
        procedure SetHeight(const Value: Integer);
        procedure SetWidth(const Value: Integer);
        procedure SetVisible(const Value: Boolean);
        function GetLeft: Integer;
        function GetTop: Integer;
        function GetHeight: Integer;
        function GetWidth: Integer;
        function GetVisible: Boolean;
    {$IFDEF POSIX}
        procedure OnTimer(Sender: TObject);
    {$ENDIF}
    public
        constructor Create(AForm: TForm);
        destructor Destroy; override;
        property Left: Integer read GetLeft write SetLeft;
        property Top: Integer read GetTop write SetTop;
        property Height: Integer read GetHeight write SetHeight;
        property Width: Integer read GetWidth write SetWidth;
        property Visible: Boolean read GetVisible write SetVisible;
    end;
    
    implementation
    uses
        Classes, System.Types
    {$IFDEF MSWINDOWS}
        , Winapi.Windows;
    {$ELSE}
        , Macapi.CocoaTypes, FMX.Platform.Mac, Macapi.CoreGraphics, Macapi.CoreFoundation;
    {$ENDIF}
    
    constructor TNoActivateForm.Create(AForm: TForm);
    {$IFDEF POSIX}
    var
        rect: NSRect;
        bounds: CGRect;
        window: NSWindow;
        style: integer;
        panelCount: integer;
    begin
        form := AForm;
        form.Visible := false;
        bounds := CGDisplayBounds(CGMainDisplayID);
        rect := MakeNSRect(form.Left, bounds.size.height - form.Top - form.Height,
            form.ClientWidth, form.ClientHeight);
        style := NSNonactivatingPanelMask;
        style := style or NSHUDWindowMask;
        panel := TNSPanel.Wrap(
            TNSPanel.Alloc.initWithContentRect(rect, style, NSBackingStoreBuffered,
            true));
        panel.setFloatingPanel(true);
        //panel.setHasShadow(false); optional
        window := WindowHandleToPlatform(form.Handle).Wnd;
    
        panel.setContentView(TNSView.Wrap(window.contentView));
        TNSView.Wrap(window.contentView).retain;
    
        timer := TTimer.Create(form.Owner);
        timer.OnTimer := OnTimer;
        timer.Interval := 50;
    end;
    {$ELSE}
    var hWin: HWND;
    begin
        form := AForm;
        form.TopMost := true;
        hWin := FindWindow(PWideChar('FM' + form.ClassName), PWideChar(form.Caption));
        if hWin <> 0 then
            SetWindowLong(hWin, GWL_EXSTYLE,
                GetWindowLong(hWin, GWL_EXSTYLE) or WS_EX_NOACTIVATE);
    end;
    {$ENDIF}
    
    destructor TNoActivateForm.Destroy;
    {$IFDEF POSIX}
    begin
        panel.release;
    end;
    {$ELSE}
    begin
    end;
    {$ENDIF}
    
    procedure TNoActivateForm.SetPosition(const x, y: Integer);
    {$IFDEF POSIX}
    var point: NSPoint;
        screen: CGRect;
    begin
        screen := CGDisplayBounds(CGMainDisplayID);
        point.x := x;
        point.y := round(screen.size.height) - y - form.height;
        panel.setFrameOrigin(point);
    end;
    {$ELSE}
    begin
        form.Left := x;
        form.Top := y;
    end;
    {$ENDIF}
    
    procedure TNoActivateForm.GetPosition(var x, y: Integer);
    {$IFDEF POSIX}
    var screen: CGRect;
    begin
        screen := CGDisplayBounds(CGMainDisplayID);
        x := round(panel.frame.origin.x);
        y := round(screen.size.height - panel.frame.origin.y - panel.frame.size.height);
    end;
    {$ELSE}
    begin
        x := form.Left;
        y := form.Top;
    end;
    {$ENDIF}
    
    procedure TNoActivateForm.SetDimensions(const width, height: Integer);
    {$IFDEF POSIX}
    var size: NSSize;
    begin
        size.width := width;
        size.height := height;
        panel.setContentSize(size);
    end;
    {$ELSE}
    begin
        form.width := width;
        form.height := height;
    end;
    {$ENDIF}
    
    procedure TNoActivateForm.SetLeft(const Value: Integer);
    begin
        SetPosition(Value, Top);
    end;
    
    procedure TNoActivateForm.SetTop(const Value: Integer);
    begin
        SetPosition(Left, Value);
    end;
    
    procedure TNoActivateForm.SetHeight(const Value: Integer);
    begin
        SetDimensions(Width, Value);
    end;
    
    procedure TNoActivateForm.SetWidth(const Value: Integer);
    begin
        SetDimensions(Value, Height);
    end;
    
    procedure TNoActivateForm.SetVisible(const Value: Boolean);
    begin
    {$IFDEF POSIX}
        panel.setIsVisible(Value);
    {$ELSE}
        form.visible := Value;
    {$ENDIF}
    end;
    
    function TNoActivateForm.GetLeft: Integer;
    var x, y: Integer;
    begin
        GetPosition(x, y);
        result := x;
    end;
    
    function TNoActivateForm.GetTop: Integer;
    var x, y: Integer;
    begin
        GetPosition(x, y);
        result := y;
    end;
    
    function TNoActivateForm.GetHeight: Integer;
    begin
    {$IFDEF POSIX}
        result := round(panel.frame.size.height);
    {$ELSE}
        result := form.Height;
    {$ENDIF}
    end;
    
    function TNoActivateForm.GetWidth: Integer;
    begin
    {$IFDEF POSIX}
        result := round(panel.frame.size.width);
    {$ELSE}
        result := form.Width;
    {$ENDIF}
    end;
    
    function TNoActivateForm.GetVisible: Boolean;
    begin
    {$IFDEF POSIX}
        result := panel.isVisible();
    {$ELSE}
        result := form.visible;
    {$ENDIF}
    end;
    
    {$IFDEF POSIX}
    procedure TNoActivateForm.OnTimer(Sender: TObject);
    var event: CGEventRef;
        point: CGPoint;
        form_rect: TRectF;
        client_point, mouse_loc: TPointF;
        shift: TShiftState;
    begin
        event := CGEventCreate(nil);
        point := CGEventGetLocation(event);
        CFRelease(event);
        mouse_loc.SetLocation(point.x, point.y);
        if Visible = true then
        begin
            form_rect := RectF(0, 0, form.Width, form.Height);
            client_point.X := mouse_loc.X - Left;
            client_point.Y := mouse_loc.y - Top;
            if PtInRect(form_rect, client_point) then
                form.MouseMove(shift, client_point.x, client_point.y)
            else
                form.MouseLeave();
        end;
    end;
    {$ENDIF}
    
    end.
    

    Usage of above unit:

    TNoActivateForm *naKeyboard; // global scope    
    void __fastcall TfrmKeyboard::TfrmKeyboard(TObject *Sender)
    {
        naKeyboard = new TNoActivateForm(frmKeyboard); // frmKeyboard is a normal fmx form
        naKeyboard->Visible = true;
    }
    

    If frmKeyboard is your Main Form then do not put above code in form constructor, It is recommended to put it in OnShow.

    enter image description here

    Note: WindowHandleToPlatform doesn't seem to exist in XE3 so that line can be replaced with

    window := NSWindow(NSWindowFromObjC(FmxHandleToObjC(Form.Handle)));
    
    0 讨论(0)
提交回复
热议问题