Archive for the ‘FireMonkey’ Category.

XE Plus Pack – Release 17

After a much longer testing phase than expected, the newest release of XE Plus Pack for Delphi XE6 and Delphi XE7 is now available for download.

XE6 Plus Pack – Download for XE6
XE7 Plus Pack – Download for XE7

The main new feature for Release 17 is the ability to quickly generate and apply all of the required icon and splash images required for a mobile (iPhone or Android) application.

Image Generator

This new feature is called Image Generator and is accessible from the Project menu in the IDE. It is only available when a FireMonkey project is loaded within the IDE.

Image Generator - Design

You have the ability to add use three elements to compose your splash images.

  1. App Icon
  2. Backgroun
  3. Company Logo

Image Generator - Output

Once the images have been generated, you can review the final creations before applying the images to the correct elements in the active project. The images are applied for All Configurations. It does not support selecting whether to apply the images to Debug and/or Release. An enhancement for the next release – if deemed necessary.

The details of the design can be save to an “xeppsettings” file in the same folder as the project. Currently this is a binary file, however it will be changed to an XML or text readable file for easier use with source control.

More information about the Image Generator expert will be available soon.

View Units Dialog

An enhancement has been made to the View Units dialog which is updated due to the Visual Forms feature. This new dialog now gives the option of viewing either the selected files thumbnail (if available) or all available thumbnails. Additional options will be added to the View Units dialog in future releases.

View Units - Single View

To toggle the thumbnail view mode, use F8.

View Units - Thumbnail View

FireMonkey – TArc and Retina, not the best of friends

Last week I noticed a post on www.fmxexpress.com that showed a different UI for a progress bar. So instead of starting work on something I should have, I turned the code into a component and added the ability to change the colours used when displayed.

I’d known TArc didn’t render nicely on a Retina device because I was using it to render a navigation UI concept. Which I’ve had to shelve because of this issue.

The control does look good under other platforms (untested under Android – issues not anticipated though).

You can download the full source code for the control from the Quality Central report lodged for the rendering issue. It looks like you will need to use a native client to download from the QC report as it doesn’t look like you can from the web client.

The control has the following “license” in the main unit:

{

TCircleProgress is based on code made open source by developer OneChen.

Original Code Link: https://github.com/OneChen/ProgressCircle
How I found out link: http://www.fmxexpress.com/build-a-progress-spinner-using-objects-in-delphi-xe6-firemonkey-on-android-and-ios/

Developer of this component: Jeremy North (jeremy.north@gmail.com)

DO NOT PAY FOR THIS COMPONENT. DONATE TO THE ORIGINAL AUTHOR OF THE IDEA IF YOU WANT TO CONTRIBUTE.

Disclaimer: This code is provided free of charge, bugs and all.

}

 

Windows

 

The Windows capture actually shows another drawing bug with FireMonkey. Notice how the rectangle at the top has two black dots in the top left and top right corners. This happens when you eliminate sides from a rectangle and maintain one full side to be drawn. Something that I’ve reported previously.

Report No: 122616 (RAID: 47827) Status: Open
Removing Sides can leave unwanted dots in corners
http://qc.embarcadero.com/wc/qcmain.aspx?d=122616

 

OS X

 

iOS

 

NOTE: This isn’t an issue for just TArc though, it is an issue with the GPU canvas. In previous versions, you could disable the usage of the GPU canvas, however with XE6 – the option to switch it off has been deprecated.

 

Zoomed view under iOS

 

Report No: 126186 (RAID: 52756) Status: Open
[Retina] [iOS] TArc doesn’t rendering nicely on a Retina device
http://qc.embarcadero.com/wc/qcmain.aspx?d=126186

 

Reported and opened. Hopefully in the next release, this rendering issue is addressed.

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

FireMonkey Style – TStyleTag

There appears to be an oversight by Embarcadero with the TStyleTag component not being registered for use.

TStyleTag is a handy little component that allows you to add it to a style definition and have it store an Integer, Float or String value. This is used in some of the default styles, one place being the TTrackBar where it is used to hold the default thumb size of the track item.

This allows you to retrieve the thumb size depending on the default style (Windows or Mac) and adjust your user interface depending on the value.

TStyleTag Component

I’ve created a package that your can compile and install into the IDE to register this component.

Download Package

The component doesn’t have to only be used in a style definition, you can drop it directly on your form if you have a use for its functionality.

NOTE: While the component is used in the default Windows and Mac styles, it doesn’t seem to be used in the other FireMonkey styles. Although I did only look in a couple of them. Perhaps something that will be address in an update.

 

 

FireMonkey – iOS styles (problems and solutions)

