Embarcadero: Don’t hide behind bad design

Recently I created an QC report to try and get the default wizard behaviour changed to be user friendly.

The issue is that the description text for the wizard item only shows when the mouse is hovered over the item. This means that keyboard navigation will never show the description nor will just opening the dialog initially, unless you magically have your mouse in the wrong spot.

Apparently, this is the way the wizard has been designed.

Report No: 115353 (RAID: 38977) Status: Closed
The mobile wizard doesn’t show a description using you hover over the item
http://qc.embarcadero.com/wc/qcmain.aspx?d=115353

Mobile Wizard comparison screen captures

Another item that is also “As Designed” is the text used to describe the blank application item in the wizard. This text states:

“The Blank Application Template is a jumping off point for creating a completely custom mobile application.”

Now I acknowledge that English was the subject I liked the least at school but that sentence could be improved.

Here are a couple of alternatives that I’ve thought about in the past ten seconds.

  1. “The Blank Application Template is the starting point for creating a custom mobile application.”
  2. “Use the Blank Application Template to create a custom mobile application.”

Perhaps you can add your own.

XE4 Mobile Tip #2 – Loading local HTML content

I’ve already answered this on the newsgroups however it deserves a little more attention.

The most important thing to do when deploying additional files with your app is to make sure they are prefixed with “StartUp/”. This tells the deployment manager to deploy these files with the application and place them in the folder specified after the StartUp/ prefix. This prefix is case sensitive.

Here is a screen capture of the deployment manager for the sample project available for download at the end of this post.

Deployment Manager

Verify file is included without running the app

You can even verify that the files have been deployed with the app by looking in the Applications section of your device in the XCode Organizer.

XCode Organizer

Sample code

The code below loads the content of the file into the WebBrowser control that is on the form.

unit Unit288;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types,
  FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.WebBrowser, FMX.StdCtrls, FMX.Layouts;

type
  TForm288 = class(TForm)
    LoadHtmlButton: TButton;
    WebBrowser1: TWebBrowser;
    Layout1: TLayout;
    procedure LoadHtmlButtonClick(Sender: TObject);
  end;

var
  Form288: TForm288;

implementation

uses
  IOUtils;

{$R *.fmx}

procedure TForm288.LoadHtmlButtonClick(Sender: TObject);
var
  LFilename: string;
begin
  LFilename := TPath.GetDocumentsPath + '/index.html';
  if TFile.Exists(LFilename) then
    WebBrowser1.Navigate('file://' + LFilename)
  else
    MessageDlg(Format('File not found: %s', [LFilename]), TMsgDlgType.mtError, [TMsgDlgBtn.mbClose], 0);
end;

end.

Works on my device!

Device Test (portrait)
Device Test (landscape)

Download the Code

Download the sample project.

A couple of notes

  • You wouldn’t deploy a static file to the Documents folder unless you wanted it to be backed up (via iTunes or iCloud) with other user data.
  • The best location for static files is Library/Application Support/, this is content that is generated when the application runs, or is included with the application.

XE4 Mobile Tip #1 – Disable the GPU Canvas

If you don’t like circles with jagged edges, you can disable the GPU canvas.

Using the GPU Canvas:

GPU Canvas

Not using the GPU Canvas:

Non-GPU Canvas

Full image comparison at 200% using Beyond Compare. Click to enlarge to full size (2600 x 1540).

Canvas Comparison

NOTE: If you see other drawing issues, enable the GPU canvas again just to see if it is a canvas issue.

Disabling the GPU Canvas

To disable the GPU canvas you must modify the project file source.

Add FMX.Platform and FMX.Consts to the uses clause. I suggest adding these items after the FMX.Forms entry but before any other units. You MUST leave System.StartUpCopy as the first unit in the uses clause.

Before Application.Initialize is called, enter this line:

TPlatformServices.Current.GlobalFlags.Add(GlobalDisableiOSGPUCanvas, True);

FireMonkey: Extending TFloatAnimation to support maximum loops

Background

In response to a QC report I wrote early last year I decided to implement a LoopCount property on the TFloatAnimation component.

Report No: 105140 Status: Open
Add a LoopCount property to the TAnimation class
http://qc.embarcadero.com/wc/qcmain.aspx?d=105140

