FireMonkey AbsoluteOpacity issue - components

I defined a component as
tZText = class (tText)
...
property Enabled : Boolean read GetEnabled write SetEnabled;
...
procedure tZText.SetEnabled (const aEnabled: Boolean);
begin
inherited Enabled := aEnabled;
case aEnabled of
False : TextSettings.FontColor := fTextColors.TextColorDisabled;
True : TextSettings.FontColor := fTextColors.TextColorIdle ;
end;
end;
It is suppoused that the component will paint the text according to the state of the Enabled property.
However, I followed the execution throughout the primitive code and, at some time, it makes the assignment
FLayout.Opacity := AbsoluteOpacity;
where AbsoluteOpacity is 0,6..., hence the text is painted as if the component is always not enabled.
I can not figure out why or where AbsoluteOpacity took this value.
I appreciate any suggestion to solve this issue.
Notes
1 the component's parent is a TRectangle
2 the environment is not styled

Related

How to fix, images loaded in multi thread sometimes don't show up on the form

In my manga viewer i try to load a large number of images (around 200), i try loading these images inside an anonymous thread and when the loading finishes i call a Thread.synchronize, this works fine, but in some cases i keep seeing some images not drawn on the form (I think they are loaded because i can see the information about the image), how can i solve this?
This issue only happens in multi thread so i decided to set the image visible to false and then at the end of the loading inside a Thread.synchronize turn the visible to true, i had hoped this would force some kind of refresh or redraw, but the problem i mentioned consists
TThread.CreateAnonymousThread(procedure
begin
for imageIndex := 0 to fileList.count - 1 do
begin
filePath := fileList[imageIndex]
//Please note that mImageList is an array of FMX.Objects.TImage
//which was filled when the Form is created (I create 300 TImage
//at Form create and later i load the images to the bitmap when
//the user clicks on a new directory(Chapter)
mImageList[imageIndex].beginUpdate();
try
mImageList[imageIndex].loadFromFile(filePath);
mImageList[imageIndex].tagString := filePath;
mImageList[imageIndex].Scale := mImageScale;
mImageList[imageIndex].Visible := false;
except
mImageList[imageIndex].EmptyBitmaps;
end;
mImageList[imageIndex].endUpdate();
end;
TThread.Synchronize(nil, procedure
var
I: Integer;
begin
for I := Low(mImageList) to High(mImageList) do
begin
mImageList[I].Visible := true;
end;
end);
end).Start;
I expect that when i scroll down through the images loaded on the form, i can see them all and they all get drawn on the form properly.

On Delphi how to I reference objects that have been created using just one variable?

I was creating a Form with some buttons in runtime in my application and I realized something that's bothering me a lot and I couldn't figure for myself or anywhere in the internet.
Look at the following code.
procedure TfrmTest.CreateFourButtons(Sender: TObject);
var
i: Integer; B: TButton;
begin
for i := 1 to 4 do
begin
B := TButton.Create(frmTest);
B.Parent := frmTest;
B.SetBounds(250,(70+(30*i)),75,25);
B.Caption := 'Button' + IntToStr(i);
B.Visible := True;
end;
end;
So I just created four buttons at runtime on a form in specific locations. So far so good right?
But now let's imagine that I want to change the Caption property of the first button to "HotPotato"? How do I reference the first button now since I used just one variable to create those buttons? I've been told to store those objects in an array of TButtons or better yet in an TObjectList and these are all fine solutions to this problem. But then one question came to my mind!
Where are those buttons located in the memory? Are the any ways for me to reference them without using arrays or object lists?
The normal way to keep track of multiple similar object is to use an array.
procedure TfrmTest.CreateFourButtons(Sender: TObject);
var
i: Integer;
B: TArray<TButton>; //or array of TButton for older versions
begin
SetLength(B, 4);
for i := 0 to 3 do begin
B[i] := TButton.Create(frmTest);
B[i].Parent := frmTest;
B[i].SetBounds(250,(70+(30*i)),75,25);
B[i].Caption := 'Button' + IntToStr(i);
B[i].Visible := True;
end;
B[0].Caption:= 'HotPotato';
end;
When placing buttons on a form this is not strictly needed, the form already uses a list to keep track of child controls placed on it, but you'll need some way to tell the different buttons apart.
You can use the tag property for this:
for i := 1 to 4 do begin
B := TButton.Create(frmTest);
B.Parent := frmTest;
B.Tag:= i;
...
end;
//This will get inefficient if there are many controls on a form.
for var C in frmTest.Controls do begin //10.3 syntax.
if (C is TButton) and (C.tag = 1) then C.Caption:= 'HotPotato'
end;
The button is an object and it is thus located on the heap. If you lose its reference you will never find it again. However if it is placed on a parent control, then that parent will keep track of it and you can always get it using FindChildControl or the Controls list of the form.
FindChildControl does a search by name. This requires you to set the name of the control, or it will not work.
var B:= frmTest.FindChildControl('Button1');
Note: FindChildControl only locates immediate children of the control. It can't find a control that is a child of one of the control's children.
This locating children can get complicated if the button is located in a subpanel. Better to use the array or a list to keep track of a range of buttons.

Checking if only one specific component is selected in Inno Setup