In this post we’ll discover and solve a couple of glitches in the default styles supplied in FireMonkey for iOS.

  • Create a new FireMonkey iOS HD application.
  • Drop a button on the form and assign an OnClick handler.
  • In the OnClick handler, add code to display a simple message.
    • i.e. ShowMessage(‘You tapped me!’);
  • Run the application (you can do this on windows without going to XCode for this post)
  • Tap/Click on the button

Problem 1 – The default TButton style doesn’t look different when it has been pressed.

To fix this issue stopped the application and perform the following steps.

Right click on the TButton component and select the Edit Default Style… command at the bottom of the context menu.

The Style Designer will load and since we are updating the Default Style, will have the default style name for TButton, which is Buttonstyle.

The style consists of a TLayout, three TRectangle and a TText control.

To rectify the pressed state, grab a TInnerGlowEffect component from the Tool Palette.

Drag the TInnerGlowEffect component from the Tool Palette and drop it on the background TRectangle control.

With the TInnerGlowEffect component selected, you can see the available properties for the control.

Change the GlowColor property to better suit this situation, I chose Black.
Set the Enabled property to False. This is because we only want this effect enabled when the button is being pressed.
In the Trigger property, enter the following “IsPressed=true“. This means then whenever the IsPressed property is true, this effect will become enabled.

After your changes, you object inspector should look like the capture below.

Run the application now and hopefully you will see the effect display when the button is pressed.

Drop a couple of TCornerButton controls on the same application. Set the XRadius and YRadius values to 10 for one of the corner buttons. The sample application should now look something like the capture below.

Problem 2 – Under side of corner button looks strange when XRadius and YRadius values are not 3.

Below is a close up to better display the issue, it is really noticeable if your background is lighter.

To discover what is occuring with this style issue, we need to inspect the source code. Most importantly where the style information is loaded by the corner button control. In FireMonkey style properties should be modified in the ApplyStyle or ApplyStyleLookup methods. The ApplyStyle method for the TCustomCornerButton control is below, seems like everything should work.

Lets take a look at the default style for a TCornerButton.

Hmm, the source code referenced a StyleName called ‘secondbackground‘ button second background has been been set. The TRectangle without a name next to it should be called ‘secondbackground‘.

Use the object inspector to set the StyleName of the un-style-named TRectangle to ‘secondbackground‘.

Successful renaming is identified by your style, looking like the style in the capture below. Don’t set the name property by accident. You can’t set the name property to anything for a style (and have it persist).

Now when you view the form the corner button style won’t looked odd.

Problem 3 – The cornerbutton doesn’t show any effect when it is pressed.

Note how there is no TInnerGlowEffect define for the style. Follow the same steps above for the TButton and add the same effect (with the same property values) to the corner button style. Parent the TInnerGlowEffect to the background TRectangle control.

Problem 4 – The corner button has a TGlowEffect.

Kind of useless in a mobile application which doesn’t have a tab key to navigate between controls! Delete the TGlowEffect component from the style by clicking on the X.

Now you can rebuild and deploy to XCode and run on iOS to make sure these changes work well. Here is a capture from a real iPad (really it is!).

Default Styles

When in the Style Designer you can use the Load Default button to load all of the style details for the applications default style. This allows you to inspect other styles to learn from them. After making changes, I recommend you save the modified style to disk for safe keeping.

I would attach the modified style file to this post for download, but I’m not sure if I’m allowed to.

 

I’ve been doing a quite a bit of work with FireMonkey styles lately trying to make a nice TListBoxItem style. You may hear more about this in the future…

 

 

SelectDirectory for OS X

I was creating an app for FireMonkey and needed to be able to select a directory, currently there is no method (that I could see) to do this. Here is what I used:

 

function SelectDirectory(const ATitle: string; var ADir: string): Boolean;
var
  LOpenDir: NSOpenPanel;
  LInitialDir: NSURL;
  LDlgResult: NSInteger;
begin
  Result := False;
  LOpenDir := TNSOpenPanel.Wrap(TNSOpenPanel.OCClass.openPanel);
  LOpenDir.setAllowsMultipleSelection(False);
  LOpenDir.setCanChooseFiles(False);
  LOpenDir.setCanChooseDirectories(True);
  if ADir <> '' then
  begin
    LInitialDir := TNSURL.Create;
    LInitialDir.initFileURLWithPath(NSSTR(ADir));
    LOpenDir.setDirectoryURL(LInitialDir);
  end;
  if ATitle <> '' then
    LOpenDir.setTitle(NSSTR(ATitle));
  LOpenDir.retain;
  try
    LDlgResult := LOpenDir.runModal;
    if LDlgResult = NSOKButton then
    begin
      ADir := string(TNSUrl.Wrap(LOpenDir.URLs.objectAtIndex(0)).relativePath.UTF8String);
      Result := True;
    end;
  finally
    LOpenDir.release;
  end;
end;