Class Definition

  TJSCustomLoopCountFloatAnimation = class(TFloatAnimation)
  public
    type
      TAnimationLoopEvent = reference to procedure (Sender: TObject; const LoopNumber: Integer; var Cancel: Boolean);
  private
    FLoopCount: Integer;
    FCheckingLooping: Boolean;
    FOnLoop: TAnimationLoopEvent;
  protected
    FLoopsComplete: Integer;
    procedure FirstFrame; override;
    procedure DoLoop(var ACancel: Boolean); virtual;
    procedure ProcessAnimation; override;
  public
    constructor Create(AOwner: TComponent); override;
    property LoopCount: Integer read FLoopCount write FLoopCount default 3;
    property OnLoop: TAnimationLoopEvent read FOnLoop write FOnLoop;
  end;

Nothing that interesting in the new descendant. New property called LoopCount to control the number of loops and a new event that gets triggered each time a loop completes.

The published component publishes the new property and event but also changes the default values for two existing properties. It makes sense to set Loop to true when the new class is for enhancing the looping ability and if you’re looping, generally AutoReverse will be set to true.

  TJSLoopCountFloatAnimation = class(TJSCustomLoopCountFloatAnimation)
  published
    property AutoReverse default True;
    property Loop default True;
    property LoopCount;
    property OnLoop;
  end;

Implementation

I won’t post all of the code here because you can download from the link provided below, just a couple of snippets.

We need to override the FirstFrame method to initialise a couple of variables we use.

  • Checking to see if the LoopCount property is valid (raise an exception if it isn’t)
  • Initialise the variable to zero that counts the interactions
  • Make sure we are going to be checking the animation process for loops

Most of the work occurs in the overridden ProcessAnimation method.

procedure TJSCustomLoopCountFloatAnimation.ProcessAnimation;
var
  LCtx: TRttiContext;
  LType: TRttiType;
  LField: TRttiField;
  LCancel: Boolean;
begin
  inherited;
  if FCheckingLooping then
  begin
    LType := LCtx.GetType(Self.ClassInfo);
    if Assigned(LType) then
    begin
      LField := LType.GetField('FTime');
      if LField <> nil then
      begin
        if LField.GetValue(Self).AsExtended = 0 then
        begin
          Inc(FLoopsComplete);
          LCancel := False;
          if FLoopsComplete > 1 then
            DoLoop(LCancel);
          // The first time through, FTime is 0 so we need to offset this by
          // adding 1 when checking for completion
          if LCancel or (FLoopsComplete = LoopCount + 1) then
          begin
            LField := LType.GetField('FRunning');
            if LField <> nil then
              LField.SetValue(Self, False);
          end;
        end;
      end;
    end;
  end;
end;

Thanks to extended RTTI we can access a couple of private fields that we need to determine if a loop has been completed. This occurs when the FTime variable is zero. There is one issue with using this value and that is that the first “Loop” should be ignored since the first time ProcessAnimation is called FTime is zero so by the logic used, a loop has completed. This is why the DoLoop method is only called if the FLoopsComplete variable is greater than one.

Naturally it is possible to handle this one-off situation differently using a “First Time Through” variable but under the circumstances, I decided to go with the solution in place.

Once the LoopsComplete value is one greater than the LoopCount (refer to the above two paragraphs if you’ve already forgotten about why) the private field FRunning is set to False. Setting FRunning to false, stops the animation immediately.

Why not just call the public Stop method instead of going to the trouble of setting a private field? The answer to that is found in the ProcessTick method of the animation control (incidently, why isn’t this method virtual?).

  ...
  ProcessAnimation; // <==== We set FRunning to false here
  DoProcess;

  if not FRunning then
  begin
    if Assigned(AniThread) then
      TAniThread(AniThread).FAniList.Remove(Self);
    DoFinish;
  end;
  ...

By setting FRunning to false within our ProcessAnimation override, we are avoiding another frame being processed before the animation is stopped. This is because the Stop method calls ProcessAnimation and DoProcess as well.

Download

You can download the component and a cheesy demo application from the link provided. There is no package for the component to install it into your IDE, this is left as an exercise for the reader :-).

Loop Animation Demo (short video – 39KB)

Download LoopCount Component and Demo

NOTE: Before downloading the source code you must agree to the license displayed below.

License Start

This space intentionally left blank…

License End

Fixing FireMonkey: TMemo/TEdit – OnExit

Currently in the Delphi XE3 release there is a bug in the OnExit processing of TMemo and TEdit (possibly others) controls.

Issue

If you make a change in a TEdit or TMemo control and then exit the control, after the OnExit event is called, another OnChange event is called. This shouldn’t happen.

This doesn’t occur in the VCL framework and shouldn’t happen in FireMonkey.

Solution

TEdit

The fix for the TEdit cause is pretty simple. Since the FNeedChange field in TCustomEdit is protected we can use a class cracker to fix it. You could put the fix into either a new control or in either an OnExit or OnChange handler in your application.

type
  TEditClass = class(TEdit);
