So erstellen Sie in Firemonkey das Formular "Keine Aktivierung"

147

Wenn Sie in XCode diese Methoden zu Ihrer NSView-Unterklasse hinzufügen, kann verhindert werden, dass das Fenster beim Klicken aktiv wird:

- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent )theEvent {
    return YES;
}
- (BOOL)acceptsFirstMouse:(NSEvent )theEvent {
    return YES; 
}
- (void)mouseDown:(NSEvent )theEvent {
    [[[NSApp]] preventWindowOrdering]; 
}

In der Windows-Plattform Dies geschieht mit diesem einfachen Code:

HWND hWnd = FindWindowW((String("FM") + fmxForm->ClassName()).c_str(), 
    fmxForm->Caption.c_str());

SetWindowLong(hWnd, GWL_EXSTYLE,
    GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_NOACTIVATE);

Wie kann ich NSView unterordnen, um zu verhindern, dass mein FMX TForm beim Klicken aktiv wird?

Wie kann ich in firemonkey das Formular " No Activate " erstellen ?

mh taqia
quelle
3
Sie sind sich nicht sicher, ob es auch für Firemonkey gilt oder ob es Ihre Frage richtig beantwortet, aber Sie sollten sich dieses Beispiel ansehen
TildalWave
Vielen Dank, aber es ist nur für Windows und der einfachere Weg ist meine Lösung, die oben von "SetWindowLong" beschrieben wurde. Die Frage betrifft MacOS.
mh taqia
Devon: Wie könnte dieser Link mir helfen?
mh taqia
Dank WBAR ist es das zweite Kopfgeld!
mh taqia

Antworten:

13

Es ist möglich, NSPanel mit dem Flag NSNonactivatingPanelMask zu verwenden. Das NSView von fmx form sollte ein Kind von NSPanel werden. Ich habe eine Hilfsklasse geschrieben, die sowohl für Windows- als auch für Mac-Plattformen funktioniert ( funktioniert unter 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.

Verwendung der oben genannten Einheit:

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

Wenn frmKeyboard Ihr Hauptformular ist, fügen Sie den obigen Code nicht in den Formularkonstruktor ein. Es wird empfohlen, ihn in OnShow einzufügen.

Geben Sie hier die Bildbeschreibung ein

Hinweis : WindowHandleToPlatform scheint in XE3 nicht vorhanden zu sein, sodass diese Zeile durch ersetzt werden kann

window := NSWindow(NSWindowFromObjC(FmxHandleToObjC(Form.Handle)));
mh taqia
quelle
1
Vielen Dank für die großartige Lösung - windowhandletoplatform scheint in XE3 nicht zu existieren, sodass die Zeile durch window ersetzt werden kann: = NSWindow (NSWindowFromObjC (FmxHandleToObjC (Form.Handle)));
David Peters
2

Sie können die Handhabung der Formularmaus deaktivieren, um zu verhindern, dass sie fokussiert wird. Angenommen, Ihr Formular heißt 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);

Es gibt ein kleines Problem darin, dass ein Rechtsklick nicht gestoppt wird. Wenn dies ein Problem ist, müssen Sie auf das Mousedown-Ereignis im Formular reagieren und die Mousedown-Funktion der Hauptformulare aufrufen, damit das Mausereignis nicht verloren geht. Da die rechte Maustaste dann die Mausereignisse erfasst, müssen Sie auch auf Ereignisse zum Bewegen der Maus und zum Hochfahren der Maus reagieren und diese an Ihr Hauptformular weiterleiten. Obwohl die Maus beim Klicken mit der rechten Maustaste erfasst wird, wird das Formular dennoch nicht fokussiert.

Dave Peters DP Software

David Peters
quelle
Falsch, funktioniert nicht. Das Formular ändert den Tastaturfokus beim Klicken.
mh taqia
Nun, es wird nicht fokussiert, aber was passiert ist, dass alle Mausklicks durch das Formular zu dem gehen, was sich darunter befindet. Wenn Sie festlegen können, dass für das Nicht-Fokus-Formular die TopMost-Eigenschaft festgelegt ist und sich nur ein leerer Teil Ihres eigenen Hauptformulars darunter befindet, funktioniert dies. Wenn Sie unter dem Fenster Hauptsteuerelemente für Formulare haben, werden diese beim Klicken mit der Maus fokussiert, da sich das Fenster ohne Fokus so verhält, als wäre es nicht vorhanden. Wenn das Fenster über dem Desktop platziert wird, erhält der Desktop einen Mausklick und Ihre Anwendung verliert den Fokus.
David Peters
Beachten Sie, dass ich Mausereignisse benötige. Ich kann Mausereignisse nicht ignorieren. Ich möchte auf eine Schaltfläche klicken, und ich möchte Firemonkey-Animationen haben, wenn der Mauszeiger auf ein Steuerelement eingeht. Angenommen, ich möchte eine virtuelle Tastatur erstellen. Die Vordergrundanwendung ist (zum Beispiel) TextEdit. Wenn ich auf eine Schaltfläche in meinem FMX-Formular klicke, wird ein Tastaturereignis generiert und ein Zeichen eingegeben.
mh taqia