How make a firemonkey HUD window - delphi

I wanna to replicate the HUD functionality of https://github.com/jdg/MBProgressHUD in Delphi with firemonkey.
This is what look like in iPhone:
The main issue is how make the form semi-transparent & completely remove the borders.

Create your Firemonkey HD form, set it's Fill.Kind to bkNone, and it's Fill.Color to Null. Additionally, set it's Transparency property to True, and it's BorderStyle to bsNone.
Create a TRectangle (or any shape), and set the Stroke.Kind property to bkNone. Set it's Fill.Color to Gray, it's Opacity to 0.5.
Create a TAniIndicator and TLabel with parent of both as the form. It's Opacity remains at 1.0. Optionally, also create a TImage and make it the exact same size and position as the TAniIndicator.
From there, it's simply a case of working with TFloatAnimation on the TAniIndicator when you want to change the image (to a tick or such) and the label text to simply change to whatever message you want to display. Ideally, you simply create a procedure that accepts either a string or integer as a variable, and then modify the text and indicator/image to match that. For example;
Procedure TForm1.Process(Mode : Integer);
Begin
if Mode = 1 then
begin
AniIndicator1.Enabled := True;
AniIndicator1.Visible := True;
Image1.Visible := False;
Label1.TextAlign := TTextAlign.taCenter; // Must be called to reset alignment
Label1.Text := 'Loading';
End
else if Mode = 2 then
Begin
AniIndicator1.Enabled := False;
AniIndicator1.Visible := False;
Label1.TextAlign := TTextAlign.taCenter; // Must be called to reset alignment
Image1.Bitmap.LoadFromFile('Tick.png');
Image1.Visible := True;
Label1.Text := 'Complete!';
end;
end;
You can then create a tpanel in your main form, and then add the above form (that contains the TAniIndicator, label, and rectangle) as a child component. You then call the procedure you created with a valid mode variable and it'll run as you indicated in the code. It's easy enough to add more modes and i've done something similar with one of my own applications (although it was related to TRectangle rather than creating an indicator).

Related

Procedure to change the color of multiple panels with different names in Delphi

