Delphi OwnerDraw PageControl customization - delphi

I am having one project with a OwnerDraw PageControl. I need to customize it as follows:
So I have written the following codes:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls;
type
TPageControl = class(Vcl.ComCtrls.TPageControl)
protected
procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
end;
type
TForm1 = class(TForm)
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
TabSheet3: TTabSheet;
TabSheet4: TTabSheet;
TabSheet5: TTabSheet;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TPageControl.CNDrawItem(var Message: TWMDrawItem);
var
Color: TColor;
Rect: TRect;
Rgn: HRGN;
SaveIndex: Integer;
Caption:string;
Size :TSize;
x,y :integer;
begin
Color := clBlack;
case Message.DrawItemStruct.itemID of
0: Color := $008000FF;
1: Color := $00FF0080;
2: Color := $00408000;
end;
SetDCBrushColor(Message.DrawItemStruct.hDC, Color);
SelectClipRgn(Message.DrawItemStruct.hDC, 0);
Rect := Message.DrawItemStruct.rcItem;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
Inc(Rect.Left, 2);
Dec(Rect.Right, 2);
Dec(Rect.Bottom, 3);
end else begin
Dec(Rect.Left, 2);
Dec(Rect.Top, 2);
Inc(Rect.Right, 2);
Inc(Rect.Bottom);
end;
FillRect(Message.DrawItemStruct.hDC, Rect, GetStockObject(DC_BRUSH));
Rgn := CreateRectRgn(0, 0, 0, 0);
SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
DeleteObject(Rgn);
with Message.DrawItemStruct^ do
begin
SaveIndex := SaveDC(hDC);
Canvas.Lock;
try
Canvas.Handle:=hDC;
Canvas.Font :=Font;
Canvas.Brush :=Brush;
Caption:=Self.Tabs.Strings[ItemID];
Size:=Canvas.TextExtent(Caption);
x:=rcItem.Left+(rcItem.Right-rcItem.Left-Size.cx) div 2;
y:=rcItem.Top +(rcItem.Bottom-rcItem.Top-Size.cy) div 2+1;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then dec(y);
Canvas.TextRect(rcItem,x,y,Caption);
finally
Canvas.Handle := 0;
Canvas.Unlock;
RestoreDC(hDC, SaveIndex);
end;
end;
Message.Result := 1;
inherited;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
PageControl1.Ctl3D:=False;
end;
end.
But after compiling I am getting it as follows:
How to solve the problem?
My requirement is as follows:
1. 3D border should be removed from PageControl.
2. Form Background color should be removed from PageControl.
3. Selected Tab Color and Height should be different.
4. TabSheet Background should be customizable.
After that I have tried the following codes:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls;
type
TPageControl = class(Vcl.ComCtrls.TPageControl)
private
{ Private procedure }
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
protected
{ protected procedure }
procedure WndProc(var Message:TMessage); override;
procedure CreateParams(var Params: TCreateParams); override;
public
{ Public procedure }
published
{ published procedure }
end;
type
TForm1 = class(TForm)
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
TabSheet3: TTabSheet;
TabSheet4: TTabSheet;
TabSheet5: TTabSheet;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TPageControl.WndProc(var Message:TMessage);
begin
if Message.Msg=TCM_ADJUSTRECT then
begin
Inherited WndProc(Message);
if Fborder=bsNone then
begin
PRect(Message.LParam)^.Left:=0;
PRect(Message.LParam)^.Right:=ClientWidth;
PRect(Message.LParam)^.Top:=PRect(Message.LParam)^.Top-4;
PRect(Message.LParam)^.Bottom:=ClientHeight;
end;
end
else
Inherited WndProc(Message);
end;
procedure TPageControl.CNDrawItem(var Message: TWMDrawItem);
var
Color: TColor;
Rect: TRect;
Rgn: HRGN;
SaveIndex: Integer;
Caption:string;
Size :TSize;
x,y :integer;
begin
Color := 0;
case Message.DrawItemStruct.itemID of
0: Color := $008000FF;
1: Color := $00FF0080;
2: Color := $00408000;
end;
SetDCBrushColor(Message.DrawItemStruct.hDC, Color);
SelectClipRgn(Message.DrawItemStruct.hDC, 0);
Rect := Message.DrawItemStruct.rcItem;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
Inc(Rect.Left, 2);
Dec(Rect.Right, 2);
Dec(Rect.Bottom, 3);
end else begin
Dec(Rect.Left, 2);
Dec(Rect.Top, 2);
Inc(Rect.Right, 2);
Inc(Rect.Bottom);
end;
FillRect(Message.DrawItemStruct.hDC, Rect, GetStockObject(DC_BRUSH));
Rgn := CreateRectRgn(0, 0, 0, 0);
SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
DeleteObject(Rgn);
with Message.DrawItemStruct^ do
begin
SaveIndex := SaveDC(hDC);
Canvas.Lock;
try
Canvas.Handle:=hDC;
Canvas.Font :=Font;
Canvas.Brush :=Brush;
Caption:=Self.Tabs.Strings[ItemID];
Size:=Canvas.TextExtent(Caption);
x:=rcItem.Left+(rcItem.Right-rcItem.Left-Size.cx) div 2;
y:=rcItem.Top +(rcItem.Bottom-rcItem.Top-Size.cy) div 2+1;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then dec(y);
Canvas.TextRect(rcItem,x,y,Caption);
finally
Canvas.Handle := 0;
Canvas.Unlock;
RestoreDC(hDC, SaveIndex);
end;
end;
Message.Result := 1;
inherited;
end;
procedure TPageControl.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style:=Params.Style or TCS_OWNERDRAWFIXED;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
PageControl1.Ctl3D:=False;
end;
end.
But it is not compiling.

