What component to derive my 'TCard' from? (game) - delphi

I am trying to make a TCard component for a game. What class should I derive it from?
This is for a card game like MTG or yu gi oh. The card should have a blank image, and when created it will load either a front or back view.
If it loads the front view, it will then have to have a few labels (for properties like power/cost/def/text). Cards must be clickable.
type
TCard = class(zzzzzzzzz)
private
Now once that is done, do I have to add anything to the constructor/destructor? Currently I have:
constructor TCard.Create(AOwner: Tcomponent);
begin
inherited Create(AOwner);
end;
{******************************************************************************}
{ Free any resources allocated to component }
destructor TCard.Destroy;
begin
inherited Destroy;
end;
Also I think I added the onclick parts right but not sure. In the published area I have
{Inherited properties}
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnClick;
property OnDblClick;
etc...

It depends on what you want to do, but typically there are two ancestors for visible controls:
TGraphicControl: This is a descendant of TControl that implements a Canvas for you. You can just override the Paint method to start drawing. Controls like this support mouse interactions, but cannot get keyboard focus.
TCustomControl: This a descendant of TWinControl. It also implements a Canvas and allows you to override the Paint method to draw any content. Because it descends from TWinControl, it actually has a handle and can gain keyboard focus and process keyboard messages.
An other good candidate is TPanel (or rather TCustomPanel). It inherits from TCustomControl, so it has the same graphical properties, but it also contains functionality to draw borders and align child controls. I doubt if you would need this, though.
Of course you can derive directly from TControl or TWinControl, but then you will have to take care of some of this stuff yourself.
Note that it is better to put the actual card game logic in a separate class and only create visual controls for drawing. If you do that, you can still choose whether you want to have separate controls for each card, or you can choose to draw your whole card game on a single control or even directly on the form. I doubt if Windows' card games like Free Cell and Solitaire have over 50 graphics controls.

Related

How to pass a component as a parameter of a procedure in Delphi?

