Dans XCode, l'ajout de ces méthodes à votre sous-classe NSView peut empêcher la fenêtre de devenir active lorsque vous cliquez dessus:
- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent )theEvent {
return YES;
}
- (BOOL)acceptsFirstMouse:(NSEvent )theEvent {
return YES;
}
- (void)mouseDown:(NSEvent )theEvent {
[[[NSApp]] preventWindowOrdering];
}
Dans la plate-forme Windows Cela se fait par ce code 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);
Comment puis-je sous-classer NSView pour empêcher mon FMX TForm de devenir actif en cliquant dessus?
Comment puis-je créer le formulaire "No Activate" sous firemonkey ?
Il est possible d'utiliser NSPanel avec l'indicateur NSNonactivatingPanelMask. La forme NSView de fmx devrait devenir l'enfant de NSPanel. J'ai écrit une classe d'aide qui fonctionne pour les plates-formes Windows et Mac (fonctionne sur 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.
Utilisation de l'unité ci-dessus:
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 est votre formulaire principal, ne mettez pas le code ci-dessus dans le constructeur de formulaire, il est recommandé de le mettre dans OnShow.
Remarque: WindowHandleToPlatform ne semble pas exister dans XE3, de sorte que la ligne peut être remplacée par
window := NSWindow(NSWindowFromObjC(FmxHandleToObjC(Form.Handle)));
Vous pouvez désactiver la gestion des formulaires par la souris pour éviter qu'elle ne soit focalisée. En supposant que votre formulaire s'appelle 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);
Il y a un léger problème en ce qu'il n'arrête pas un clic droit de la souris. Si c'est un problème, vous devrez répondre à l'événement mousedown dans le formulaire et appeler les principaux formulaires mousedown afin qu'il ne perde pas l'événement mouse. Étant donné que la souris vers le bas capturera alors les événements de la souris, vous devez également répondre aux événements de déplacement de la souris et de la souris vers le haut - en les transmettant à votre formulaire principal. Bien qu'il capture la souris sur un clic droit, il ne concentrera toujours pas le formulaire.
Dave Peters DP Software