Come creare il modulo "No Activate" in Firemonkey


147

In XCode aggiungendo questi metodi alla sottoclasse NSView è possibile impedire che la finestra diventi attiva quando si fa clic su di essa:

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

Nella piattaforma Windows Viene eseguito con questo semplice codice:

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

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

Come posso eseguire la sottoclasse di NSView per evitare che FMorm TForm diventi attivo quando faccio clic su di esso?

Come posso creare il modulo " No Activate " in firemonkey ?


3
Non sono sicuro se si applica anche a Firemonkey o se risponde correttamente alla tua domanda, ma potresti voler dare un'occhiata a questo esempio: delphi.about.com/od/delphitips2008/qt/ex_noactivate.htm
TildalWave

Grazie, ma è solo per Windows e il modo più semplice è la mia soluzione descritta sopra da "SetWindowLong", La domanda riguarda MacOS.
mh taqia,


Devon: In che modo questo link potrebbe aiutarmi?
mh taqia,

Grazie a WBAR, è la seconda taglia!
mh taqia,

Risposte:


13

È possibile utilizzare NSPanel con flag NSNonactivatingPanelMask . NSView di fmx dovrebbe diventare figlio di NSPanel. Ho scritto una classe di supporto che funziona su piattaforme Windows e Mac ( funziona su 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.

Utilizzo dell'unità sopra:

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

Se frmKeyboard è il tuo modulo principale, non inserire il codice sopra nel costruttore del modulo, si consiglia di inserirlo in OnShow.

inserisci qui la descrizione dell'immagine

Nota : WindowHandleToPlatform non sembra esistere in XE3, quindi quella linea può essere sostituita con

window := NSWindow(NSWindowFromObjC(FmxHandleToObjC(Form.Handle)));

1
Grazie per l'ottima soluzione - windowhandletoplatform non sembra esistere in XE3, quindi quella linea può essere sostituita con window: = NSWindow (NSWindowFromObjC (FmxHandleToObjC (Form.Handle)));
David Peters,

2

È possibile disattivare la gestione del mouse sui moduli per evitare che venga focalizzato. Supponendo che il tuo modulo si chiami 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);

C'è un leggero problema in quanto non si ferma un clic del tasto destro del mouse. Se questo è un problema, dovrai rispondere all'evento mousedown nel modulo e chiamare i mousedown dei moduli principali in modo che non perda l'evento del mouse. Dal momento che il tasto destro del mouse acquisirà quindi gli eventi del mouse, dovrai anche rispondere agli eventi di spostamento del mouse e anche al passaggio del mouse, inoltrandoli al modulo principale. Sebbene catturi il mouse con il tasto destro, non focalizzerà comunque il modulo.

Software Dave Peters DP


Non corretto, non funziona. Il modulo cambia lo stato attivo della tastiera al clic.
mh taqia,

Beh, non si sta focalizzando, ma ciò che accade è che qualsiasi clic del mouse passa attraverso il modulo fino a ciò che è sotto di esso. Se puoi organizzare che il modulo non attivo abbia la proprietà TopMost impostata e che solo una parte vuota del tuo modulo principale sia sempre al di sotto di essa, allora funzionerà. Se hai dei controlli del modulo principale sotto la finestra, questi verranno attivati ​​quando fai clic con il mouse poiché la finestra non attiva si comporta come se non fosse presente. Allo stesso modo se la finestra viene posizionata sul desktop, il desktop ottiene il clic del mouse e l'applicazione perde lo stato attivo.
David Peters,

Nota che ho bisogno di eventi del mouse. Non posso ignorare gli eventi del mouse. Voglio fare clic su un pulsante, inoltre voglio avere animazioni firemonkey quando il puntatore del mouse entra in un controllo. Supponiamo che io voglia creare una tastiera virtuale, l'applicazione in primo piano è (ad esempio) TextEdit. Quando faccio clic su un pulsante nel mio modulo fmx, verrà generato un evento da tastiera e verrà digitato un carattere.
mh taqia,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.