Just in case someone comes across this old question, TCM_ADJUSTRECT and TCS_OWNERDRAWFIXED are defined in Winapi.CommCtrl. TPageControl, nor any of its ancestors, have a Border property or a FBorder member. If you feel that you need one, you should create a new control by deriving from either TCustomTabControl or TPageControl.

Related

Adding all buttons captions automatically to string grid

I am new to Delphi. I would like to know, is there any way to add any Caption or Text inserted/created by the user in a Form to a StringGrid automatically?
For example, using for a simple translator VCL, the Form detects a Button added and the Caption of this new Button automatically appears in the StringGrid to go for the translating process.
unit frmTranslation_u;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls,
Vcl.Grids;
type
TfrmTranslation = class(TForm)
pnlPersonalInformation: TPanel;
lblFirstName: TLabel;
lblSureName: TLabel;
edtFirstName: TEdit;
edtSurName: TEdit;
pnlAction: TPanel;
btnEnglish: TButton;
btnAfrikaans: TButton;
btnDisplay: TButton;
bmbReset: TBitBtn;
bmbClose: TBitBtn;
memResult: TMemo;
sgdData: TStringGrid;
procedure btnAfrikaansClick(Sender: TObject);
procedure btnEnglishClick(Sender: TObject);
procedure btnDisplayClick(Sender: TObject);
procedure bmbResetClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure sgdDataClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmTranslation: TfrmTranslation;
implementation
{$R *.dfm}
procedure TfrmTranslation.bmbResetClick(Sender: TObject);
begin
// Clear the edit
edtFirstName.Clear;
edtSurName.Clear;
// Clear The memo
memResult.Clear;
// Shift the focus to the first name edit
edtFirstName.SetFocus;
end;
procedure TfrmTranslation.btnAfrikaansClick(Sender: TObject);
begin
lblFirstName.Caption := 'Noemnaam';
lblSureName.Caption := 'Van';
frmTranslation.Caption := 'Vertaling';
lblFirstName.Left := 32;
lblSureName.Left := 80;
btnAfrikaans.Enabled := False ;
btnEnglish.Enabled := true;
end;
procedure TfrmTranslation.btnDisplayClick(Sender: TObject);
begin
// show the full name in the memo
memResult.Lines.Add('You Added '+edtFirstName.Text +' '+ edtSurName.Text);
end;
procedure TfrmTranslation.btnEnglishClick(Sender: TObject);
begin
lblFirstName.Caption := 'First Name';
lblSureName.Caption := 'Surname';
frmTranslation.Caption := 'translation';
lblFirstName.Left := 40 ;
lblSureName.Left := 50 ;
btnEnglish.Enabled := false ;
btnAfrikaans.Enabled := true ;
end;
procedure TfrmTranslation.FormCreate(Sender: TObject);
var
i, iCol, iRow : integer ;
begin
sgdData.Cells[0,0] := 'NAME';
sgdData.Cells[1,0] := 'TYPE';
sgdData.Cells[2,0] := 'Id_LAN';
sgdData.Cells[3,0] := 'VALUE';
end;
procedure TfrmTranslation.sgdDataClick(Sender: TObject);
begin
end;
end.

