Quantcast
Channel: Delphi Haven
Viewing all 62 articles
Browse latest View live

I’m speechless

$
0
0

Back last July, I blogged about terrible example code posted by Stephen Ball, an Embarcadero ‘Product Evangelist’. Ultimately, the critique of Ball’s code was really just a lead-off for pointing out how the same anti-pattern used had appeared prominently in the FMX source too. Happily, XE3 RTM saw most of that removed (though not all of it). However, for reasons I don’t understand, Ball has now proudly turned his blog post into a YouTube video:

Honestly, view it and weep. I expect his defence will be ‘but I’m only illustrating class helpers’, but if so, that would be dubious given he’s already been warned the example makes him look foolish [on his original post, the automated pingback from my blog was accepted, but my actual comment - 'I’ve just posted a critical (but friendly) commentary here' - never got past the moderation queue. His reply implied he still read it though]. Moreover, it’s perfectly possible to demonstrate class helpers without writing rubbish – check out the relevant page on Lachlan Gemmell’s TIndex for examples. (*)

That said, the class helper anti-example wasn’t the first time Ball had put out poor code – if you want something just as bad, check out his ‘white paper’ on packaging a FMX form into a DLL or dylib, which was also something he originally put out in the XE2 timeframe and has now recently re-promoted. The example used in it is an image file picker, which is fair enough, but here’s how he writes his exports:

  function SelectPicture(AFolder : PChar): PChar; cdecl;
  var
    ResultStr : string;
    Temp: PWideChar;
  begin
    ResultStr := '';
    try
      ResultStr := TfrmImages.SelectPicture(AFolder);
    finally
      Result := StrAlloc(Length(ResultStr));
      Temp := Addr(ResultStr[1]);
      StrCopy(Result,Temp);
    end;
  end;

  procedure DisposePicture(SelectPictureResult : PChar); cdecl;
  begin
    StrDispose(SelectPictureResult);
  end;

If that doesn’t embody the mentality of ‘It compiles, so ship it!’, I don’t know what does.

(*) PS – the David Glassborow articles linked to on the TIndex are now found here and here – I’ve posted the corrections to the TIndex blog, so hopefully the links might even be fixed by the time you read this post.

PPS – eh?



Review: Getting Started with Lazarus IDE by Roderick Person

$
0
0

Recently I came across a new Lazarus book, Getting Started with Lazarus IDE, by Roderick Person (that’s not a typo by the way – there is no definite article in the title). This is a small book (only a hundred pages) put out by Packt, a small publisher with a growing catalogue. While you can purchase just the ebook, I bought the printed/ebook package from the Packt website, which still only cost £15.

The book is presented as being for Delphi programmers interested in Lazarus in the first instance, and any other developers used to RAD environments in the second. As such, it begins by walking through how to install Lazarus on Linux, Windows, OS X and FreeBSD. Following this, the next thirty-five pages or so (up until half way) are taken up by a quick tour of the IDE itself, brief descriptions of the core components of the Lazarus Component Library (LCL), discussions of how to create ‘hello world’ console and GUI applications, an overview of the Lazarus debugger, and an introduction to the IDE’s rename refactoring.

While nicely written, I found this section a little uneven – words are spent on introducing the Object Inspector, for instance, when this is something that works identically to the Delphi version. Conversely, the Project Inspector – Lazarus’s substitute for what on the face of it is Delphi’s rather more fully featured Project Manager – is not mentioned at all, despite actually appearing in a screenshot.

As an aside, while this is no reflection on the author, I did find Person’s description of how to create a Lazarus console application remarkable given how much code was needed simply to output ‘Hello World’. While good ol’

program HelloWorld;

begin
  WriteLn('Hello world');
end.

is still possible, the IDE defaults to bringing in a rather overwrought console application framework. This leads Person having to devote six pages to walking though how to write a console-based ‘Hello World’ followed by less than two for the GUI version!

Once the book gets to half way, chapter 4 tackles the topic of how to convert a Delphi/VCL application to Lazarus and the LCL. I found this chapter very well pitched, with many useful tips presented. Following it, chapter 5 provides an introduction to Lazarus custom component writing. I confess I came out of this being slightly amazed at how the Lazarus team have managed to keep their photocopiers working even when it comes to core parts of Delphi’s Open Tools API! The final two chapters of the book then provide a valuable overview of the Lazarus Documentation Editor (LazDE), followed by a somewhat less obviously useful account of rebuilding the Lazarus IDE to use the GDK+ widgetset on Windows rather than native Win32 controls.