I want to be able to use one procedure to center all the components on a form. This is the kind of thing I'm going for:
procedure TForm4.centerComponent(x: Tobject);
begin
x.Left := (Form4.ClientWidth - x.Width) div 2;
end;
I would only be passing built in components (memo, label, edit etc...)
I get the feeling this is either not possible or if it is its probably not best practice
This is easy, but you must be careful about terminology:
A TObject is any Delphi object. It need not be a control. It doesn't even need to be something you can drop on a form.
A TComponent is an object you can drop on a form. It might be a visual control (like a button, a label, or an edit box), or it might be a non-visual component (like a TActionList).
A TControl is a visual control, like a button, a label, an edit box, or an animated analogue clock.
The above classes are ordered by inheritance.
So, you want a procedure that acts on TControls in general:
procedure Centre(AControl: TControl);
var
Parent: TWinControl;
begin
Parent := AControl.Parent;
if Parent = nil then
Exit;
AControl.SetBounds(
(Parent.ClientWidth - AControl.Width) div 2,
(Parent.ClientHeight - AControl.Height) div 2,
AControl.Width,
AControl.Height
);
end;
Every TControl has Top, Left, Width, and Height properties, as well as the SetBounds method, which we use above.
Notice that I centre the control in its parent window. (A control's Top and Left values are always relative to its parent.)
Now, there are two kinds of controls in Delphi:
Controls that are actual Win32 windows (with HWNDs).
Controls that are not actual Win32 windows.
Only the former kind of control can have child controls. These controls derive from TWinControl. That's the reason I declare Parent as a TWinControl. This is also the type of the TControl.Parent property.
Some notes about your code
x.Left := (Form4.ClientWidth - x.Width) div 2;
Here there are two issues (except for x: TObject not having any Width or Left properties):
Form4 is one particular instance of the form class. It is much better to write Self.ClientWidth or simply ClientWidth, so you refer to the current instance of the form class.
But even this is not good enough, because this only works if the form is the parent of x. x might well have a different parent. For instance, x might have a TPanel as its parent (the TPanel's parent being the form).

TGraphicControl transparency on a TCustomControl

I created a control based on TGraphicControl that is transparent and mostly empty space. It actually implements a simple symbol in line art.
i.e. TLFMagicControl = class(TGraphicControl)
In the constructor which I have:
ControlStyle := ControlStyle + [csOpaque];
My "TLFMagicControl" is then placed on my own panel that is a TCustomControl.
i.e. TLFGridPanel = class(TCustomControl)
The transparency of the TLFMagicControl works perfectly interacting with each other but not with the parent panel they are on (TLFGridPanel).
The TLFGridPanel spends most of its time just black so its not an issue but I want to user to be able to turn on/off grid lines on the panel. When I override the paint handler TLFGridPanel and draw my grid the controls placed on top are not transparent and block the grid lines underneath.
Is there a way around this for a TCustomControl or have I chosen the wrong base for my panel?
csOpaque tells the VCL that you draw the control entirely by yourself, which supresses the automatic background drawing (or erasing). Remove that control style in order to let WM_ERASEBKGND do its work.

Set DevExpress Grid Tableview Editable property and determine its parent via RTTI

I have a project with multiple TcxGrids on. The class hierarchy structure of the grids in my project are as follows:
TForm->TPanel->TcxPageControl->TcxTabSheet(subclassed)->TcxGrid->TcxGridLevel->TcxGridDBBandedTableView
In my subclassed TcxTabSheet I have added a new property "ReadOnly" that when set loops over the tabsheets controls and sets them to enabled/disabled respectively.
When a TcxGrid controls enabled property is set to true, the user can no longer interact with the grid at all (including navigating).
It seems I need to set the OptionsData.Editing property on the TcxGridDBBandedTableView to achieve a readonly but still navigable grid control.
Simple enough until you factor in that I want to do this in a loose coupled manner which I think leaves me the option of RTTI.
I have written the following code that loops over the forms controls (looping over the tabs controls or components doesn't give me access to the TcxGridDBBandedTableView). Once the control is found I can set its editing property via RTTI. I just don't seem to able to determine if that TcxGridDBBandedTableView belongs to the TabSheet it sits on.
var
compIdx: Integer;
begin
for compIdx := 0 to Pred(ComponentCount) do
if (Components[compIdx].ClassNameIs('TcxGridDBBandedTableView')) then
SetOrdProp(GetObjectProp(Components[compIdx], 'OptionsData'), 'Editing', Ord(not FReadOnly));
end;
TL;DR
How can I determine what pagectrl tab a cxgrid is on and set its TableView.OptionsData.Editable property without adding any devexpress units to the uses clause of the unit.
You are iterating over the components owned by the form. I think that's the wrong approach. You should be looking at the parent/child relationship rather than ownership. Not least because it's perfectly possible for a form to contain controls that it does not own. So, your approach can fail to find controls, particularly dynamically created controls.
So, if you have a tabsheet (or indeed any windowed control), you can iterate over its children like this:
for i := 0 to TabSheet.ControlCount-1 do
DoSomething(TabSheet.Controls[i]);
If your target grid control is a direct descendent of the tabsheet then this will be enough. If it is more than one level deep in the hierarchy then you will need a recursive solution. I will leave that recursive solution as an exercise for you.
Suppose that you have a control and want to find the tabsheet that it sits inside, then you need to walk up the parent chain. Like this:
function GetParentOfClass(Control: TControl; AClass: TWinControlClass): TWinControl;
var
Control: TWinControl;
begin
while Assigned(Control) and not (Control is AClass) do
Control := Control.Parent;
Result := TWinControl(Control);
end;

Adding Custom control to another custom control

What i am trying to accomplish is to create new touchkeyboard.
First i created buttons which i derive from speed buttons.
That done, now i need to create multiple buttons and layout them somewhere. This is were i get stuck.
I created a new component which i derive from TGraphicControl (this should be my new touchkeyboard), but i don't know how to add components to canvas. I actually don't know whether i'm supposed to add them to canvas or to some other component (eg. panel)!?!
Is my approach OK?
Thanks in advance.
If you're creating a custom visual control, you need to create the buttons and position them manually. For example:
TOnScreenKeyboard = class(TWinControl)
public
constructor Create(AOwner: TComponent);
end;
[...]
constructor TOnScreenKeyboard.Create(AOwner : TComponent)
var
TempButton : TSpeedButton;
begin
inherited;
TempButton := TSpeedButton.Create(self);
TempButton.Parent := self;
TempButton.Top := 10;
TempButton.Left := 15;
TempButton.Caption := 'A';
end;
You can put the button creation into a loop and position each one according to where it should be.
(I wrote this off the top of my head, and I don't write a lot of Pascal anymore, so there may be some minor mistakes! But it should get you started.)
Because of your wording and confusion between Panel, Canvas and custom controls in general, I assume you're a Delphi beginner. You need to learn about frames: embarcadero docwiki link on frames
Frames allow you to create re-usable portions of GUI. You use the IDE to "draw" the frame, you can then place that composite control (the frame) onto forms or other frames. It's a very powerful feature and it's conceptually very close to what other languages call "custom controls" (very close to what asp.net or WPF consider a custom control to be).
In the Delphi world, when you say "custom control", people would normally expect you to want to create an reusable control that's placed in a package and it's installed in the IDE. It's an fairly advanced subject. If that's what you want then I misunderstood the question, sorry.

Is there a Delphi 'joystick' control?

I'd like to align an object in X and Y by eye using the mouse to move it into position. I can roll a solution by using various spin controls (or buttons) but it's messy and I got to wondering whether there is a control - like a joystick - that provides 2-axis control using the mouse and fires events at rates which vary depending on its position?
Afaik Jedi (jedi apilib?) had a joystick header too. It is winapi, not COM, so no TLB involved
Try NLDJoystick, an open-source component written by me and based on pure WinAPI (MMSystem.pas). It is downloadable from GitHub.
The interface:
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property AbsCenter;
property AbsMax;
property AbsMin;
property Active default False;
property Advanced default False;
property AxisCount;
property Axises;
property ButtonCount;
property HasPOV;
property ID;
property OnButtonDown;
property OnButtonUp;
property OnMove;
property OnPOVChanged;
property PollingInterval default 40;
property RepeatButtonDelay default 350;
property RepeatMoveDelay default 350;
property SuspendScreensaver default False;
property ThresholdFactor;
Maybe you can make something like that yourself.
Take a panel, and register on Mouse up, down and move events
On MouseDown, set a boolean (fButtonDown) so you know that the mousebutton is pressed and save the X and Y coordinates of the mousepointer.
On MouseMove, check if a button is down (fButtonDown) and move your object. The more your mousecursor is moved from its saved position, the faster you move.
On MouseUp, set fButtonDown to false
The above is very primitive, but should work.
I Googled for "joystick dll" and found countless. You can probably throw one of these into your project and then access its C API from Delphi by simply writing a TLIB header (or whatever it's called, haven't done this in a while) for it.
You may use a DelphiX components. They are wrappers for DirectX and one of them wraps around DirectX joystick interface as far as I remember. It wraps in a Delphi-style so it is easy to use.

Resources