Delphi Graphics32 resize layer in ImgView32 - delphi

I want to be able to programatically resize one layer (the selected one) on click of a button.
So basically I have an ImgView32, and I add layers to it. The last one is selected, then I want to press a button and onClick of that button I want the selected layer to be enlarged...
I want to be able to draw lines horizontal and vertical, in order to allow the user to draw the layout of a house (in 2D). But I want the user to be able to resize a line without the mouse... so he should be able to enter the width and height in editboxes and on click of a button apply the dimensions to the respective (selected) line.
How can I do that in graphics32?
I tried like this:
var
orig,Tmp: TBitmap32;
Transformation: TAffineTransformation;
begin
Tmp := TBitmap32.Create;
Orig := TBitmap32.Create;
Transformation := TAffineTransformation.Create;
if Selection is TBitmapLayer then
begin
orig := TBitmapLayer(Selection).Bitmap;
try
Transformation.BeginUpdate;
Transformation.SrcRect := FloatRect(0, 0, orig.Width+200, orig.Height+200);
Transformation.Translate(-0.5 * orig.Width, -0.5 * orig.Height);
tmp.SetSize(200,200);
Transformation.Translate(0.5 * Tmp.Width, 0.5 * Tmp.Height);
Transformation.EndUpdate;
orig.DrawMode := dmTransparent;
Transform(Tmp, orig, Transformation);
orig.Assign(Tmp);
orig.DrawMode := dmTransparent;
finally
Transformation.Free;
Tmp.Free;
end;
end;
end;
But the selected layer remains the same size and the contents shrink...
I do not know what I am doing wrong.
Please help.
Thank you

Something like:
begin
if Selection is TBitmapLayer then
begin
TBitmapLayer(Selection).Location := FloatRect(TBitmapLayer(Selection).Location.Left,
TBitmapLayer(Selection).Location.Top, TBitmapLayer(Selection).Location.Right + 200, TBitmapLayer(Selection).Location.Bottom + 200);
end;
end;
would make the layer wider by 200 pixel (in both x- and y- dimension). Doing so, the content will (typically) be stretched, if not specified otherwise.
The ugly assignment can be written more elegantly using a function like IncreaseRect(), which however is not present, but must be written by yourself.
It could look like:
function IncreaseRect(SourceRect: TFloatRect; IncX, IncY: TFloat): TFloatRect;
begin
Result := FloatRect(SourceRect.Left, SourceRect.Top,
SourceRect.Right + IncX, SourceRect.Top + IncY);
end;
and called with
TBitmapLayer(Selection).Location := IncreaseRect(TBitmapLayer(Selection).Location, 200, 200);
Still I'm not sure if this is what you're after.

Related

Best practice regarding plotting graph data

Hope this is clear...
I want to know if the PaintBox control can allow the user to scroll, left to right through data? Imagine it like an oscilloscope display where a single capture allows zooming and scrolling. In this case I do not need zooming. So, my Paintbox is 800x600 and my data set is 16000x600.
I can plot in the 800x600 region as shown below, no problems at all, and can apply scaling to get all the data in, but I want to keep the Y-axis scaled to 1 and be able to scroll/drag left/right and view the data.
for J := 1 to ((Form1.Memo1.Lines.count)-1) do
begin
MyTorques[J] := StrToInt(Form1.Memo1.Lines[J]);
Tqmult := ((StrToInt(Label6.Caption) + 500) Div 600);
Ycalc[J] := ((MyTorques[J]) Div Tqmult);
InvY[J] := (600 - (Ycalc[J]));
X1 := (J-1);
Y1 := InvY[J-1];
X2 := (J);
Y2 := InvY[J];
with PaintBox1.Canvas do
begin
pen.Style := psSolid;
pen.Color := clBlack;
pen.Width := 1;
moveto(X1, Y1);
Lineto(X2, Y2);
Label51.Caption := IntToStr(X1);
Label52.Caption := IntToStr(Y1);
Label28.Caption := IntToStr(X2);
Label29.Caption := IntToStr(Y2);
Label35.Caption := IntToStr(Tqmult);
Label37.Caption := IntToStr(Ycalc[J]);
Label39.Caption := IntToStr(InvY[J]);
Label41.Caption := IntToStr(MyTorques[J]);
end;
if MyTorques[J] < Smallest Then
Begin
Smallest := MyTorques[J];
SmallestIndex := J;
end;
if MyTorques[J] > Largest Then
begin
Largest := MyTorques[J];
LargestIndex := J;
end;
Label30.Caption := IntToStr(Smallest);
Label31.Caption := IntToStr(SmallestIndex);
Label32.Caption := IntToStr(Largest);
Label33.Caption := IntToStr(LargestIndex);
end;
So, does my paintbox.canvas need to be sized 16000x600 with a "window" over the top sized 800x600, and the paintbox control is drag-able with vertical and horizontal constraints?
PaintBox by default does not have any scrolling support built in.
So if you want to have scrolling capabilities you will have to place your PaintBox into some other scrollable control like ScrollBox and set size of your PaintBox large enoought to contain rendering of your entire plot.
But this is a bad practice. Why? Doing so you will spend a lot of time painting your plot even thou only a part of it is visible to user at one time.
Instead you should be thinking of painting just part of your plot that can actually be visible by your user at the sima time (fits into PaintBox) and then redraw the plot when user scrolls to different position on a plot.