Overall, I found the book easy to read, and with two very solid chapters (4 and 6). At other times I found it a little inconsistent in its focus however – in particular, while the best chapters were clearly aimed towards the book’s stated primary audience, namely Delphi developers, others seemed to assume very little prior Delphi knowledge. Because of that, parts of the book can cover things that most experienced Delphi developers will know off of the back of their hands. A related issue is that the author doesn’t appear to have used any modern version of Delphi. For example, when first presenting the Lazarus IDE, a comment is made about how similar it looks to the Delphi IDE – which is true, but only if you are using Delphi 7 or earlier. Likewise, mention is made when discusing 64 bit compatibility that the PtrInt/PtrUInt types should be used instead of Integer when you need to typecast from a pointer to an integer or vice versa. While that’s good advice in itself, Person doesn’t seem to be aware that Delphi defines NativeInt/NativeUInt types for the same purpose, and has done so for quite a few versions now.

Nonetheless, this shouldn’t detract from the fact that when the book maintains a clear focus, it presents its material well. Also, while it is small, it is also cheap, so if you’re interested in Lazarus, check it out.


Delphi XE4 pricing

$
0
0

It’s not exactly a ‘leak’ given it comes from an official promo video, albeit in Russian, but anyhow, the US pricing for Delphi XE4 has come out:

Screen Shot 2013-04-18 at 22.00.44

See here (for some reason, the video couldn’t show on Firefox, my usual browser, but it ran fine in Safari). In a nutshell, the XE4 upgrade will be a ‘nominal’ $49 for XE3 Professional edition owners uninterested in iOS support, or $499 if they are. In contrast, the upgrade cost from XE3 for Enterprise and above will be a flat $499. Slightly disappointingly (well, in my view at least), nothing clever has been done with the Starter edition – that remains Win32 only.

