-
Content Count
266 -
Joined
-
Last visited
-
Days Won
30
Posts posted by vfbb
-
-
14 hours ago, David Heffernan said:Why would you need to know this, unless you are rendering the text
All manipulations of strings received by user input should consider each element and not each individual character. This is basically why delphi TEdit does not work properly with these kind of emojis (just try adding one and pressing backspace that you will see part of problem). In short, when you don't consider this, you risk breaking a string into a position in the middle of a Grapheme, creating a malformed string.
As I said, this iteration is essential, Embarcadero should implement this natively to work cross-platform just like C # did.
The best thing to do at this point is to port the C # code.
-
Embarcadero should create a class based on C# TextElementEnumerator, which does exactly this.
-
The delphi string is an implementation of UTF-16 in which the "normal" graphic characters corresponding to 1 Char (2 bytes), but having others graphic characters being represented by 2 Chars (4 bytes), the so-called surrogate pair, (like the emoji 🙏). And in this case we need to check if the char IsSurrogate, to know that it is a graphic character of 2 Chars (4 bytes). But the problem is that utf-16 is not limited to a maximum of 4 bytes for a graphic character, in fact it already predicts that a graphic character can have up to 14 bytes for future implementations, and today most of the emojis already occupy at least 8 bytes (like the emoji 🙏🏻 <- light version). This is a problem, because delphi only handles the graphic character with the possibility of being 2 bytes or 4 bytes, and is often converting the graphic character or string to UCS4Char or UCS4String respectively, to treat everyone as being 4 bytes.
So, how do you know the actual size of each graphic character in a string? -
Apparently simple, but in practice nothing trivial, and with nothing on the internet about this, I decided to do a brief tutorial to master the Android system bars.
https://github.com/viniciusfbb/fmx_tutorials/tree/master/delphi_android_system_bars/
- 3
-
Just a tip: it is not mandatory to place the .ttf file in the project folder, you can put it in a subfolder then add that subfolder in "Search path"
-
1 hour ago, John Kouraklis said:What's the strategy to shut down a multi-thread app?
This problem is very common because without proper care the threads end in a different order than the units.
The rule to avoid this is: each unit that creates each thread has to guarantee that the thread it created will be canceled and completely terminated until the finalization of the unit.
How to do this? There are several possibilities, I will quote one:
1) Create a way to cancel each thread (it is almost always possible through a simple Boolean + TEvent.SetEvent, but each case must be evaluated)
2) Create a unique TEvent to signal the end of the thread; Just run the thread code in a try finaly FFinishEvent.SetEvent;
3) At the end of the unit, cancel the thread, and give FFinishEvent.WaitFor;- 1
-
2 hours ago, Davide Angeli said:It's hard... the project is quite complex with several runtime packages
Have you tried to see if there are any more exception or information debugging the IDE? Open some bpl then Run> Parameters... > Host Application "C:\Program Files (x86)\Embarcadero\Studio\21.0\bin\bds.exe" > Save > press F9
Then new instance of delphi will be created in debug mode, then you can reproduce the error in that delphi instance and you may have more information about the problem.
- 1
- 1
-
You can use Firebase Cloud Messaging instead. There are some examples of FCM with delphi in github.
-
On 3/6/2021 at 2:44 PM, Vitaliy said:Thank you very much for all the help, the mentoring was done by @vfbb in a very satisfactory way, and here is my recommendation.
It was a pleasure to help you 😉
-
I'm not going to talk about delphi 10.4.2 because I still have 10.4.1. But on sdk, you don't need to use this Embarcadero tool, it is almost always a version behind. Ideally, you should install Android Studio on your own, open it and Configure> SDK Manager and install (download) the latest sdk, get the directory and configure it in your Delphi Options > Deployment > SDK Manager> Android
- 3
- 1
-
Virtual interfaces: to create an object that support an interface in run time. See System.Rtti.TVirtualInterface
-
Sometimes I come across some delphi language enhancements that are undocumented or are poorly documented. In the last few years docwiki has improved a lot, but there are still undocumented cases, such as accessing an item in an array just by declaring a property, without any method:
TipGeolocation = record private FCoordinates: array[0..1] of Double; public property Latitude: Double read FCoordinates[0] write FCoordinates[0]; property Longitude: Double read FCoordinates[1] write FCoordinates[1]; end;
Share here any enhancements to the delphi language that you have discovered but are not documented.
- 4
-
Although I don't particularly like this design, you can make a descendant of TInterfacedPersistent, your class will not be arc and will be able to implement interfaces.
TBaseFrame = class(TInterfacedPersistent)
-
@Attila Kovacs You are right about compatibility, thanks for checking this out. I will put this observation. In the future I can do some backwards compatibility, but not now.
About the null values serialization, this library is just to consumes one rest api, then the serialization can occur just when you call a Post or Put and have the parameter ABody that is a record or a class or an array. In this specific case, the library will serialize the type as json, and in this case, the values that is null will be written to the json body without skip. The current implementation of System.JSON.Serializers don’t permit to ignore the fields with null values.
1 hour ago, mvanrijnen said:Ah ok, its for creating an API consumer,
(my first thought was something for declaring API's,, e.g. like in radserver (which we are using right now)).
Sorry i'll make it clearer
- 1
-
@Attila Kovacs Here one example without TValue.Make<T>, and with the nullables stating with IsNull True:
uses System.Rtti, System.TypInfo, System.JSON.Serializers, System.JSON.Readers, System.JSON.Writers, System.JSON.Types, iPub.Rtl.Refit; type TNullable<T> = record strict private FIsNotNull: Boolean; function GetIsNull: Boolean; procedure SetIsNull(AValue: Boolean); public Value: T; property IsNull: Boolean read GetIsNull write SetIsNull; end; TNullableConverter<T> = class(TJsonConverter) public procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); override; function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; override; function CanConvert(ATypeInf: PTypeInfo): Boolean; override; end; { TNullable<T> } function TNullable<T>.GetIsNull: Boolean; begin Result := not FIsNotNull; end; procedure TNullable<T>.SetIsNull(AValue: Boolean); begin FIsNotNull := not AValue; end; { TNullableConverter<T> } function TNullableConverter<T>.CanConvert(ATypeInf: PTypeInfo): Boolean; begin Result := ATypeInf = TypeInfo(TNullable<T>); end; function TNullableConverter<T>.ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; var LNullable: TNullable<T>; begin if AReader.TokenType = TJsonToken.Null then begin LNullable.IsNull := True; LNullable.Value := Default(T); end else begin LNullable.IsNull := False; LNullable.Value := AReader.Value.AsType<T>; end; TValue.Make(@LNullable, TypeInfo(TNullable<T>), Result); end; procedure TNullableConverter<T>.WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); var LNullable: TNullable<T>; LValue: TValue; begin LNullable := AValue.AsType<TNullable<T>>; if LNullable.IsNull then AWriter.WriteNull else begin TValue.Make(@LNullable.Value, TypeInfo(T), LValue); AWriter.WriteValue(LValue); end; end; initialization GRestService.RegisterConverters([TNullableConverter<string>, TNullableConverter<Byte>, TNullableConverter<Word>, TNullableConverter<Integer>, TNullableConverter<Cardinal>, TNullableConverter<Single>, TNullableConverter<Double>, TNullableConverter<Int64>, TNullableConverter<UInt64>, TNullableConverter<TDateTime>, TNullableConverter<Boolean>, TNullableConverter<Char>]); end.
To test:
TUser = record Name: TNullable<string>; Location: string; Id: Integer; Email: TNullable<string>; NonexistentField: TNullable<Integer>; end; [BaseUrl('https://api.github.com')] IGithubApi = interface(IipRestApi) ['{4C3B546F-216D-46D9-8E7D-0009C0771064}'] [Get('/users/{user}')] function GetUser(const AUser: string): TUser; end; procedure TForm1.FormCreate(Sender: TObject); var LGithubApi: IGithubApi; LUser: TUser; begin LGithubApi := GRestService.&For<IGithubApi>; LUser := LGithubApi.GetUser('viniciusfbb'); end;
-
4 minutes ago, Attila Kovacs said:-To be able to send and receive pure arrays "[]" , some php api's are working that strange way
Works perfectly in both cases, with the [] or with {..},{..},{..}. In both cases your function result should be a TArray<> of a record or a class.
8 minutes ago, Attila Kovacs said:-To be able to suppress the null values from the nullables (or a shouldmarshal callback/wallpaper)
Do you mean what happens with the nullable fields that were not received? Well, all fields are set to the default value before reading the received json, so there are several possible workarounds to ensure that the field not received is considered null. In sydney just add the class operator Initialize to set IsNull true, but in older versions, you can simply change the name of IsNull to IsValid, since the boolean fields started False, then the initial value of IsValid of nullable will be False. But there are other possibilities.
-
@Attila Kovacs About the version of delphi supported, according to docwiki, the units System.JSON.XXX was introduced in Delphi 10 Seattle, but again I can’t guarantee it’s compatible as it would need to be tested on these versions.
- 1
-
@Attila Kovacs I added support to register custom json converters.
About the nullables, is very easy to implement the nullable and the converters. See one simple solution:
uses System.Rtti, System.TypInfo, System.JSON.Serializers, System.JSON.Readers, System.JSON.Writers, System.JSON.Types, iPub.Rtl.Refit; type TNullable<T> = record IsNull: Boolean; Value: T; end; TNullableConverter<T> = class(TJsonConverter) public procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); override; function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; override; function CanConvert(ATypeInf: PTypeInfo): Boolean; override; end; { TNullableConverter<T> } function TNullableConverter<T>.CanConvert(ATypeInf: PTypeInfo): Boolean; begin Result := ATypeInf = TypeInfo(TNullable<T>); end; function TNullableConverter<T>.ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo; const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; var LNullable: TNullable<T>; begin if AReader.TokenType = TJsonToken.Null then begin LNullable.IsNull := True; LNullable.Value := Default(T); end else begin LNullable.IsNull := False; LNullable.Value := AReader.Value.AsType<T>; end; TValue.Make<TNullable<T>>(LNullable, Result); end; procedure TNullableConverter<T>.WriteJson(const AWriter: TJsonWriter; const AValue: TValue; const ASerializer: TJsonSerializer); var LNullable: TNullable<T>; LValue: TValue; begin LNullable := AValue.AsType<TNullable<T>>; if LNullable.IsNull then AWriter.WriteNull else begin TValue.Make<T>(LNullable.Value, LValue); AWriter.WriteValue(LValue); end; end; initialization GRestService.RegisterConverters([TNullableConverter<string>, TNullableConverter<Byte>, TNullableConverter<Word>, TNullableConverter<Integer>, TNullableConverter<Cardinal>, TNullableConverter<Single>, TNullableConverter<Double>, TNullableConverter<Int64>, TNullableConverter<UInt64>, TNullableConverter<TDateTime>, TNullableConverter<Boolean>, TNullableConverter<Char>]); end.
To use just replace the simple types to the nullable types:
TUser = record Name: TNullable<string>; Location: string; Id: Integer; Email: TNullable<string>; end; [BaseUrl('https://api.github.com')] IGithubApi = interface(IipRestApi) ['{4C3B546F-216D-46D9-8E7D-0009C0771064}'] [Get('/users/{user}')] function GetUser(const AUser: string): TUser; end; procedure TForm1.FormCreate(Sender: TObject); var LGithubApi: IGithubApi; LUser: TUser; begin LGithubApi := GRestService.&For<IGithubApi>; LUser := LGithubApi.GetUser('viniciusfbb'); end;
You can test the code above and see the user name is not null but the user email is null.
The nullable type and the nullable converter code in this example need to be implemented by the developer, I can't put it on the library because each developer has it own implementation os nullable.
- 1
- 1
-
21 minutes ago, Attila Kovacs said:I don't think so as I can't find System.JSON.Serializers under Berlin but under Tokyo.
I don't have older versions installed to be able to test, or do backwards compatibility, but it looks like you're right, delphi 10.2 tokyo or newer.
22 minutes ago, Attila Kovacs said:Is it possible to decorate the custom nullable fields that this internal serializer/reverter can translate them?
A one-time-registered type converter/reverter would be even better.
What exactly did you want? The json serialization of delphi is very malleable, you do not need to declare all the necessary fields and you can also use all existing attributes in System.Json.Serializers.pas.
29 minutes ago, Attila Kovacs said:A one-time-registered type converter/reverter would be even better.
It's a great option, I will implement it today.
-
A very simple way to consume one Rest Api, just declaring one interface, based on .Net Refit:
- 3
-
29 minutes ago, Bob Devine said:@vfbb Thanks for this looks interesting. Would be worth putting a link to DEB on the readme https://github.com/spinettaro/delphi-event-bus
Made.
33 minutes ago, Bob Devine said:There's another one here: https://github.com/THSoftPoland/DelphiMessageBus
It's not worth, it's a very limited system, synchronous, windows support only ...
-
51 minutes ago, Lars Fosdal said:I use OmniThreadLibrary queues for this.
This is just a simplistic messaging system. Don't compare a simple, lightweight 1k line system with a complete 50k line system (although messages work just as well).
-
The full explanation is on the github page. See this part:
"The problem
Delphi has its own messaging system (System.Messaging.pas) that works well but is totally synchronous and thread unsafe. In multithreaded systems we always need to communicate with other classes, sometimes synchronously, sometimes asynchronously, sometimes synchronizing with the mainthread (in the case of the UI), and doing this without a message system (communicating directly) makes the code large and complex, prone to many bugs."
-
Making it available to everyone who want a thread safe, asynchronous, cross-platform and simplistic messaging system for communication between classes / layers in delphi applications.
https://github.com/viniciusfbb/ipub-messaging
- 7
- 1
Unicode string - how element iterating?
in RTL and Delphi Object Pascal
Posted
Wrong! Actually porting the C# code will be laborious and will require maintenance as the unicode is changed.
The best solution at this point is to use apis:
Windows - DWriteTextAnalyze
Android - GraphemeCharsLength := JString.codePointAt(Index);
iOS - CFStringGetRangeOfComposedCharactersAtIndex