Delphi: making two Chrome to run on two TPanel

I want to run two different instance of chrome into two TPanel of my form, for making a personal developing tool for test a realtime web editor with two different accounts.
My code isn't stable, sometimes chrome opens out of the TPanel and sometimes it works as expected.
unit WMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, ShellApi;
type
TForm2 = class(TForm)
Button1: TButton;
Panel1: TPanel;
Panel2: TPanel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure RunInsideControl(AFileName, AParams, AClassName: String; AHParent: THandle);
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.RunInsideControl(AFileName, AParams, AClassName : String; AHParent : THandle);
var
aRec: TShellExecuteInfo;
aAppHandle: integer;
const
AVerb = 'open';
ADir = '';
begin
aAppHandle := 0;
FillChar(aRec, SizeOf(aRec), #0);
aRec.cbSize := SizeOf(aRec);
aRec.fMask := SEE_MASK_NOCLOSEPROCESS;
aRec.lpVerb := PChar( AVerb );
aRec.lpFile := PChar( AfileName );
aRec.lpParameters := PChar( AParams );
aRec.lpDirectory := PChar( Adir );
aRec.nShow := SW_HIDE;
ShellExecuteEx(#aRec);
WaitForInputIdle(aRec.hProcess, 5000);
while aAppHandle = 0 do begin
aAppHandle := Winapi.Windows.FindWindow(PChar(AClassName), nil);
Winapi.Windows.SetParent(aAppHandle, AHParent);
SetWindowPos(aAppHandle, 0, 0, 0, ClientWidth, ClientHeight, SWP_ASYNCWINDOWPOS);
ShowWindow(aAppHandle, SW_SHOW);
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
RunInsideControl('Chrome.exe', '"https://stackoverflow.com"', 'Chrome_WidgetWin_1', Panel1.Handle);
RunInsideControl('Chrome.exe', '-incognito "https://stackoverflow.com"', 'Chrome_WidgetWin_1', Panel2.Handle);
end;
end.
fixed myself... the problem is
SetWindowPos(aAppHandle, 0, 0, 0, ClientWidth, ClientHeight, SWP_ASYNCWINDOWPOS);
ShowWindow(aAppHandle, SW_SHOW);
final code with resize handling:
unit WMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls, ShellApi;
type
TForm2 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Splitter1: TSplitter;
Panel3: TPanel;
procedure FormShow(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure Panel1Resize(Sender: TObject);
procedure Panel2Resize(Sender: TObject);
private
{ Private declarations }
FApp1: integer;
FApp2: integer;
function RunInsideControl(AFileName, AParams, AClassName : String; AHParent : THandle): THandle;
procedure SizeMove(var msg: TWMSize); message WM_SIZE;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
function TForm2.RunInsideControl(AFileName, AParams, AClassName : String; AHParent : THandle): THandle;
var
aRec: TShellExecuteInfo;
aAppHandle: integer;
const
AVerb = 'open';
ADir = '';
begin
aAppHandle := 0;
FillChar(aRec, SizeOf(aRec), #0);
aRec.cbSize := SizeOf(aRec);
aRec.fMask := SEE_MASK_NOCLOSEPROCESS;
aRec.lpVerb := PChar( AVerb );
aRec.lpFile := PChar( AfileName );
aRec.lpParameters := PChar( AParams );
aRec.lpDirectory := PChar( Adir );
aRec.nShow := SW_HIDE;
ShellExecuteEx(#aRec);
WaitForInputIdle(aRec.hProcess, 5000);
while aAppHandle = 0 do begin
aAppHandle := Winapi.Windows.FindWindow(PChar(AClassName), nil);
Winapi.Windows.SetParent(aAppHandle, AHParent);
// SetWindowPos(aAppHandle, 0, 0, 0, ClientWidth, ClientHeight, SWP_ASYNCWINDOWPOS);
// ShowWindow(aAppHandle, SW_SHOW);
end;
Result := aAppHandle;
end;
procedure TForm2.FormShow(Sender: TObject);
begin
FApp1 := RunInsideControl('Chrome.exe', '"https://stackoverflow.com/"', 'Chrome_WidgetWin_1', Panel1.Handle);
FApp2 := RunInsideControl('Chrome.exe', '-incognito "https://stackoverflow.com/"', 'Chrome_WidgetWin_1', Panel2.Handle);
Panel1.Width := Trunc( Screen.Width / 2 );
ShowWindowAsync(Handle, SW_MAXIMIZE);
end;
procedure TForm2.FormResize(Sender: TObject);
begin
if IsWindow(FApp1) then SetWindowPos(FApp1, 0, 0, 0, Panel1.Width, Panel1.Height, SWP_ASYNCWINDOWPOS);
if IsWindow(FApp2) then SetWindowPos(FApp2, 0, 0, 0, Panel2.Width, Panel2.Height, SWP_ASYNCWINDOWPOS);
end;
procedure TForm2.Panel1Resize(Sender: TObject);
begin
FormResize(nil);
end;
procedure TForm2.Panel2Resize(Sender: TObject);
begin
FormResize(nil);
end;
Procedure TForm2.SizeMove(var msg: TWMSize);
begin
inherited;
if (msg.SizeType = SIZE_MAXIMIZED) OR (msg.SizeType = SIZE_RESTORED) then FormResize(nil);
end;
end.

keypad popup blocks view of application in delphi xe8 Firemonkey

I tried this example. But it doesn't work if the edit field has 'password' property set to true. Any idea how to make it work with password edit fields?
Here's a sample for what you are trying to do. Simply put all your controls inside lyVKMain.
object frmBaseForm: TfrmBaseForm
OnFocusChanged = FormFocusChanged
OnVirtualKeyboardShown = FormVirtualKeyboardShown
OnVirtualKeyboardHidden = FormVirtualKeyboardHidden
DesignerMasterStyle = 0
object vsVKScrollBox: TVertScrollBox
Align = Contents
Size.PlatformDefault = False
TabOrder = 2
Viewport.Width = 640.000000000000000000
Viewport.Height = 480.000000000000000000
object lyVKMain: TLayout
Align = Client
Size.PlatformDefault = False
TabOrder = 0
end
end
end
And the unit file...
unit uForm;
interface
uses
System.SysUtils,
System.Types,
System.UITypes,
System.Classes,
System.Variants,
FMX.Types,
FMX.Controls,
FMX.Forms,
FMX.Graphics,
FMX.Dialogs,
FMX.Layouts;
type
TfrmBaseForm = class(TForm)
vsVKScrollBox: TVertScrollBox;
lyVKMain: TLayout;
procedure FormFocusChanged(Sender: TObject);
procedure FormVirtualKeyboardHidden(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect);
procedure FormVirtualKeyboardShown(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect);
private
{ Private declarations }
public
{ Public declarations }
FKBBounds: TRectF;
FNeedOffset: Boolean;
procedure CalcContentBoundsProc(Sender: TObject; var ContentBounds: TRectF);
procedure RestorePosition;
procedure UpdateKBBounds;
constructor Create(AOwner: TComponent); override;
end;
var
frmBaseForm: TfrmBaseForm;
implementation
uses
System.Math;
{$R *.fmx}
{ TfrmBaseForm }
procedure TfrmBaseForm.CalcContentBoundsProc(Sender: TObject; var ContentBounds: TRectF);
begin
if FNeedOffset and (FKBBounds.Top > 0) then
begin
ContentBounds.Bottom := Max(ContentBounds.Bottom, 2 * ClientHeight - FKBBounds.Top);
end;
end;
constructor TfrmBaseForm.Create(AOwner: TComponent);
begin
inherited;
vsVKScrollBox.OnCalcContentBounds := CalcContentBoundsProc;
end;
procedure TfrmBaseForm.FormFocusChanged(Sender: TObject);
begin
UpdateKBBounds;
end;
procedure TfrmBaseForm.FormVirtualKeyboardHidden(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect);
begin
FKBBounds.Create(0, 0, 0, 0);
FNeedOffset := False;
RestorePosition;
end;
procedure TfrmBaseForm.FormVirtualKeyboardShown(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect);
begin
FKBBounds := TRectF.Create(Bounds);
FKBBounds.TopLeft := ScreenToClient(FKBBounds.TopLeft);
FKBBounds.BottomRight := ScreenToClient(FKBBounds.BottomRight);
UpdateKBBounds;
end;
procedure TfrmBaseForm.RestorePosition;
begin
vsVKScrollBox.ViewportPosition := PointF(vsVKScrollBox.ViewportPosition.X, 0);
lyVKMain.Align := TAlignLayout.Client;
vsVKScrollBox.RealignContent;
end;
procedure TfrmBaseForm.UpdateKBBounds;
var
LFocused: TControl;
LFocusRect: TRectF;
begin
FNeedOffset := False;
if Assigned(Focused) then
begin
LFocused := TControl(Focused.GetObject);
LFocusRect := LFocused.AbsoluteRect;
LFocusRect.Offset(vsVKScrollBox.ViewportPosition);
if (LFocusRect.IntersectsWith(TRectF.Create(FKBBounds))) and (LFocusRect.Bottom > FKBBounds.Top) then
begin
FNeedOffset := True;
lyVKMain.Align := TAlignLayout.Horizontal;
vsVKScrollBox.RealignContent;
Application.ProcessMessages;
vsVKScrollBox.ViewportPosition := PointF(vsVKScrollBox.ViewportPosition.X, LFocusRect.Bottom - FKBBounds.Top);
end;
end;
if not FNeedOffset then
RestorePosition;
end;
end.

Delphi Created Images are not displayed

I am trying to dynamiclly create a custom component with images and display them in a Grid , but the Images don't show up. Below is the code with omitted part of declarations , could someone help me and tell me what am I doint wrong ?
Custom component Class
unit Tile;
interface
uses FMX.Controls, FMX.StdCtrls, System.Classes, FMX.Types, System.StrUtils ,
System.SysUtils, System.Types, System.UITypes,
System.Variants,
FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Ani,
FMX.Objects, FMX.Layouts;
type
TTileType = (Slider, Memory, Tile3D);
TTile = class
private
FOnChangedText: TNotifyEvent;
FType: TTileType;
FControl: TComponent;
FText: String;
FName: String;
FBitmap : TBitmap;
FAlign : TAlignLayout;
procedure TextChangedDefault(Sender: TObject);
protected
procedure SetText(aText: String);
procedure TextChanged; virtual;
procedure SetControlOnClick(AProc: TNotifyEvent);
function GetControlOnClick: TNotifyEvent;
procedure SetControlName(aName: String);
procedure SetBitmap(bitmap:TBitmap);
procedure SetAlign(align :TAlignLayout);
public
constructor Create(AParent: TFmxObject; AType: TTileType);
destructor Destroy; override;
published
property Text: String read FText write SetText;
property Name: String read FName write SetControlName;
property Bitmap:TBitmap read FBitmap write SetBitmap;
property Align:TAlignLayout read FAlign write SetAlign;
property OnChangedText: TNotifyEvent read FOnChangedText
write FOnChangedText;
property OnClick: TNotifyEvent read GetControlOnClick
write SetControlOnClick;
end;
implementation
constructor TTile.Create(AParent: TFmxObject; AType: TTileType);
begin
FType := AType;
case FType of
Slider:
begin
FControl := TButton.Create(AParent as TComponent);
FOnChangedText := TextChangedDefault;
(FControl as TFmxObject).Parent := AParent;
end;
Memory:
begin
FControl := TImage.Create(AParent as TComponent);
FOnChangedText := TextChangedDefault;
(FControl as TFmxObject).Parent := AParent;
end;
Tile3D:
FControl := nil;
else
FControl := nil;
end;
FName := FControl.Name;
end;
destructor TTile.Destroy;
begin
FControl.DisposeOf;
inherited;
end;
function TTile.GetControlOnClick: TNotifyEvent;
begin
case FType of
Slider:
begin
Result := (FControl as TButton).OnClick;
end;
Memory:
begin
Result := (FControl as TImage).OnClick;
end;
Tile3D:
begin
// TODO
end;
else
Result := nil;
end;
end;
procedure TTile.SetControlName(aName: String);
begin
FName := aName;
FControl.Name := aName;
end;
procedure TTile.SetBitmap(bitmap :TBitmap);
begin
FBitmap:=bitmap;
end;
procedure TTile.SetAlign(align :TAlignLayout);
begin
FAlign:=align;
end;
procedure TTile.SetControlOnClick(AProc: TNotifyEvent);
begin
case FType of
Slider:
begin
(FControl as TButton).OnClick := AProc;
end;
Memory:
begin
(FControl as TImage).OnClick := AProc;
end;
Tile3D:
begin
// TODO
end;
end;
end;
procedure TTile.SetText(aText: String);
begin
FText := aText;
TextChanged;
end;
procedure TTile.TextChanged;
begin
if Assigned(FOnChangedText) then
FOnChangedText(Self);
end;
procedure TTile.TextChangedDefault(Sender: TObject);
begin
(FControl as TButton).Text := FText;
end;
end.
Memory Game Class:
unit MemoryGame;
interface
uses Tile, Consts, FMX.Controls, FMX.StdCtrls, FMX.Layouts, System.Classes,
FMX.Types, System.Types, FMX.Graphics, System.SysUtils, FMX.Dialogs,Helper,FMX.ExtCtrls ,
System.UITypes,
System.Variants,
FMX.Forms,
FMX.TabControl, SliderPuzzle, System.Actions,
FMX.ActnList, FMX.StdActns, FMX.MultiView, FMX.Controls.Presentation, FMX.Edit,
DateUtils ,FMX.Objects ;
type
TMemoryGame = class(TGridLayout)
private
FTiles: TArray<TTile>;
procedure FillGrid(aTileNo: Integer);
protected
public
constructor Create(AParent: TFmxObject; aTileNo: Integer); reintroduce;
end;
var
moveCounter : Integer = 0 ;
implementation
{ MemoryGame }
constructor TMemoryGame.Create(AParent: TFmxObject; aTileNo: Integer);
begin
inherited Create(nil);
Parent := AParent;
FillGrid(aTileNo);
end;
procedure TMemoryGame.FillGrid(aTileNo: Integer);
var
I: Integer;
LTile: TTile;
begin
SetLength(FTiles, aTileNo);
for I := 0 to aTileNo - 1 do
begin
LTile := TTile.Create(Self, TTileType.Memory);
FTiles[I] := LTile;
if I = 0 then
begin
LTile.Bitmap:= TBitmap.CreateFromFile('../../img/img1.bmp');
LTile.Align := TAlignLayout.Client;
LTile.Align := TAlignLayout.Center;
end
else
begin
LTile.Bitmap:= TBitmap.CreateFromFile('../../img/img1.bmp');
LTile.Align := TAlignLayout.Client;
LTile.Align := TAlignLayout.Center;
end;
end;
end;
end.
Main Form:
unit MainForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants, Consts,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, FMX.ExtCtrls,
FMX.Layouts, FMX.TabControl, SliderPuzzle, System.Actions,
FMX.ActnList, FMX.StdActns, FMX.MultiView, FMX.Controls.Presentation, FMX.Edit,
DateUtils,MemoryGame, FMX.Objects;
type
TFormMain = class(TForm)
tcMain: TTabControl;
ti1Slider: TTabItem;
ti2Runtime: TTabItem;
ti4Game3D: TTabItem;
ti3Memory: TTabItem;
GridLayout: TGridLayout;
bTile1: TButton;
bTile2: TButton;
bTile3: TButton;
bTile4: TButton;
bTile5: TButton;
bTile6: TButton;
bTile7: TButton;
bTile8: TButton;
bTile9: TButton;
bTile10: TButton;
bTile11: TButton;
bTile12: TButton;
bTile13: TButton;
bTile14: TButton;
bTile15: TButton;
bTileEmpty: TButton;
bNew: TButton;
MultiView: TMultiView;
bExitApp: TButton;
ActionList: TActionList;
FileExitActn: TFileExit;
NewGameActn: TAction;
StyleBook: TStyleBook;
hitCountLabel: TLabel;
movesCounter: TLabel;
TimeCountLabel: TLabel;
timer: TLabel;
Timer1: TTimer;
procedure bTileClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure NewGameActnExecute(Sender: TObject);
procedure GridLayoutResize(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormMain: TFormMain;
Slider: TSliderPuzzle;
Memory : TMemoryGame;
firstMove : Boolean = true;
stop, elapsed : TDateTime ;
start : TDateTime = 0 ;
implementation
{$R *.fmx}
procedure TFormMain.NewGameActnExecute(Sender: TObject);
begin
if ti1Slider.IsSelected then
repeat
begin
firstMove:=true;
Slider.ShuffleTiles(GridLayout);
Slider.resetMoveCounter;
Timer1.Enabled := true;
Timer1.Interval :=1000;
Slider.resetTimer(start);
movesCounter.Text := IntToStr(Slider.GetMoveCount);
timer.Text := '--/--/--';
end;
until not Slider.IsGameOver(GridLayout)
else if ti2Runtime.IsSelected then
repeat
Slider.ShuffleTiles
until not Slider.IsGameOver;
end;
procedure TFormMain.Timer1Timer(Sender: TObject);
var myVar:Integer;
begin
if start<>0 then
begin
myVar := SecondsBetween(start,Now);
timer.Text :=Format('%.2d:%.2d', [myVar div 60, myVar mod 60]); ;
end;
end;
procedure TFormMain.bTileClick(Sender: TObject);
begin
if firstMove then
begin
Slider.startCount(start);
firstMove:=false;
end;
Slider.incrementCounter;
movesCounter.Text := IntToStr(Slider.GetMoveCount);
Slider.SwapTiles(GridLayout, Sender as TButton, bTileEmpty);
if Slider.IsGameOver(GridLayout) then
begin
Slider.resetMoveCounter;
Slider.resetTimer(start);
// movesCounter.Text := IntToStr(Slider.GetMoveCount);
// timer.Text := '--/--/--';
Timer1.Enabled := false;
ShowMessage('GAME OVER');
firstMove:=true;
ti3Memory.Enabled := true;
ti3Memory.TabControl.SetActiveTabWithTransition(ti3Memory,TTabTransition.Slide);
end;
end;
procedure TFormMain.GridLayoutResize(Sender: TObject);
begin
GridLayout.ItemHeight := GridLayout.Height / COLS-25;
GridLayout.ItemWidth := GridLayout.Width / ROWS;
end;
procedure TFormMain.FormShow(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := true;
Slider := TSliderPuzzle.Create(Self.ti2Runtime, TILES);
Slider.Height := GridLayout.Height;
Slider.Width := GridLayout.Width;
Slider.Align := TAlignLayout.Client;
//PuzzleGame
ReportMemoryLeaksOnShutdown := true;
Memory := TMemoryGame.Create(Self.ti3Memory, TILES);
Memory.Height := GridLayout.Height;
Memory.Width := GridLayout.Width;
Memory.Align := TAlignLayout.Client;
end;
end.
Call the assign() method of the FBitmap variable inside youe Set procedure:
procedure TTile.SetBitmap(bitmap :TBitmap);
begin
FBitmap.Assign(bitmap);
end;
Adding the following code to Tile class , fixed the issues.
type
private
FOnChangedBitmap : TNotifyEvent;
protected
procedure BitmapChanged;virtual;
procedure TTile.BitmapChanged;
begin
if Assigned(FOnChangedBitmap) then
FOnChangedBitmap(Self);
end;
procedure TTile.BitmapChangedDefault(Sender: TObject);
begin
(FControl as TImage).Bitmap := FBitmap;
end;
procedure TTile.SetBitmap(bitmap :TBitmap);
begin
FBitmap:=bitmap;
BitmapChanged;
end;
This all looks very complicated and perhaps it is.
But I solved a similar problem by simply setting the parent of the image:
Fheart := TImage.Create(self);
Fheart.Parent := self;
Fheart.SetSubComponent(true);
It seems unneccessary setting the parent when that is passed as the owner in the constructor - but it did solve my problem

Right Scrolling text

I am having one Delphi XE2 Project to display scrolling text (better "Marquee Text").
In my project I am having Timer1, Timer2, Button1, Button2, Label1 and Label2.
My object is to display some left scrolling text on Label1 after Button1.Click using Timer1 and some right scrolling text on Label2 after Button1.Click using Timer2.
I have defined the following codes:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
Button2: TButton;
Timer1: TTimer;
Timer2: TTimer;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled := true;
Timer2.Enabled := true;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Timer1.Enabled := false;
Timer2.Enabled := false;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Interval := 100;
Timer2.Interval := 100;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
const
{$WRITEABLECONST ON}
ScrollingText : string = 'This is left scrolling text ';
{$WRITEABLECONST OFF}
var
ScrollPosition: Integer;
begin
Label1.Caption := ScrollingText;
for ScrollPosition := 1 to (Length(ScrollingText) - 1) do
begin
ScrollingText[ScrollPosition] := Label1.Caption[ScrollPosition + 1];
ScrollingText[Length(ScrollingText)] := Label1.Caption[1];
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
const
{$WRITEABLECONST ON}
ScrollingText : string = 'This is right scrolling text ';
{$WRITEABLECONST OFF}
var
ScrollPosition: Integer;
begin
Label2.Caption := ScrollingText;
for ScrollPosition := (Length(ScrollingText) - 1) to 1 do
begin
ScrollingText[ScrollPosition] := Label2.Caption[ScrollPosition - 1];
ScrollingText[Length(ScrollingText)] := Label2.Caption[1];
end;
end;
end.
My problem is that Left Scrolling is happening using Timer1 but Right Scrolling is not happening using Timer2.
The for loop in Timer2Timer should run down instead of up:
procedure TForm1.Timer2Timer(Sender: TObject);
const
{$WRITEABLECONST ON}
ScrollingText : string = 'This is right scrolling text ';
{$WRITEABLECONST OFF}
var
ScrollPosition: Integer;
begin
Label2.Caption := ScrollingText;
for ScrollPosition := (Length(ScrollingText) - 1) downto 2 do
begin
ScrollingText[ScrollPosition] := Label2.Caption[ScrollPosition - 1];
ScrollingText[1] := Label2.Caption[Length(ScrollingText) - 1];
end;
end;
But I suggest not using writeable const nor using the for loop at all:
procedure TForm1.FormCreate(Sender: TObject);
begin
...
Label2.Caption := 'This is right scrolling text ';
end;
procedure TForm1.Timer2Timer(Sender: TObject);
var
S: String;
begin
S := Label2.Caption;
S := S[Length(S)] + Copy(S, 1, Length(S) - 1);
Label2.Caption := S;
end;

Resources