Archive

Posts Tagged ‘Confirmation’

HowTo: show a confirmation dialog with Delphi FireMonkey UI library

Here’s my answer at https://stackoverflow.com/a/71728787/903783 on how to show a confirmation dialog with Delphi’s cross-platform FireMonkey UI library.

Based on other answers, I’ve added a “Confirm” class function (adapted to return a Boolean) to TApplicationHelper (which also has an untested function to get the executable filename), currently maintained under the READCOM_App repository.

Update: had to modify the answer to use a callback so that it also works on platforms where FMX supports only asynchronous dialogs, like Android

unit Zoomicon.Helpers.FMX.Forms.ApplicationHelper;

interface
uses
  FMX.Forms; //for TApplication

type
  TApplicationHelper = class helper for TApplication
  public
    function ExeName: String;
    class function Confirm(Prompt: String): Boolean;
  end;

implementation
  uses
    {$IFDEF ANDROID}
    System.SysUtils, //TODO: is this needed?
    Androidapi.Helpers, //for TAndroidHelper, JStringToString
    Androidapi.Jni.JavaTypes, //to avoid "H2443 Inline function 'JStringToString' has not been expanded"
    {$ENDIF}
    System.UITypes, //for TMsgDlgType
    FMX.DialogService, //for TDialogService
    FMX.Dialogs; //for mbYesNo

{$region 'TApplicationHelper'}

//from https://gist.github.com/freeonterminate/2f7b2e29e40fa30ed3c4
function TApplicationHelper.ExeName: String;
begin
  Result := ParamStr(0);

  {$IFDEF ANDROID}
  if (Result.IsEmpty) then
    Result := JStringToString(TAndroidHelper.Context.getPackageCodePath);
  {$ENDIF}
end;

//based on https://stackoverflow.com/questions/42852945/delphi-correctly-displaying-a-message-dialog-in-firemonkey-and-returning-the-m
class procedure TApplicationHelper.Confirm(const Prompt: String; const SetConfirmationResult: TProc<Boolean>);
begin
  with TDialogService do
  begin
    PreferredMode := TPreferredMode.Platform;
    MessageDialog(Prompt, TMsgDlgType.mtConfirmation, mbYesNo, TMsgDlgBtn.mbNo, 0,
      procedure(const AResult: TModalResult)
      begin
        //Note: assuming this is executed on the main/UI thread later on, so we just call the "SetResult" callback procedure passing it the dialog result value
        case AResult of
          mrYes: SetConfirmationResult(true);
          mrNo: SetConfirmationResult(false);
        end;
      end
    );
  end;
end;

{$endregion}

end.

usage example is in the MainForm of READCOM_App

procedure TMainForm.HUDactionNewExecute(Sender: TObject);
begin
  TApplication.Confirm(MSG_CONFIRM_CLEAR_STORY, //confirmation done only at the action level //Note: could also use Application.Confirm since Confirm is defined as a class function in ApplicationHelper (and those can be called on object instances of the respective class too)
    procedure(Confirmed: Boolean)
    begin
      if Confirmed and (not LoadDefaultDocument) then
        NewRootStoryItem;
    end
  );
end;

where MSG_CONFIRM_CLEAR_STORY is declared as

resourcestring
  MSG_CONFIRM_CLEAR_STORY = 'Clearing story: are you sure?';

Gotcha: var x = x() in Javascript gives “Object Expected” error

At ClipFlair Studio (a Silverlight app), I had some time ago implemented a confirmation warning upon user trying to close the webpage (when it was running inside the web browser), which then had stopped functioning. It seems at some refactoring I had added code like the following:

var activityView = activityView();

and it was failing with error “Object Expected” (and even worse this was done silently – had to use browser’s debugging tools to catch it – because the respective code was running at onbeforeunload browser window event).

Such code would be fine in C#, but in Javascript it seems that a local variable with name x hides a function named x (with any number of parameters in its definition). This is obviously because functions are first-class objects in Javascript and can be treated like variables themselves too.

All started working again fine after changing that code to:

var a = activityView();

Below is the respective block of Javascript code included in a script tag at the webpage that hosts the Silverlight control. Note the “control.content.activityWindow”, where “activityWindow”  is accessed via the Silverlight HTMLBridge (that object – that has to be marked with ScriptableType class attribute – is registered using that key via HtmlPage.RegisterScriptableObject at the Silverlight app side).

function silverlightControl() {
  return document.getElementById("silverlightControl");
}

function onSilverlightLoad(sender, args) {
  var control = silverlightControl();
  if (control != null)
    control.focus();
}

function activityWindow() {
  var control = silverlightControl();
  if ( (control != null) && (control.content != null) )
    return control.content.activityWindow;
  else
    return null; //need this so that it doesn't return undefined
}

function activityView() {
  var a = activityWindow();
  if (a != null)
    return a.GetView();
  else
    return null; //need this so that it doesn't return undefined
}

function onClosing() {
  var a = activityView();
  if ( (a != null) && (a.WarnOnClosing) )
    return "Do you want to exit ClipFlair Studio?";
//else return undefined is implied (no onClosing message that is) } function onClosed() { var a = activityView(); if (a != null) a.WarnOnClosing = false; } function installEventHandlers() { window.onbeforeunload = onClosing; window.onunload = onClosed; } installEventHandlers();