2012-10-07 43 views
149

en Xcode mediante la adición de estos métodos a su subclase NSView puede evitar que la ventana se vuelva activa cuando se hace clic en él:Cómo crear la forma "Sin Activar" en FireMonkey

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

En la plataforma de Windows Se realiza mediante esta código simple:

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

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

Como puedo subclase NSView para evitar que mis FMX TForm convertirse en activa cuando se hace clic en él?

¿Cómo puedo crear "Sin Activar" forma en FireMonkey?

+3

No está seguro de si se aplica a FireMonkey así, o si responde a su pregunta correctamente, pero es posible que desee echar un vistazo a este ejemplo: http://delphi.about.com/od/delphitips2008/qt/ex_noactivate.htm – TildalWave

+0

Gracias, pero es solo para Windows y la forma más fácil es mi solución descrito anteriormente por "SetWindowLong", la pregunta es sobre MacOS. –

+0

Enlace: http://stackoverflow.com/questions/9048346/how-embed-a-firemonkeykeyform-inside-a-control – Devon117

Respuesta

13

Es posible utilizar NSPanel con NSNonactivatingPanelMask bandera. El NSView de la forma fmx debería convertirse en hijo de NSPanel. He escrito una clase de ayuda que trabaja para las plataformas Windows y Mac (Obras en 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. 

uso de la unidad anterior:

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

Si frmKeyboard es su formulario principal no lo haga poner el código anterior en el constructor de formulario, se recomienda ponerlo en OnShow.

enter image description here

Nota: WindowHandleToPlatform no parece existir en XE3 modo que la línea se puede sustituir por

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

Gracias por la gran solución - windowhandletoplatform no parece existir en XE3 por lo que la línea se puede reemplazar con la ventana: = NSWindow (NSWindowFromObjC (FmxHandleToObjC (Form.Handle))); –

+0

Gracias, actualicé mi respuesta. –

2

Puede desactivar el manejo del mouse de formularios para evitar que se enfoque. Asumiendo que su forma se llama 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); 

Hay un ligero problema de que no se detiene un clic derecho del ratón. Si eso es un problema, tendrá que responder al evento mousedown en el formulario y llamar al mousedown de las formas principales para que no pierda el evento del mouse. Como el mouse derecho hacia abajo capturará los eventos del mouse, también tendrá que responder a los eventos de movimiento del mouse y mouse up también, reenviándolos a su formulario principal. Aunque captura el mouse con el botón derecho, no enfocará el formulario.

Software Dave Peters DP

+0

Incorrecta, no funciona. El formulario cambia el foco del teclado al hacer clic. –

+0

Bueno, no se enfoca, pero lo que sucede es que cualquier clic del mouse pasa a través del formulario a lo que está debajo de él. Si puede organizar que el formulario no enfocado tenga la propiedad TopMost establecida y solo una parte en blanco de su formulario principal esté debajo de él, entonces funcionará. Si tiene algún control de formulario principal debajo de la ventana, obtendrá el foco cuando haga clic mientras la ventana que no está enfocada se comporta como si no estuviera allí. De manera similar, si la ventana se coloca sobre el escritorio, el escritorio hace clic y su aplicación pierde el foco. –

+0

Tenga en cuenta que necesito eventos de mouse. No puedo ignorar los eventos del mouse. Quiero hacer clic en un botón, también quiero tener animaciones de firemonkey cuando el puntero del mouse ingresa en un control. Supongamos que quiero crear un teclado virtual, la aplicación de primer plano es (por ejemplo) TextEdit. Cuando hago clic en un botón de mi formulario de fmx, se genera un evento de teclado y se escribe un carácter. –

Cuestiones relacionadas