I have a TForm with 5 TPanel controls on it. I need them to change color when they are clicked on.
I can easily write code for each TPanel to change colors, but that consists of too much code, so I was wondering if I could write a procedure to change the color of each one independently and just call this function with the name of each TPanel as a parameter? If not, then is there any other way to do this?
This is really easy.
Create a new VCL application.
Add five TPanel controls to the main form.
Select all five panels.
In the Object Inspector, set ParentBackground to False.
In the Object Inspector, click the Events tab and click the empty field to the right of the OnClick row caption. Type PanelClick and press Enter.
Write the following code:
procedure TForm1.PanelClick(Sender: TObject);
begin
if Sender is TPanel then
TPanel(Sender).Color := RGB(Random(255), Random(255), Random(255));
end;
I think this is what you asked. However, given your image, I suspect you rather would like a toggling behaviour:
procedure TForm1.PanelClick(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ControlCount - 1 do
if Controls[i] is TPanel then
if Controls[i] = Sender then
TPanel(Controls[i]).Color := clHighlight
else
TPanel(Controls[i]).Color := clBtnFace;
end;
This iterates over the child controls of Self, since ControlCount and Controls mean Self.ControlCount and Self.Controls, respectively. If your target panels have a different parent (a panel, say), iterate over its children instead.
Optionally, you can give the target panels a specific Tag value (like 500) and only change the colour of such panels.
And please set BevelOuter to bvNone. We have left the 90s.

Form positioning when using TStyleManager in Delphi XE7 application

I'm trying to make a Delphi application form remember it's position and recall it the next time the application is started:
procedure TForm1.LoadSettings;
var
xml : IXMLDocument;
node : IXMLNode;
begin
[ ... initializing XML ...]
// Loading theme
if node.HasAttribute('Theme') then TStyleManager.SetStyle(node.Attributes['Theme']);
// Loading position & size
if node.HasAttribute('PosTop') then self.Top := StrToInt(node.Attributes['PosTop']);
if node.HasAttribute('PosLeft') then self.Left := StrToInt(node.Attributes['PosLeft']);
if node.HasAttribute('PosWidth') then self.Width := StrToInt(node.Attributes['PosWidth']);
if node.HasAttribute('PosHeight') then self.Height := StrToInt(node.Attributes['PosHeight']);
[ ... some other stuff ... ]
end;
Everything works fine when the "Theme" is set to default "Windows" theme. With any theme, the form appears at it's default position set up by Windows, close to the upper left display corner, no matter where the form was located before.
Any ideas about what may be the cause and how to fix that?
Thanks in advance!
If you want to properly restore form's position you have to set it initial Position property to poDesigned in form designer.
Default Position property is poDefaultPosOnly. That means your form position will be determined automatically by Windows. Depending at which point you trigger your own form positioning during the form creating process and depending on other code you may either successfully change forms position or not.
However, if the Position property is poDesigned all positioning will be left to Delphi code itself and you will be able to properly apply your setting.
Why?
Because if you use any code that will trigger form's window recreation, that code will again try to set your form's position to Windows default one and can interfere with your positioning process.
Calling TStyleManager.SetStyle does exactly that.
Simple test case for above would be creating new VCL project, adding some style to it and put following code in FormCreate event
procedure TForm1.FormCreate(Sender: TObject);
var Styled: boolean;
begin
Styled := true;
if Styled then TStyleManager.SetStyle('Silver');
Top := 200;
Left := 300;
Width := 800;
Height := 600;
end;
If Styled is false setting position works, if it is true it doesn't.

What causes a control to be placed more left-right-top-bottom than another which has same alignment? [duplicate]

In this particular case I'm using PowerPDF library to dynamically build a PDF document, but the same applies for the general concept of dynamically aligning controls sequentially inside of a parent control. In this library, TPRPage is the base control to contain all element controls, in this case, sequential instances of TPRLayoutPanel.
What I do when dynamically adding controls:
Create a control (TPRLayoutPanel)
Set the control's parent (TPRPage)
Align the control to top (PRLayoutPanel.Align:= alTop;)
The problem is it gets forced to the very beginning (top) instead of the very end (bottom) of the page.
I've tried setting its order PRLayoutPanel.SendToBack; or PRLayoutPanel.BringToFront but with no luck.
How can I dynamically create and align multiple controls within a parent control sequentially? My only current work-around is to add the controls in reverse order (from end to beginning) which is ridiculously unnecessary.
Here's my universal function which creates every new instance of an aligned control in this parent:
function TfrmReport.InsertPanel: TPRLayoutPanel;
begin
Result:= TPRLayoutPanel.Create(PRPage);
Result.Parent:= PRPage;
Result.Align:= alTop;
Result.Height:= 40; //Default, may change later
end;
Once again, DisableAlign and EnableAlign to the rescue:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
P: TPanel;
begin
DisableAlign;
try
for I := 0 to 4 do
begin
P := TPanel.Create(Self);
P.Caption := IntToStr(I);
P.Align := alTop;
P.Parent := Self;
end;
finally
EnableAlign;
end;
end;
Explanation:
When alignment is enabled, every single addition of a control to a container (the form itself in this specific case) will re-evaluate all alignment (and anchor) settings of all other controls within that container. In case that control has no specific Top property set, then Top will be 0. When there is already another control aligned to the top, then there are two controls with Top = 0, and the one which is about to inserted wins. I (currently) have no in-depth explanation for that, but it just is, and the position order indeed gets reversed from the creation order.
Now, when alignment of the container is disabled, then consecutive added controls are simply just inserted with all their positioning properties unaltered. When alignment is enabled again, then all those controls are re-evaluated in the same manner, with the difference that this takes place in one single loop in the order of the index in the Controls array; i.e. the order in which they were created.
You need to set the Top property to be the bottom of the previous panel. For example, like this:
PanelTop := 0;
for i := 0 to 5 do
begin
Panel[i] := TPanel.Create(Self);
Panel[i].Parent := Self;
Panel[i].Height := PanelHeight;
Panel[i].Align := alTop;
Panel[i].Top := PanelTop;
inc(PanelTop, PanelHeight);
end;
To fit it into your code you'd have to keep track of the location of the most recently added panel. Perhaps you could add a var parameter to your InsertPanel function:
function TfrmReport.InsertPanel(var PanelTop: Integer): TPRLayoutPanel;
begin
Result:= TPRLayoutPanel.Create(PRPage);
Result.Parent:= PRPage;
Result.Top:= PanelTop;
Result.Height:= 40;
Result.Align:= alTop;
inc(PanelTop, Result.Height);
end;
I trust you get the idea!
You may use alCustom align type and control all of your panels positions via CustomAlignPosition method (you will need to override it in parent control). This will give you more flexibility and control.

Resize won't execute untill I manually call ClientHeigh or until I manually resize it

I want to create a custom control derived from TPanel that contains an image and a bunch of other controls on it.
After writing the code I have some weird behavior in my program. I realized that some vars that were supposed to be initialized in TDisplay.Resize (override) was never initialized because the Resize was never executed.
To 'solve it' I put a button on a form and called the LoadSample function which calls ClientHeight which calls Resize FOR THE FIRST TIME!
constructor TDisplay.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Ready := FALSE;
Parent := Owner as TWinControl;
Width := 200;
Height := 86;
Color := clSilver;
Caption := '';
DoubleBuffered:= TRUE;
InternalDisplay:= TImage32.Create(Self);
with Display DO
begin
Parent := Self;
Bitmap.Width := 1;
Bitmap.Height := 1;
RepaintMode := rmOptimizer;
Align := alClient;
SetupBitmap(TRUE, clBlack32);
Visible := TRUE;
OnMouseDown := DMouseDown;
end;
...
end;
Update:
Also the InternalDisplay won't be aligned to its parent size until I manually resize the form (the control) at runtime. Only then it will act as it was supposed to act (to stay aligned to alClient).
Update 2:
Resize is declared like that: procedure Resize; override;
Update 3:
I removed the ClientHeight line from my construnctor and move it here:
procedure TDisplay.LoadSample(VAR Obj: TMySample; CONST bReleaseOnExit: boolean)
begin
ClientHeight; <--------- this will call Resize for the first time and my code will be finally initialized. But until this point my display will look odd because the code was never initialized. So the user will see weird stuff until it pushes the 'LoadSample' button.
more code here....
end;
Update 4:
I used HandleNeeded as David suggested and it solved the initialization problem. However, the Image still won't align to the entire client area unless I manually resize the form/control.
Update 5
Continued here, as David suggested: TImage won't align to parent
Your control is derived from TWinControl, and TWinControl calls Resize in response to the WM_SIZE message. So, Resize will not be called unless the control's window handle has been created.
The Resize method is not called when you assign Height, or indeed Width, because the window handle has not yet been allocated.
When you evaluate the ClientHeight property, that results in the window handle being created, and then Resize is called. That's because GetClientHeight calls GetClientRect which looks like this:
function TWinControl.GetClientRect: TRect;
begin
Winapi.Windows.GetClientRect(Handle, Result);
end;
And it's the evalutation of the Handle property that forces the window handle into existence.
Your form isn't showing yet, so it isn't yet able to receive Windows messages (such as the resize message that triggers the OnResize event).

Dynamically resize a form to fit the size of a Frame in Delphi

I have an application that has 5 different sized frames. I'd like to dynamically re-size the main form to fit the frame when I move from one frame to another.
I can use the MinHeight/MinWidth properties of the frame to force the main form to fit the frame, but then when moving to a smaller frame, the main form does not adjust it's size.
Any ideas?
--Edit
...
TFormMain = Class(TForm)
...
public
FrameImportPackage: TFrameImportPackage;
...
procedure TFormMain.MenuPackagesImportClick(Sender: TObject);
begin
if not (Assigned(FrameImportPackage)) then
begin
FrameImportPackage := TFrameImportPackage.Create(Self);
FrameImportPackage.LabelFrameCaption.Caption := 'Import or Edit a Package';
end
else
begin
FrameImportPackage.BringToFront;
end;
FrameImportPackage.Parent := Self;
end;
--Edit
Regards, Pieter
If I understand your question correctly, you've got frames that don't change size, you want the form to update size to fit your frames. Let Delphi handle that for you, using the AutoSize property.
Set AutoSize = True for your form.
I've tested AutoSize with the following code, using Delphi 2010:
Create a new VCL application. On the blank form drop a single Panel, let it keep it's name (Panel1). Make sure the panel is not too small, because we'll write code to decrease it's size at runtime.
Set the form's AutoSize property to True.
Drop two buttons on the panel, Button1 and Button2.
Double click the buttons, and copy-paste the following event handlers:
Code:
procedure TForm31.Button1Click(Sender: TObject);
var NewR: TRect;
begin
NewR := Panel1.BoundsRect;
Dec(NewR.Right, 32);
Dec(NewR.Bottom, 32);
Button1.Parent := Self;
Button2.Parent := Self;
Panel1.Free;
Panel1 := TPanel.Create(Self);
Panel1.BoundsRect := NewR;
Panel1.Parent := Self;
Button1.Parent := Panel1;
Button2.Parent := Panel1;
end;
procedure TForm31.Button2Click(Sender: TObject);
begin
Panel1.Height := Panel1.Height - 32;
Panel1.Width := Panel1.Width - 32;
end;
This essentially gives you two ways of decreasing the size of the panel, to handle two possible scenarios: Button1 frees the old panel and creates a new, smaller panel. Button2 directly resize the existing panel. Both work as expected!
At least on Delphi 2006 there is a really anonying BUG with Form AutoSize.
You put a TStringGrid onto the form (Left and Top equal zero, align equal None, Top, CLient does not matter), when you change its ClientWidth and ClientHeightt the form not allways adjusts its size to the control.
Normally when it fails is when control size is reduced, form size does not get reduced.
There is no good fix, the only way to do it is manually set clientwidth and clientheight of the form when the object is resized.
It is said: Form AutoSize does not allways work well! It is a BUG on the VCL.

Resources