Delphi 앱을 macOS 바람의 움직임으로 만들기

Delphi 앱



안녕, 여러분!
오늘도 Mori Mori Delphi에서 앱을 만들고 있습니까? ?
나는 딱딱하게 만들고 있습니다.

Delphi MainForm의 움직임



Delphi로 다양한 OS용 앱을 만들 수 있지만, 걱정되는 것이 macOS에서의 움직임.
Delphi 앱은 MainForm이 닫히면 앱이 종료됩니다.
개인적으로는 괜찮다고 생각하지만 macOS 사용자에게는 부자연스러운 움직임이됩니다.
즉 MainForm이 닫혀도 앱은 계속 생존해야 합니다.

macOS 바람을 만들 준비



이제 MainForm이 닫혀도 끝나지 않도록 합시다.
다만, 메뉴로부터는 종료될 필요가 있으므로 MainMenu 가 필수가 됩니다.
현재의 TMainMenu 는 제일 좌측의 메뉴가 어플리케이션 메뉴로서 표시되므로, 제일 좌측을 어플리케이션 메뉴로 한 메뉴를 만들어 둡니다.
또한 숨겨진 MainForm을 다시 표시하기 위해 창 메뉴에 표시라는 메뉴도 만들어 둡니다. 여기는 「파일」메뉴의 「신규」로서 새롭게 MainForm 를 만든다, 라고 하는 방법에서도 괜찮습니다만, 이번은 제일 간단한 방법을 나타냅니다.



또한 menuAppQuit의 바로 가기 키를 Cmd + Q로 설정하는 것이 좋습니다.



menuAppQuit 의 OnClick 이벤트 핸들러는 다음과 같이 종료할 수 있습니다.
Close가 아닌 Applicatio.Terminate를 사용하는 것이 포인트입니다.
마찬가지로 보기 메뉴에는 Show를 작성하여 MainForm을 표시합니다.
procedure TfrmMain.menuAppQuitClick(Sender: TObject);
begin
  Application.Terminate;
end;

procedure TfrmMain.menuWindowShowClick(Sender: TObject);
begin
  Show;
end;

macOS 바람으로 만드는 단계



가장 간단한 절차는 다음과 같습니다.
  • 더미 폼 만들기 (TDummyMain)
  • 독의 문맥 메뉴의 「숨기기」로 숨긴 뒤 표시시키면(자) TDummyMain 가 보여 버리므로, 그것을 막기 위해서 TDummyMain.CanShow 를 override 하고 False 를 돌려주도록 합니다.
  • MainForm.OnClose 에 이벤트 핸들러를 설정해 Action := TCloseAction.caHide; 로서 윈도우 좌상의 버튼을 눌러도 폼을 닫지 않게 합니다.
  • Application.MainForm을 TDummyMain의 인스턴스로 바꿉니다.
  • 원래 MainForm에 있던 MainMenu를 TDummyMain에 넣습니다.

  • 그리고 이런 형태가됩니다.

    이것을 모두 실장한 것이 이하입니다.
    (*
     * Supports macOS style behavior
     *
     * PLATFORMS
     *   macOS
     *
     * LICENSE
     *   Copyright (c) 2018 HOSOKAWA Jun
     *   Released under the MIT license
     *   http://opensource.org/licenses/mit-license.php
     *
     * HOW TO USE
     *   uses
     *     PK.Utils.MacDummyMain;
     *
     *   type
     *     TForm1 = class(TForm)
     *       procedure FormCreate(Sender: TObject);
     *     end;
     *
     *   procedure TForm1.FormCreate(Sender: TObject);
     *   begin
     *     ReplaceMainFormForMac;
     *   end;
     *
     * 2018/03/28 Version 1.0.0
     * 2018/03/29 Version 1.0.1
     * Programmed by HOSOKAWA Jun (twitter: @pik)
     *)
    
    unit PK.Utils.MacDummyMain;
    
    interface
    
    procedure ReplaceMainFormForMac;
    
    implementation
    
    uses
      System.Classes
      , System.UITypes
      , System.SysUtils
      , System.Rtti
      , FMX.Types
      , FMX.Forms
      , FMX.Menus
      ;
    
    type
      TOpenForm = class(TCommonCustomForm)
      end;
    
      TDummyMain = class(TCommonCustomForm)
      protected
        procedure MainFormClose(Sender: TObject; var Action: TCloseAction);
        function CanShow: Boolean; override;
      public
        constructor CreateNew(Owner: TComponent; Dummy: NativeInt = 0); override;
      end;
    
    { TDummyMain }
    
    function TDummyMain.CanShow: Boolean;
    begin
      Result := False;
    end;
    
    constructor TDummyMain.CreateNew(Owner: TComponent; Dummy: NativeInt = 0);
    begin
      inherited;
    
      TThread.ForceQueue(
        TThread.Current,
        procedure
        var
          MainForm: TOpenForm;
          Obj: TObject;
          MM: TMainMenu absolute Obj;
          RttiType: TRttiType;
          RttiField: TRttiField;
        begin
          // MainForm 取得
          MainForm := TOpenForm(Application.MainForm);
    
          // OnClose を変更して閉じないようにする
          MainForm.OnClose := MainFormClose;
    
          // 自分を MainForm に指定
          Application.MainForm := Self;
    
          // Menu を載せ替える
          Obj := MainForm.MainMenu;
          if (Obj <> nil) and (Obj is TMainMenu) then
          begin
            AddObject(MM);
    
            RttiType := SharedContext.GetType(ClassType);
            if RttiType <> nil then
            begin
              RttiField := RttiType.GetField('FMainMenu');
              if RttiField <> nil then
                RttiField.SetValue(Self, MM);
            end;
    
            TMainMenu(MM).RecreateOsMenu;
          end;
        end
      );
    end;
    
    procedure TDummyMain.MainFormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := TCloseAction.caHide;
    end;
    
    procedure ReplaceMainFormForMac;
    begin
      {$IFDEF OSX}
      TDummyMain.CreateNew(Application);
      {$ELSE}
      // 他の OS では何もしない
      {$ENDIF}
    end;
    
    end.
    

    사용법



    PK.Utils.MacDummyMain을 사용하고 FormCreate에서 CreateDummyMainForm을 호출하면됩니다.
    implementation
    
    {$R *.fmx}
    
    uses
      PK.Utils.MacDummyMain;
    
    procedure TfrmMain.FormCreate(Sender: TObject);
    begin
      ReplaceMainFormForMac;
    end;
    

    작동 중





    이 동작 중 스크린 샷을 보더라도 알 수는 없지만 ...
    왼쪽 상단의 빨간색 버튼을 눌러도 양식이 사라지면 응용 프로그램이 종료되지 않습니다.

    그 밖에도 폼을 내는 것과 같은 어플리케이션이라면, 「파일」메뉴등을 만들어 「신규 xxx」라고 하는 메뉴로 폼을 호출하도록(듯이) 하면, 보다 macOS풍의 동작이 될 것입니다.

    요약



    macOS의 움직임에 맞추려면 PK.Utils.MacDummyMain을 사용하면 간단합니다.
    하지만, MainForm 가 닫히는 것과 동시에 어플리케이션이 끝나도, 개인적으로는 개미입니다.

    좋은 웹페이지 즐겨찾기