How to create “No Activate” form in Firemonkey

后端 未结 2 1469
温柔的废话
温柔的废话 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: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)));
    

提交回复
热议问题