how to implement TTreeView OnCustomDrawItem so it draws completely and sets DefaultDraw to false

Using either TTreeView.OnAdvancedCustomDrawItem or TTreeView.CustomDrawItem, how does one completely draw an tree view item? There is no sample code on the docwiki for the Advanced custom draw item.
The Delphi documentation wiki has a confusing sample that references code that does not exist in the sample, and does not compile. I take it that the problem is split into three separate painting issues:
Get the theme element for the Windows tree view node and draw it.
Get any image that exists in the node and paint it.
Draw the text for the node.
Finally, one should set DefaultDraw := false so that the control doesn't do the default painting for the node.
It seems to me that such a basic thing should exist as a real working sample somewhere, but the closest I have found, is the documentation wiki which just says "See this other thing" and provides no link to the rest of the code.
The non-working sample with rather too much hand-waving and incomplete code, references some things that are not defined in this demo, but were obviously cut and pasted from a working sample. Some of the changes one would make are obvious (like define your own way to get the colors for the brush, and your own way to decide what font the node should be), and some are not, like how to implement DrawButton:
procedure TCustomDrawForm.TVCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
State: TCustomDrawState; var DefaultDraw: Boolean);
var
NodeRect: TRect;
begin
with TV.Canvas do
begin
{
If DefaultDraw it is true, any of the node's font
properties can be changed. Note also that when
DefaultDraw = True, Windows draws the buttons and
ignores our font background colors, using instead the
TreeView's Color property.
}
if cdsSelected in State then
begin
Font.Assign(SelectedFontDialog.Font);
Brush.Color := SelBkgColorDialog.Color;
end;
DefaultDraw := False; // FDefaultDrawItem;
{
DefaultDraw = False means you have to handle all the
item drawing yourself, including the buttons, lines,
images, and text.
}
if not DefaultDraw then
begin
//draw the selection rect.
if cdsSelected in State then
begin
NodeRect := Node.DisplayRect(True);
FillRect(NodeRect);
end;
NodeRect := Node.DisplayRect(False);
if None1.Checked then
//no bitmap, so paint in the background color.
begin
Brush.Color := BkgColorDialog.Color;
Brush.Style := FBrushStyle;
FillRect(NodeRect)
end
else
//don't paint over the background bitmap.
Brush.Style := bsClear;
NodeRect.Left := NodeRect.Left + (Node.Level * TV.Indent);
// NodeRect.Left now represents the left-most portion
// of the expand button
DrawButton(NodeRect, Node); // See the CustomDraw demo
NodeRect.Left := NodeRect.Left + TV.Indent + FButtonSize;
//NodeRect.Left is now the leftmost portion of the image.
DrawImage(NodeRect, Node.ImageIndex); // See the CustomDraw demo
NodeRect.Left := NodeRect.Left + ImageList.Width;
//Now we are finally in a position to draw the text.
TextOut(NodeRect.Left, NodeRect.Top, Node.Text);
end;
end;
end;

How to set the form position and font size in a KOL form