...
procedure Form1.OnExit(Sender: TObject);
begin
  TEditClass(Edit1).FNeedChange := False;
end;

It’s interesting to note for TEdit, FNeedChange is protected. This is because in the TCustomEditBox descendant (used by TSpinBox and TNumberBox controls), the FNeedChange field is set to False after the overriden Change method is called. Perhaps this should have triggered the developer making that change, to actually fix the issue in the ancester class.

TMemo

The fix for TMemo is more interesting because unlike the TCustomEdit class, FNeedChange is private. Thankfully extended RTTI comes into play.

I put this code where the memo content was saved to disk, you could put the code in the same spot in your applications (if applicable), or place it in either OnChange or OnExit events.

var
  LCtx: TRTTIContext;
  LField: TRttiField;
  LInst: TRttiInstanceType;
begin
  // save your memo contents
  LInst := LCtx.GetType(Memo1.ClassType).AsInstance;
  LField := LInst.GetField('FNeedChange');
  if LField <> nil then
    LField.SetValue(Memo1, False);
end;

If your save is triggered by the user selecting a control that takes focus from the memo, the OnExit event will trigger before executing the fix above. Under these circumstances, moving the fix to the OnExit event of the memo is advised.

Another TMemo Issue

The OnChange event is only triggered after typing two characters in a TMemo, the first character typed into the memo doesn’t trigger the OnChange event.

XE Plus Pack – Release 11

Release 11 of XE Plus Pack is now available for all supported IDE versions (Delphi XE, Delphi XE2, Delphi XE3).

XE Plus Pack and XE2 Plus Pack now include all features available in XE3 Plus Pack.

Release 11 features:

  • Visual Forms can now support multiple projects in the same location.
    • Note: Existing projects with Visual Forms active will need to be rescanned.
  • Added new commands to the Project Managers’ File Actions menu item.
    • Open With…
    • Open with Notepad
  • Improved Mouse Scrolling of the designer surface for VCL applications.
  • Minor UI enhancements to dialogs within Visual Forms expert.
  • Fix issue with the replace capture dialog not displaying the image to use.
  • Add “Capture Form” command to a forms context menu so it is even easier to update a form within the Visual Forms view if automatic updating is not active.
  • Support Resolution guidelines being displayed in the FireMonkey designer.

Office closing for a week (plus what’s coming up)

We will be closed from today for a week. Reopening on Friday the 30th of November.

No this isn’t a thanksgiving holiday, we are located in Australia.

 

After reopening, Release 11 of XE Plus Pack will be made available for all supported IDE versions.

During the second week of December, a beta of the IDE Settings Wizard (first mentioned June 2011) will commence for registered XE Plus Pack users (since it will be included with XE Plus Pack licenses).

Here is how the wizard starts:

IDE Settings Wizard

IDE Settings Wizard

 

FireMonkey Style – TStyleTag (Part 2)

Part 1 – http://jed-software.com/blog/?p=699

Along with the missing registration of TStyleTag, the following components cannot be used in styles without hand editing FMX or Style files.

  1. TFontObject
  2. TBrushObject

I’ve updated the package to now register these missing style components so you can use them in custom styles.

Download Package

The change is straight forward if you have already downloaded and installed the previous package.

The register procedure should now look something like this:

procedure Register;
begin
  RegisterComponents('Shapes', [TStyleTag, TFontObject, TBrushObject]);
end;

NOTE: You may want to register them to a different palette location.

 

JSDialog Pack 2.0.021

A new version of JSDialog Pack was sent to customers earlier this month. If you haven’t received your Delphi XE3 compatible version of JSDialog Pack, let me know.

 

Release History

October (2.0.021)

– Add support for Delphi XE3
– Make non-modal dialog sheets move when the form is being moved or resized
– Fix combo box items not being set when default values specified
– Fix non-native progress bar state colours being incorrect

 

License Change

Like XE Plus Pack, JSDialog Pack as had a license change where new licenses are now lifetime licenses with no more to pay. You will continue to receive each new version with no additional payment. This is retrospective for the past 18 months of purchases. If you think you fall into this category, send me an email from the account used for the purchase.

For those that had been customers previously and decided not to renew, but would like to, contact me and we’ll work out the top up fee.

New licenses with lifetime support and upgrades are available for 90 Euros.

Image issues with previous post

Apparently there were some issues with rendering images from the Improving the default FM2 Popup Menu style under OS X post. They were just tiff images created using Snag-It on OS X.

I’ve now resaved these images to PNG (using MS Paint – since Snag-It on windows can’t open the files!). Now they should show correctly under all browsers, let me know if there are still issues.