I have a requirement to skip a page when only a particular component is selected in the component selection page. Please look at the below image
[Components]
Name: DBS\TRACE; Types: DBS TBLWOS; Description: DBS Tracing Bodylife Database;
The requirement is to skip a page when the option "DBS Tracing Bodylife Database" (highlighted in the image) alone is selected and clicked on Next button. If I select only that option, I am successfully able to skip the page using below code.
if PageID = PageToBeSkipped.ID then begin
Result := not (IsComponentSelected('not DBS\TRACE'));
end;
But If I select any other additional component along with the "DBS Tracing Bodylife Database" from this page which is nearly 20, the page should not be skipped. The above code skips the page if any additional component is selected too.
How can I handle this?
Thanks in advance!
To test if any component, except one specific, is selected, you can use WizardSelectedComponents function (which returns a comma-separated list of selected components).
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = PageToBeSkipped.ID then
begin
Result := (CompareText(WizardSelectedComponents(False), 'DBS,DBS\TRACE') = 0);
end;
end;
Note that the WizardSelectedComponents returns even "partially" selected components groups.

Drawing and image on a button in a cell in a TStringGrid

I have a TStringGrid in Lazarus, running on Linux. I have a column which has editor type cbsButton. I want the button to display a certain image, instead of an ellipsis. I have the following code, which causes an error:
procedure TForm1.streams_gridDrawCell(Sender: TObject; aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState);
var
aCanvas: TCanvas;
aGrid: TStringGrid;
Editor: TWinControl;
image: TImage;
begin
if (aCol <> 1) or (aRow = 0) then begin
Exit;
end;
aGrid := (Sender as TStringGrid);
aCanvas := image.Canvas;
aCanvas.FillRect(aRect);
imagelist1.Draw(aCanvas, aRect.Left+2, aRect.Top+2, 8);
Editor := (aGrid.EditorByStyle(cbsButton) as TButtonCellEditor);
Editor.Brush.Style := TBrushStyle.bsImage;
(Editor.Brush.Image as TImage) := image; // causes the error below
end;
The error is:
mainform.pas(156,23) Error: Class or Object types "TFPCustomImage" and
"TImage" are not related
At this point, I'm sure I'm going about this in entirely the wrong way. Could someone please put me back on the right path?
I doubt that the OnDrawCell event is the correct place to modify a cell editor because probably the correct cell editor does not exist at this moment when the cell is painted.
The correct event to define the cell editor is the OnSelectEditor event of the grid. Please read the wiki (http://wiki.lazarus.freepascal.org/Grids_Reference_Page).
The cbsButton editor which you use inherits from TButton. A TButton does not have a Glyph property - you cannot assign a bitmap to the button. But can you write your own cell editor easily, just follow the standard example in examples/gridexamples/gridcelleditor:
Add a TBitBtn to the form. Delete its Caption, add the requested image to the Glyph property. Set the Visible property to false.
In the OnClick event of this button write how you want to edit the cell. Access the cell specified by the properties Col and Row of the grid. As an example, I assume here that you just want to open an InputBox:
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] :=
InputBox('Input some text', 'Text:', '');
end;
Now write an event handler for the OnSelectEditor event of the grid. It must assign the BitBtn to the Editor parameter of the event and make sure that the button is at the correct position within the selected cell - that's all!
procedure TForm1.StringGrid1SelectEditor(Sender: TObject; aCol, aRow: Integer;
var Editor: TWinControl);
var
R: TRect;
begin
if (aCol=2) and (aRow > 0) then begin
R := StringGrid1.CellRect(aCol, ARow);
R.Left := R.Right - (R.Bottom - R.Top);
BitBtn1.BoundsRect := R;
Editor := BitBtn1;
end;
end;
Editor.Brush.Image is a property of type TFPCustomImage. This is a TPersistent descendant. And TImage is a descendant of TCustomImage and thus TGraphicControl and TControl. So these are entirely different classes that are not compatible.
So you are not expected to cast (Editor.Brush.Image as TImage) and assign any TImage instance to it.

Inno Setup Place controls on wpPreparing Page

I am trying to place a label on the wpPreparing page to indicate uninstallation of an existing version, prior to running the new installation. Here is my code:
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
UninstallingLabel: TNewStaticText;
intResultCode: Integer;
begin
with UninstallingLabel do
begin
Caption := 'Uninstalling existing version...';
Left := WizardForm.StatusLabel.Left;
Top := WizardForm.StatusLabel.Top;
Parent := wpPreparing.Surface;
end;
if strExistingInstallPath <> '' then
begin
Exec(GetUninstallString, '/verysilent /suppressmsgboxes', '', SW_HIDE,
ewWaitUntilTerminated, intResultCode);
end;
end;
The trouble is it does not seem to like Parent := wpPreparing.Surface and compiling fails with a
Semicolon (;) expected
error. This syntax works when adding a label to a custom created page. Why does this fail when trying to add it to wpPreparing?
The wpPreparing is not an object, it's just a numerical constant.
The WizardForm.PreparingPage holds a reference to the "Preparing to Install" page. Note that it is of type TNewNotebookPage already, not TWizardPage. So you use it directly as a parent.
Also note that the StatusLabel is on the "Installing" page. You probably want to relate your new label to the PreparingLabel instead.
And you have to create the UninstallingLabel.
UninstallingLabel := TNewStaticText.Create(WizardForm);
with UninstallingLabel do
begin
Caption := 'Uninstalling existing version...';
Left := WizardForm.PreparingLabel.Left;
Top := WizardForm.PreparingLabel.Top;
Parent := WizardForm.PreparingPage;
end;
Though do you really want to shadow the PreparingLabel (as you use its coordinates).
What about reusing it instead?
WizardForm.PreparingLabel.Caption := 'Uninstalling existing version...';
I've replayed your code. It works if you use just WizardForm as the parent. But it's in the top left corner of the form...
wpPreparing is the name of a constant that holds the ID of the correspnding page.
And you have to create an instance of UninstallingLabel

Resources