I have two questions about KOL.
I have a main form. As I see this placed on the TForm's position.
I wanna put it to the screen center.
How can I access it's coordinates, or the handle for "SetWindowPos"?
I have 4 labels in the form. For 2 of them I want to use smaller fonts. But I don't found any Font Size property... How to do it then?
1.1. How to center form on screen ?
Use the CenterOnForm method. As description says, if it's applied to a form, centers form on screen:
Form.CenterOnForm(nil);
1.2. How to get form position ?
Just like in Delphi by the Left and Top properties or e.g. by the Position property. The following pseudo-code results to the same:
ShowMessage('Form pos.: [' +
Int2Str(Form.Left) + '; ' +
Int2Str(Form.Top) + ']'
);
ShowMessage('Form pos: [' +
Int2Str(Form.Position.X) + '; ' +
Int2Str(Form.Position.Y) + ']'
);
Note, that unless you change the form position by yourself or until the applet is running, both properties returns 0. The form position, if you didn't set it, is adjusted when the applet starts.
1.3. How to get form handle ?
Use either the Handle property or the GetWindowHandle method. The following pseudo-code results to the same:
ShowMessage(Int2Str(Form.Handle));
ShowMessage(Int2Str(Form.GetWindowHandle));
2. How to change the font size ?
As #David already mentioned in his post, use the Font.FontHeight property. Just one sidenote, the default font when you create e.g. that label is set to System to which is not possible to change the size, so don't be surprised when the size changes won't be applied. Here's a quote from the source:
Value 0 (default) says to use system default value, negative values
are to represent font height in "points", positive - in pixels. In XCL
usually positive values (if not 0) are used to make appearance
independent from different local settings.
And here's a sample usage shown on positioned label creation:
Label1 := NewLabel(Form, 'Label1').SetPosition(8, 8);
Label1.Font.FontName := 'Tahoma';
Label1.Font.FontHeight := -11;
3. Example project
program Project1;
uses
KOL;
type
PForm1 = ^TForm1;
TForm1 = object(TObj)
Form, Label1, Label2, Label3, Label4: PControl;
end;
var
Form1: PForm1;
procedure CreateForm(var Result: PForm1; AParent: PControl);
begin
New(Result, Create);
with Result^ do
begin
Form := NewForm(AParent, 'Caption').SetSize(320, 240);
Form.CenterOnForm(nil);
Label1 := NewLabel(Form, 'Label1').SetPosition(8, 8);
Label1.Font.FontName := 'Tahoma';
Label1.Font.FontHeight := -11;
Label2 := NewLabel(Form, 'Label2').SetPosition(72, 8);
Label2.Font.FontName := 'Tahoma';
Label2.Font.FontHeight := -11;
Label3 := NewLabel(Form, 'Label3').SetPosition(136, 8);
Label3.Font.FontName := 'Tahoma';
Label3.Font.FontHeight := -15;
Label4 := NewLabel(Form, 'Label4').SetPosition(200, 8);
Label4.Font.FontName := 'Tahoma';
Label4.Font.FontHeight := -15;
end;
end;
begin
Applet := NewApplet('Test');
CreateForm(Form1, Applet);
Run(Applet);
end.
Question 1
Call the SetPosition and SetSize methods on the form.
Form.SetPosition(x,y);
Form.SetSize(w,h);
Question 2
Use Font.FontHeight.
MyControl.Font.FontHeight := ...;
If you want to call SetWindowPos, you can retrieve the handle calling the GetWindowHandle method of the returned pointer.
Please, don't ask more than 1 question per question.

How to identify zoom direction (in/out) using gestures and apply the zoom effect?

Using Delphi XE 2 I've trying to identify the zoom direction to apply the zoom efect to an image (TImage) but didnt find an function to do it and the EventInfo property in the event OnGesture of the Image didnt have this information.
I've seen a lote of samples using Direct2d to zoom in and zoom out but it uses the wp_touch messages direct to do it and the zoom effects are performed using transform matrix scale functions from direct 2d but i dont want to use direct2d to this project as it will only have this zoom in and out effect based on touch, the other things is simple clicks.
It could be possible to identify the in/out storing the first direction and comparing to the current one since the EventInfo parameter has an property Direction but i dont think this is the nice way to do it or i'm wrong ?
So after that is there any recomendations or examples on how to perform the zoom effect in a TImage ? I've already done that but it does not pan with when zooming to give the pinch efect that every application does.
After reading a lote of documentations I found out that the right way to do is:
Intercept the EventInfo.GestureID to identify the command desired in my case the zoom command, after that you should read the EventInfo.Flags and to identify if it is the gfBegin so you can cache the first location point (x,y) and the first distance and when the flag is diferent then gfBegin you perform your calculations using the firstpoint and the currentpoint (EventInfo.Location)
Basic the command should be like that:
case EventInfo.GestureID of
igiZoom:
begin
if (EventInfo.Flags = [gfBegin]) then
begin
FLastDistance := EventInfo.Distance;
FFirstPoint.X := EventInfo.Location.X;
FFirstPoint.Y := EventInfo.Location.Y;
FFirstPoint := ScreenToClient(FFirstPoint);
if (FSecondPoint.X = 0) and (FSecondPoint.Y = 0) then
begin
FSecondPoint.X := EventInfo.Location.X + 10;
FSecondPoint.Y := EventInfo.Location.Y + 10;
FSecondPoint := ScreenToClient(FSecondPoint);
end;
//ZoomCenter is a local TPoint var
ZoomCenter.Create(((FFirstPoint.X + FSecondPoint.X) div 2),
((FFirstPoint.Y + FSecondPoint.Y) div 2));
//Apply the zoom to the object
FDrawingObject.Zoom(EventInfo.Distance / FLastDistance, ZoomCenter.X, ZoomCenter.Y);
Invalidate;
end
else
begin
FSecondPoint.X := EventInfo.Location.X;
FSecondPoint.Y := EventInfo.Location.Y;
FSecondPoint := ScreenToClient(FSecondPoint);
ZoomCenter.Create(((FFirstPoint.X + FSecondPoint.X) div 2),
((FFirstPoint.Y + FSecondPoint.Y) div 2));
FDrawingObject.Zoom(EventInfo.Distance / FLastDistance, ZoomCenter.X, ZoomCenter.Y);
Invalidate;
//Update with the new values for next interaction
FFirstPoint := FSecondPoint;
FLastDistance := EventInfo.Distance;
end;
There is a sample code write in c# available in the Windows v7.0 SDK that can be used as reference and helps me a lote.
For recent Delphi release, EventInfo has a Distance property. We don't have to calculate it.
For interactive gesture like zoom, look at the sample code in docwiki :
http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/FMXInteractiveGestures_(Delphi)
procedure TForm36.handleZoom(EventInfo: TGestureEventInfo);
var
LObj: IControl;
image: TImage;
begin
LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
if LObj is TImage then
begin
if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then
begin
image := TImage(LObj.GetObject);
image.Width := image.Width + (EventInfo.Distance - FLastDIstance)/2;
image.Height := image.Height + (EventInfo.Distance - FLastDIstance)/2;
image.Position.X := image.Position.X - (EventInfo.Distance - FLastDIstance)/2;
image.Position.Y := image.Position.Y - (EventInfo.Distance - FLastDIstance)/2;
end;
end;
FLastDIstance := EventInfo.Distance;
end;

