Cara membuat formulir "Tidak Aktifkan" di Firemonkey

147

Dalam XCode dengan menambahkan metode ini ke subclass NSView Anda dapat mencegah jendela menjadi aktif saat mengkliknya:

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

Di platform Windows Ini dilakukan oleh kode sederhana ini:

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

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

Bagaimana saya bisa mensubklasifikasikan NSView untuk mencegah FMX TForm saya menjadi aktif saat mengkliknya?

Bagaimana saya bisa membuat formulir " Tidak Aktifkan " di firemonkey ?

mh taqia
sumber
3
Tidak yakin apakah itu berlaku untuk Firemonkey juga, atau jika itu menjawab pertanyaan Anda dengan benar, tetapi Anda mungkin ingin melihat contoh ini: delphi.about.com/od/delphitips2008/qt/ex_noactivate.htm
TildalWave
Terima kasih, tetapi ini hanya untuk Windows dan cara yang lebih mudah adalah solusi saya yang dijelaskan di atas oleh "SetWindowLong", Pertanyaannya adalah tentang MacOS.
mh taqia
Devon: Bagaimana tautan ini dapat membantu saya?
mh taqia
Berkat WBAR, ini adalah hadiah kedua!
mh taqia

Jawaban:

13

Dimungkinkan menggunakan NSPanel dengan flag NSNonactivatingPanelMask . NSView bentuk fmx harus menjadi anak dari NSPanel. Saya telah menulis kelas pembantu yang berfungsi untuk platform Windows dan Mac ( Bekerja pada 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.

Penggunaan unit di atas:

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

Jika frmKeyboard adalah Form Utama Anda, maka jangan masukkan kode di atas dalam form constructor, Disarankan untuk meletakkannya di OnShow.

masukkan deskripsi gambar di sini

Catatan : WindowHandleToPlatform sepertinya tidak ada di XE3 sehingga garis itu dapat diganti

window := NSWindow(NSWindowFromObjC(FmxHandleToObjC(Form.Handle)));
mh taqia
sumber
1
Terima kasih atas solusi hebat - windowhandletoplatform tampaknya tidak ada di XE3 sehingga garis tersebut dapat diganti dengan jendela: = NSWindow (NSWindowFromObjC (FmxHandleToObjC (Form.Handle)));
David Peters
2

Anda bisa mematikan form penanganan mouse untuk mencegahnya menjadi fokus. Dengan asumsi formulir Anda disebut 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);

Ada sedikit masalah karena tidak menghentikan klik mouse kanan. Jika itu masalah, Anda harus merespons acara mousedown dalam formulir dan memanggil form mousedown utama agar tidak kehilangan acara mouse. Karena mouse kanan bawah akan menangkap kejadian mouse, Anda juga perlu merespons gerakan mouse dan mouse juga - meneruskannya ke bentuk utama Anda. Meskipun ia menangkap mouse pada klik kanan, ia tetap tidak memfokuskan form.

Perangkat Lunak Dave Peters DP

David Peters
sumber
Salah, tidak berfungsi. Formulir mengubah fokus keyboard saat klik.
mh taqia
Yah itu tidak mendapatkan fokus tetapi yang terjadi adalah bahwa setiap klik mouse melalui formulir apa pun yang ada di bawahnya. Jika Anda dapat mengatur bahwa formulir non fokus memiliki set properti TopMost dan hanya bagian kosong dari formulir utama Anda sendiri yang ada di bawahnya, maka itu akan berfungsi. Jika Anda memiliki kontrol bentuk utama di bawah jendela maka mereka akan mendapatkan fokus ketika Anda mengklik mouse karena jendela non-fokus berperilaku seperti itu tidak ada. Demikian pula jika jendela ditempatkan di atas desktop maka desktop mendapat klik mouse dan aplikasi Anda kehilangan fokus.
David Peters
Perhatikan bahwa saya perlu acara mouse. Saya tidak bisa mengabaikan acara mouse. Saya ingin mengklik tombol, juga saya ingin memiliki animasi firemonkey ketika pointer mouse masuk pada kontrol. Asumsikan bahwa saya ingin membuat keyboard virtual, aplikasi foreground adalah (misalnya) TextEdit. Ketika saya mengklik tombol pada formulir FMX saya, acara keyboard akan dihasilkan dan karakter akan diketik.
mh taqia