Does $499 (I shan’t guess the exchange rate for outside the US) fulfil the promise of iOS support being a ‘low cost add-on’ for XE3 Professional owners? Probably not, and while I’d prefer it lower, I’m not sure it’s particularly expensive for the market either:

  • RemObjects are currently running a ‘limited offer’ promotion for getting Oxygene for Windows, OS X, iOS and Android for $499. While this is more platforms than Delphi, at least for now, you’re only getting a vaguely Delphi-like language – ignoring the lack of a shared visual component library (which RemObjects are no doubt quite sincere in dismissing the very idea of), there’s no shared RTL either, at least at the level of Delphi’s, let alone .NET’s.
  • Xamarin sell their eponymous Mono-based tools for $299 per platform for ‘indie’ developers, and $999 per platform per seat for ‘business’ customers. Since only the ‘business’ (or beyond that, ‘enterprise’) edition comes with Visual Studio integration, if you want that, you’re looking at $2,697.30 (!) for all supported platforms (this includes a multiplatform discount). On the other hand, unlike Oxygene, you are buying a commercial licence for not just a language (C# in Xamarin’s case – and a very fine language it is too), but an extensive RTL (i.e., their implementation of the .NET BCL) and a dedicated IDE, which Xamarin ships alongside (or in the case of the ‘indie’ edition, instead of) its VS integration.

Of course, beyond pricing, there is the quality issue to consider too. Put briefly, it’s all very well promising the moon, but if you end up delivering a trip to a boggy field instead, the original promise will backfire. Time will tell…

[PS - I'd forgotten about Software Assurance when I wrote this post (silly!). I've no idea how that will be handled. Pure speculation, but maybe XE3 Professional users on SA will get iOS support, but with higher renewal charges kicking in if they want to keep it for later releases...?]

[PPS - Joylon Smith has posted further comments on his blog (link). As there's no point in fragmenting the discussion, please feel free to comment there, or the thread I started on Embarcadero's Delphi Non-Technical forum (link), rather than here.]


XE4 documentation is up

FMX anti-pattern: returning nil rather than raising an exception on an invalid state or arguments

$
0
0

In the Delphi RTL and VCL, a method called LoadFromFile or the like will raise an exception if the file doesn’t exist or is invalid. Similarly, if an object property getter implements a lazy-loading pattern (meaning, the value of the property will only be initialised when first needed), it will raise an exception if the situation is such that nothing valid can be loaded or initialised. This is pretty basic exception programming – when an error arises, the flow of the calling code is forcibly interupted with a clear account of what call was invalid. Imagine if (say) TBitmap.LoadFromFile didn’t raise an exception when the file name passed to it didn’t refer to a valid bitmap file – calling code could go happily on its way assuming a graphic has been loaded when it hasn’t. Likewise, what if the Items property getter for TList<T> just returned nil (or equivalent) with an out of bounds index? If T is a class type, then off-by-one errors in the calling code will end up with cryptic access violations later on that might be nowhere near where the erroneous call was actually made!

Alas, but such basic understanding of exception programming is not grasped by at least one developer on the FMX team. Repeatedly across the XE2, XE3 and now (I learn) XE4 releases, methods that an experienced Delphi programmer would expect to raise an exception when nothing valid can be returned do not. As soon as one case is fixed – e.g., the TFmxObject.Children property getter was done so in XE3, albeit implicitly – another one or two are added, and frankly, it needs to stop now. The latest cases I learn are the HScrollBar and VScrollBar property getters on TScrollBox – done properly in XE3, the anti-pattern has however been introduced in XE4, causing ‘random’ access violations in TMemo (the FMX TMemo inherits from TScrollBox).


Arnaud Bouchez on the ‘nextgen’ compiler

$
0
0

Arnaud Bouchez of Synopse open source fame (mORmot etc.) has written an interesting piece on the ‘nextgen’ compiler that debuted with XE4′s iOS support – check it out.


TMS releases iCL – native iOS control toolkit for XE4

$
0
0

Now this looks interesting – TMS have just released iCL, a native iOS control toolkit for Delphi XE4 (link). Given I’ve only just seen the news I don’t have much more to say, however it definitely looks like a product to check out if you’re interested in writing iOS apps using Delphi.


Programmatically shutting down, restarting, sleeping or logging off on OS X

$
0
0

Browsing StackOverflow, I came across a question asking how to programmatically shut down the computer in Delphi when targeting OS X. Mysteriously, the question has been met with four downvotes as I write this, leaving it ‘on hold as off-topic’ until the darstardly questioner stops thinking a programmer’s Q&A site is a proper place for programming questions or something.

Anyhow, with respect to the question, one easy way to do the deed is to use a Cocoa NSAppleScript object to run the following piece of AppleScript:

tell application "Finder" to shut down

As desired, ‘to shut down’ can be replaced with ‘to restart’, ‘to sleep’ or ‘to log out’.

Now in Delphi, NSAppleScript (or more exactly, a wrapper interface type for NSAppleScript) is declared in the Macapi.Foundation unit. Alas, but this is misdeclared, or at least was when I last looked (see here – if someone wants to confirm this is still the case in the latest and greatest, please do in the comments). As such, you need to fix the declaration before using it. On the other hand, fixing it is easy:

uses
  Macapi.ObjectiveC, Macapi.CocoaTypes, Macapi.Foundation;

type
  NSAppleScript = interface(NSObject)
    ['{0AB1D902-25CE-4F0B-A3BE-C4ABEDEB88BC}']
    function compileAndReturnError(errorInfo: Pointer): Boolean; cdecl;
    function executeAndReturnError(errorInfo: Pointer): Pointer; cdecl;
    function executeAppleEvent(event: NSAppleEventDescriptor; error: Pointer): Pointer; cdecl;
    function initWithContentsOfURL(url: NSURL; error: Pointer): Pointer; cdecl;
    function initWithSource(source: NSString): Pointer; cdecl;
    function isCompiled: Boolean; cdecl;
    function source: NSString; cdecl;
  end;
  TNSAppleScript = class(TOCGenericImport<NSAppleScriptClass, NSAppleScript>)  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Script: NSAppleScript;
  Error: Pointer;
begin
  Error := nil;
  Script := TNSAppleScript.Wrap(TNSAppleScript.Alloc.initWithSource(
    NSSTR('tell application "Finder" to shut down')));
  try
    if Script.executeAndReturnError(Error) = nil then
      raise EOSError.Create('AppleScript macro failed');
  finally
    Script.release;
  end;
end;


Don’t expect the Parent property to be set inside an FMX control’s Paint method

$
0
0

So… I’m working on a little custom FireMonkey control. As in the VCL, if you want to handle drawing a custom control yourself, you need to override its Paint method. In my case the override was looking like this:

procedure TMyChildControl.Paint;
begin
  if (Parent <> nil) and (Parent.Parent is TMyOtherControl) then
    Canvas.Font.Assign(TMyOtherControl(Parent.Parent).Font);
  Canvas.FillText(LocalRect, SomeText, False, 1, [], TTextAlign.taCenter);
end;

If you’re wondering, this was partly a workaround for the fact FMX doesn’t have a ‘parent font’ concept like the VCL. Anyhow, the code seemed to work fine until I enabled drag and drop. In FireMonkey, the drag image for an internal drag and drop operation is dynamically created from the image of the dragged control (good), but in my case, the drag image wasn’t being drawn with the proper font set. Digging through the source I discovered the reason: BeginAutoDrag calls MakeScreenshot to generate the drag image, which calls PaintTo… whose final parameter is a parent property override that defaults to nil. As such, while my Paint override was being called, the Parent property was returning nil inside of it. Argh…


The little things…

$
0
0

Honestly, for how many versions now has the following got through?

unit FMX.Types;

//...

type
  TGradientPoint = class(TCollectionItem)
  private
    FColor: TAlphaColor;
    FOffset: Single;
    function GetColor: TAlphaColor;
    procedure SetColor(const Value: TAlphaColor);
  public
    procedure Assign(Source: TPersistent); override;
    property IntColor: TAlphaColor read FColor write FColor;
  published
    property Color: TAlphaColor read GetColor write SetColor;
    property Offset: Single read FOffset write FOffset nodefault;
  end;

//...

procedure TGradientPoint.Assign(Source: TPersistent);
begin
  if Source is TGradientPoint then
  begin
    FColor := TGradientPoint(Source).FColor;
    FOffset := TGradientPoint(Source).FOffset;
  end
  else
    inherited;
end;

function TGradientPoint.GetColor: TAlphaColor;
begin
  Result := FColor;
end;

procedure TGradientPoint.SetColor(const Value: TAlphaColor);
begin
  FColor := Value;
end;

What am I whinging about you say? This:

  1. What’s with the weird IntColor/Color duplication? Probably an historical thing… but why wasn’t the IntColor version taken out when the Color version was refactored?
  2. Why does Color have a getter that just directly returns the value of the backing field?
  3. Why doesn’t its setter (and Assign) call the Changed method?
  4. Where’s the Add method for TGradientPoints to cast the TCollection implementation’s result to TGradientPoint?
  5. Where’s the Update override for TGradientPoints? We want a property change to make a visible difference, right?

Oh, and don’t get me started on how the TGradient property editor is both horrible to use and set up to replace (not complement) what would have been perfectly reasonable default Object Inspector behaviour…


How to have a blue FMX TPanel, redux

$
0
0

Back in the XE2 timeframe, I published a short post about giving a TPanel a custom colour. While the solution still works in XE4, give or take an extra unit in the uses clause or use of the TAlphaColors.ColorName syntax rather than claColorName, nowadays I’d emphasise the first part of my original advice. This was quite simple – don’t try and give a TPanel a custom colour in the first place! If you want an irregularly styled TPanel, use a TRectangle instead, whose colours you can customise to your heart’s content:

  1. Add a TRectangle to the form.
  2. Change Stroke.Color to Gray.
  3. Change Fill.Color to whatever you want.
  4. At runtime, change the colour with code like this:
MyRectangle.Fill.Color := TAlphaColors.Blue;

Annoyingly, and dating from the infamous XE2 update 4, TRectangle is made to pretend it can’t parent other controls at designtime. As such, when a TRectangle is selected and you double click on a control type in the Tool Palette, the new control is added parented to the rectangle’s parent, not the rectangle itself. However, you can use the Structure pane top left to reparent controls as you wish.

As an aside, this highlights a key difference between FMX and the VCL: in FMX, all controls can potentially be parents to any other. Once you grasp this point, obsessing over wanting to use a TPanel specifically, even though you don’t want the normal TPanel ‘look’, is completely illogical.


My FMX TClipboard and TMacPreferencesIniFile implementations now compiling in XE4

$
0
0

A bit belatedly, but my FireMonkey TClipboard and TMacPreferencesIniFile implementations are now compiling with XE4 (thanks goes to Ken Schafer for prodding me in the case of the former). See my post from nearly a year ago for the details:

http://delphihaven.wordpress.com/2012/07/27/fmx-tclipboard-and-tmacpreferencesinifile/

With respect to TClipboard, I’ve also done the following:

  • Added a cfPNG TClipboardFormat identifier (this is the same as cfBitmap on OS X).
  • Fixed a bad assumption about bitmap pitches on Windows that was causing issues for some people.
  • Switched to using CF_DIBV5 internally when reading or writing bitmaps on Windows.
  • When assigning to a bitmap, cfPNG is now looked for first (this makes things works better with MS Word), and correspondingly, HasFormat(cfBitmap) now also checks for cfPNG as a special case.
  • On Windows again, when a source bitmap includes transparency, the outputted DIB has this transparency removed, however at the same time a PNG representation is added that maintains the original alpha channel.
  • For both Windows and OS X, added GetFormats and GetFormatName methods. Where the former returns an array of TClipboardFormat, the latter converts a TClipboardFormat to a string:
procedure TfrmClipboardDemo.btnListClick(Sender: TObject);
var
  Format: TClipboardFormat;
  S: string;
begin
  for Format in Clipboard.GetFormats do
    S := S + sLineBreak + Clipboard.GetFormatName(Format);
  if S = '' then
    S := 'Nothing is currently on the clipboard.'
  else
    S := 'The following formats are on the clipboard:' + S;
  MessageDlg(S, TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0)
end;

This is the result when I copy a word on a webpage in Safari:

One word copied from Safari

As this example hints at, it’s better to use Apple’s ‘UTI’ format for custom clipboard identifiers on OS X (e.g. com.mycompany.formatname, though ultimately just something.something, or something.something.something, etc.). While things still work if you don’t, failing to do so will lead the OS to allocate a parallel ‘dynamic’ UTI for you (e.g. dyn.a1oad0fg1bber13hthat600s0nand0nand0n). As such, I’ve tweaked the demo accordingly – click the ‘Copy as Custom Clipboard Format’ button then the ‘List’ one, and you get this:

Custom format list


FMX TClipboard now supports iOS

$
0
0

I’ve just checked in a revision of my open source FMX TClipboard implementation that has an iOS backend. This supports the current rather than the FPC-based version of ‘Delphi for iOS’, however the Windows and OS X backends still compile with XE2 and above.

In essence, the new code wraps the native iOS clipboard API (UIPasteboard) and presents it in a fashion that closely follows the VCL clipboard interface, just like the existing Windows and OS X support did the same for desktop platforms. For some reason Apple in their wisdom decided to make the iOS clipboard API similar yet randomly different to the OS X one, so even though there’s not masses of code, it was a bit fiddly to implement. Anyhow, I’ve also knocked out a little demo similar to the previous desktop one:

TiOSClipboard demo

Using (say) Photos, you can copy an image to the clipboard and paste it into the demo. Conversely, you can from the demo itself copy either just the text entered, just the image, both the text and the image as two representations of the same clipboard item, or both the text and image as a custom clipboard format. The ‘List Formats on Clipboard’ button, as its name implies, then lists the formats currently on the clipboard. This is what I get after copying an image from Photos on the iOS simulator:

Format List

Technically, the iOS clipboard, like the OS X one, can have multiple items, each with multiple representations. Since the multiple-item concept doesn’t exist on Windows (indeed, it didn’t exist on OS X originally either), my class is only concerned with the first item, which is what most applications only bother with anyhow.

If you want the code, the SVN URL for it and a few other pieces is the following:

http://delphi-foundations.googlecode.com/svn/trunk/FMX%20Utilities/

The core files are now CCR.FMXClipboard.pas, CCR.FMXClipboard.Apple.pasCCR.FMXClipboard.iOS.pas, CCR.FMXClipboard.Mac.pas and CCR.FMXClipboard.Win.pas, and together they stand alone.


A few XE5-related bits

$
0
0

With XE5 now out, I’ve updated my FMX TClipboard and Mac PDF writer code to compile with it, and in the case of the latter, also made an unrelated fix suggested by Sebastian Zierer. With respect to TClipboard, I must add the caveat that the Android implementation merely delegates to the standard, frankly half-arsed IFMXClipboardService, and as such, only supports text. This is because my investigations into the Android API suggest there is no standard way to exchange pictures over the Android clipboard – if anyone knows something to the contrary, by all means let me know in the comments though. Anyhow, if you want to browse the code, check it out here:

http://code.google.com/p/delphi-foundations/source/browse/#svn%2Ftrunk%2FFMX%20Utilities

Alternatively, the SVN link is http://delphi-foundations.googlecode.com/svn/trunk/FMX%20Utilities/

I have also just posted to the same place a TCustomIniFile descendant that delegates to the Android SharedPreferences API, which I have unimaginatively called TAndroidPreferencesIniFile (the unit is CCR.PrefsIniFile.Android.pas). This class corresponds to the TMacPreferencesIniFile TApplePreferencesIniFile class I had written previously, which sits on top of the CFPreferences API on OS X and iOS. The idea in both cases is to have classes that roughly correspond to TRegistryIniFile on Windows, providing a consistent interface over whatever is the native preferences store for the platform.

Be warned that unlike TIniFile (but like TApplePreferencesIniFile), TAndroidPreferencesIniFile is case sensitive with respect to section and key names. Further, there’s also a slightly annoying issue in which each time an app is deloyed by the IDE, existing preference data gets wiped in the process. Given when you run an Android app from the IDE you see a message saying it is uninstalling the previous version of the app, I imagine the preference wiping is just a function of that.

Anyhow, accompanying the main unit is a small (and very crude!) demo designed for a 7″ tablet (a Nook HD in my case):

TAndroidPreferencesIniFile demo

The demo works best if you write a series of values first, given the third edit box doubles as both the entry field for values to write and default values to read.

Update (17/9/13): originally the unit was called CCR.Android.PrefsIniFile.pas; it’s now CCR.PrefsIniFile.Android.pas for consistency with the naming pattern used by my TClipboard units. I’ve also now added a ‘mobile’ version of the demo which uses TAndroidPreferencesIniFile when targeting Android, TApplePreferencesIniFile when targeting iOS, and TRegistryIniFile when targeting Windows (Delphi provides the Windows option in a mobile project to allow ‘quick and dirty’ debugging).


FMX issue – inability to safely process paint messages immediately

$
0
0

There are times when you want to repaint a control immediately, typically to give an indication to the user that something is actually happening during a lengthy bit of processing. In the VCL, this is covered by a troika of methods on TControl:

  • Invalidate posts a message to the event queue saying the control needs to be repainted.
  • Update forces processing of any paint messages pending for the control.
  • Repaint calls Invalidate and Update in succession.

That said, sometimes it isn’t even necessary to call one of these methods if the control wraps a native API widget, and the native API does something special behind the scenes. For example, try this:

  1. Create a new VCL application
  2. Add a TButton and a TProgressBar to the form
  3. Add the following handler for the button’s OnClick event:
uses
  System.Diagnostics;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  Stopwatch: TStopwatch;
begin
  ProgressBar1.Position := ProgressBar1.Min;
  for I := ProgressBar1.Min + 1 to ProgressBar1.Max do
  begin
    Stopwatch := TStopwatch.StartNew;
    repeat
    until Stopwatch.ElapsedMilliseconds >= 50;
    ProgressBar1.Position := I;
  end;
end;

When I run this code, I find the progress bar still updating, if not entirely smoothly. Adding an Update call fixes that:

    ProgressBar1.Position := I;
    ProgressBar1.Update; //!!!added
  end;
end;

Turn to FireMonkey however, and this core functionality is missing:

  • While the FMX TControl has a Repaint method, it corresponds to the VCL Invalidate, not the VCL Repaint. (Oddly, the FMX TControl also has a InvalidateRect method to invalidate just a certain area of the control; given that, why confuse matters with the Repaint naming…?)
  • Since FMX to a large extent reimplements native widgets instead of wrapping them like the VCL prefers to do, niceties like the VCL progress bar doing its own updating don’t happen with the FMX equivalent.

To see this, try reimplementing the VCL demo presented above. In doing so the event handler will need to be tweaked a bit, mainly because the FMX TProgressBar uses Single not Integer values for its progress values:

uses
  System.Diagnostics;

procedure TForm2.Button1Click(Sender: TObject);
var
  Stopwatch: TStopwatch;
begin
  ProgressBar1.Value := ProgressBar1.Min;
  while ProgressBar1.Value < ProgressBar1.Max do
  begin
    Stopwatch := TStopwatch.StartNew;
    repeat
    until Stopwatch.ElapsedMilliseconds >= 50;
    ProgressBar1.Value := ProgressBar1.Value + 1;
  end;
end;

Run it, and you will find the progress bar visually updates in one big leap at the end. Adding a Repaint call doesn’t help:

    ProgressBar1.Value := ProgressBar1.Value + 1;
    ProgressBar1.Repaint; //!!!added
  end;
end;

What does ‘work’ is adding a call to Application.ProcessMessages instead. Is this a good idea however? Hardly: Application.ProcessMessages, as its name implies, processes all messages currently outstanding (paint and otherwise) for all controls (and if there are any other message-handling primitives in the application, those too), which can soon lead to serious re-entrancy issues.

Nonetheless, on Windows it is possible to process paint messages for a FMX form using something like the following code:

uses
  WinApi.Windows, FMX.Platform.Win;

procedure ProcessPaintMessages(const Form: TCustomForm);
begin
  UpdateWindow(WindowHandleToPlatform(Form.Handle).Wnd);
end;

This is XE4/5; for XE3 use the following (a stable API isn’t one of FMX’s strengths!):

uses
  WinApi.Windows, FMX.Platform.Win;

procedure ProcessPaintMessages(const Form: TCustomForm);
begin
  UpdateWindow(FmxHandleToHWND(Form.Handle));
end;

You have to do things at the form level since a FMX control on Windows isn’t backed by its own HWND. Further, on OS X, trying something similar appears to confuse the FMX styling system, so all in all… the situation isn’t great. For a recent QC report on the matter, see here: http://qc.embarcadero.com/wc/qcmain.aspx?d=119083. Unfortunately the person who mans QC hasn’t understood the point of it, and I wouldn’t be surprised if there are other similar reports on the system.



Pearls in the ‘nextgen’-ified RTL source

$
0
0

Back in September 2012, a post appeared in non-tech reporting Delphi’s PCRE wrapper to be many, many times slower than Python’s. With sample code attached, the problem was undeniable, though the immediate cause was soon identified: the Delphi wrapper’s failure to include a ‘don’t validate the UTF8′ flag (PCRE was traditionally a UTF-8 based library, so the Delphi wrapper was using UTF8String). Putting everyone’s work together, I posted a QC report. Soon after doing that an even better solution was noted, which was for the wrapper to wrap a newer version of PCRE that supported UTF-16 internally, i.e. Delphi’s native string encoding, and so allow avoiding UTF-8 roundtrips entirely.

To be fair to Embarcadero, the second solution might have been considered a bit problematic in practice, given PCRE’s UTF-16 mode was only 6 months old at that point, and using it may have been tricky for OS X. This is because on that platform, the Delphi wrapper uses the system PCRE dylib rather than statically linking equivalent C object files, due to the fact DCCOSX only consumes object files produced by the Windows C++Builder compiler (or at least, only did when I last looked into the matter). On the other hand, the additional flag fix involves adding just a couple of lines… so perhaps it could be implemented fairly quickly?

Alas, but it isn’t been implemented as yet. Oh well – I can see shipping iOS and Android support were much bigger fish to fry. Does this mean the unit in question hasn’t been touched at all? Oh no: it has been extensively fiddled about with due to the fact the UTF8String type was removed from the so-called ‘nextgen’ (i.e., LLVM-based) compilers. As such, an elegant UTF8String interface has been replaced with an ordinary string one that now has to use the ugly ‘marshaller’ API and TBytes internally. Even worse, it now includes pearls like the following:

function CopyBytes(const S: TBytes; Index, Count: Integer): TBytes;
var
  Len, I: Integer;
begin
  Len := Length(S);
  if Len = 0 then
    Result := TEncoding.UTF8.GetBytes('')
  else
  begin
    if Index < 0 then Index := 0
    else if Index > Len then Count := 0;
    Len := Len - Index;
    if Count <= 0 then
      Result := TEncoding.UTF8.GetBytes('')
    else
    begin
      if Count > Len then Count := Len;
      SetLength(Result, Count);
      for I := 0 to Count - 1 do
        Result[I] := S[Index + I];
    end;
  end;
end;

If you’re reading this and thinking ‘oh no – it looks like the Move procedure has been removed!’, don’t worry, because it hasn’t. Likewise, Delphi hasn’t suddenly gone all Java-esque and dropped the equation of an empty dynamic array with a nil one – i.e., this code:

    Result := TEncoding.UTF8.GetBytes('')

really is what it seems, namely an obscure way of assigning nil that if you step through it, passes through several method calls and IF tests to do the deed.


StackOverflown

$
0
0

So… I was finally broken: I created a StackOverflow account. Annoyingly, a certain individual in particular is waaaayy too fast in answering most Delphi questions (genuine expertise + quick on the button = cheating, surely?)… so I started picking off Access ones instead (easy points there – write three lines of trivial SQL, and bingo). Alas, but even for a subject area in which one would expect to find a fair few novices asking novice questions, there remains a certain… priggishness about the fact.

Cartman

(Actual picture of self-appointed SO prefect moderator.)

 

[PS: to the literal-minded, no I don't actually think David is a 'cheat' any more than I think Eric Cartman actually patrols StackOverflow ;-) ]

New Delphi blog – Dave Nottage’s Delphi Worlds

$
0
0

I’ve just realised Dave Nottage (TeamB, and now MVP) has a blog, currently focusing on Delphi for iOS – hopefully it will get syndicated by DelphiFeeds.com soon, but until then, check it out directly.


TAndroidPreferencesIniFile fix

$
0
0

Just a small note to say, if anyone has downloaded my Android SharedPreferences wrapper (more info here), I’ve just put up a small fix for the ReadBool method, so you might want to update from the trunk. Thanks goes to Orren Grushkin for reporting the bug.


FMX/XE5 issue: new ShowModal overload poorly implemented

$
0
0

A few weeks ago Marco Cantù, the Delphi Project Manager, blogged about a new FMX ShowModal overload introduced in XE5. As Marco explained, the reason for this new overload is because a Windows-style ShowModal isn’t possible on mobile platforms. While his blog post met the odd objection in the comments, if anything, a revised ShowModal could have been added from the start – due to deliberate design decisions by Apple, the classic ShowModal isn’t entirely straightforward even on OS X, so Embarcadero have my sympathies.

That said, I’ve found the actual implementation of the new overload problematic. On Windows and OS X, it fails to show the form modally at all (see QC 120024). Moreover, on all platforms, setting the shown form’s ModalResult property (either directly or by setting a button’s ModalResult property) does not close the form like it would do in the classic ShowModal style (QC 120025). To fix these issues, I would suggest Embarcadero do something like the following:

1. Move the ShowWindowModal method from IFMXWindowService into its own IFMXModalWindowService interface. If a platform doesn’t implement classic ShowModal functionality, then it shouldn’t implement the new interface.

2. Change the new ShowModal overload’s implementation to call the classic ShowModal if it is supported:

procedure TCommonCustomForm.ShowModal(const ResultProc: TProc<TModalResult>);
begin
  if Supports(FWinService, IFMXModalWindowService) then
    ResultProc(ShowModal)
  else
  begin
    FResultProc := ResultProc;
    Show;
  end;
end;

3. Amend TCommonCustomForm.SetModalResult to look something like this:

procedure TCommonCustomForm.SetModalResult(Value: TModalResult);
begin
  FModalResult := Value;
  if Assigned(FResultProc) then
  begin
    Close; //!!!added
    FResultProc(FModalResult);
    FResultProc := nil;
  end;
end;

4. Raise an exception in the classic ShowModal’s method body if IFMXModalWindowService isn’t implemented.


Viewing all 62 articles
Browse latest View live