Delphi - Open window at location of a TLabel

I have two forms, Form1 and Form2
I have a TLabel on Form1 which an onclick event which calls Form2.show;
What I am trying to do, if figure out how I can make form2 show 5px below the label centered between the label :) Form2 is small and just shows some options.
I can use the mouse position but it's not quite good enough.
I was thinking something like
// Set top - add 20 for the title bar of software
Form2.Top := Form1.Top + Label1.Top + Label1.Height + 20;
// Set the Left
Form2.Left := Form1.Left + Label1.Left + round(Label1.Width / 2) - round(form2.Width/2);
but I think there can be a better way
You need to set the coordinates for Form2 using the coordinate system of it's Parent. Assuming the Parent is the Desktop (since you're attempting to compensate for the height of the title bar), this can do it:
procedure ShowForm;
var P: TPoint;
begin
// Get the top-left corner of the Label in *screen coordinates*. This automatically compensates
// for whatever height the non-client area of the window has.
P := Label1.ScreenToClient(Label1.BoundsRect.TopLeft);
// Assign the coordinates of Form2 based on the translated coordinates (P)
Form2.Top := P.Y + 5; // You said you want it 5 pixels lower
Form2.Left := P.X + 5 + (Label1.Width div 2); // Use div since you don't care about the fractional part of the division
end;
You'll need to adapt the code for the positioning of Form2 based on your centering requirement, I didn't quite understand what you want. And of course, if a frame or panel is enough, it's better! Take a good look at Guillem's solution.
procedure TForm2.AdjustPosition(ARefControl: TControl);
var
LRefTopLeft: TPoint;
begin
LRefTopLeft := ARefControl.ScreenToClient(ARefControl.BoundsRect.TopLeft);
Self.Top := LRefTopLeft.Y + ARefControl.Height + 5;
Self.Left := LRefTopLeft.X + ((ARefControl.Width - Self.Width) div 2);
end;
Then you can have the form adjust itself relative to any desired control as follows:
Form2.AdjustPosition(Form1.Label1);
Do you really need Form2 to be a form? You could choose to create a frame containing the Form2 logic and use a hidden TPanel as its parent. When the user clicks on the Label1 you show then the panel.
Something like following. When you create Form1 or when you click on Label1 (depending on your needs):
Frame := TFrame1.Create(Self);
Frame.Parent := Panel1;
In the OnClick event for Label1:
Panel1.Top := Label1.Top + 5;
Panel1.Left := Label1.Left + round(Label1.Width / 2) - round(form2.Width/2);
Panel1.Visible := true;
When the user is done just hide the panel again (and destroy the Frame if necessary). If you keep the Frame alive while the user is using Form1 remember to free it when leaving the form.
HTH
The ClientOrigin property will return the lebel's upper-left corner in screen coordinates, so you do not need to determine it manually:
var
Pt: TPoint;
begin
Pt := Label1.ClientOrigin;
Form2.Left := Pt.X + Round(Label1.Width / 2) - Round(Form2.Width/2);
Form2.Top := Pt.Y + Label1.Height + 5;
end;

Resources