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;
Related
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.
Delphi Xe4. Form, ActionManager, ImageList (with 32x32 Icons), ActionMainMenuBar.
I can not ensure that the icons are displayed correctly. What should you do?
At the same time, if I apply any vcl style of decoration, it displays fine. But if the style of "Windows" by default, the text moves out of the icon. Help.
Sorry for the bad English.
This is a valid question, the TActionMainMenuBar is meant to be designed to be able to handle custom icon sizes as menu images, just as the native menus can handle them fine. One indication of that can be found in the comments in the code, f.i. in the below VCL code you can find the comment 16 is standard image size so adjust for larger images.
The faulty code, I believe, is in TCustomMenuItem.CalcBounds in 'ActnMenus.pas'. Below excerpt is from D2007. Notice the line below I commented with some exclamation marks. After the ascendant class TCustomActionControl calculates the positioning of text and image in its CalcLayout method, the TCustomMenuItem ruins it with the hard-coded 24 in the said statement.
procedure TCustomMenuItem.CalcBounds;
var
AWidth, AHeight: Integer;
NewTextBounds: TRect;
ImageSize: TPoint;
ImageOffset: Integer;
begin
inherited CalcBounds;
ImageSize := GetImageSize;
AHeight := FCYMenu;
if Separator then
AHeight := FCYMenu div 3 * 2
else
// 16 is standard image size so adjust for larger images
if ImageSize.Y > 16 then
AHeight := ImageSize.Y + 4;
if ActionClient = nil then exit;
if ImageSize.X <= 16 then
ImageOffset := 24
else
ImageOffset := ImageSize.X + 6; // Leave room for an image frame
NewTextBounds := TextBounds;
OffsetRect(NewTextBounds, 24 - TextBounds.Left, // <- !!!!!
AHeight div 2 - TextBounds.Bottom div 2 - 1);
TextBounds := NewTextBounds;
ShortCutBounds := Rect(0,0,0,0);
if ActionClient.ShortCut <> 0 then
begin
Windows.DrawText(Canvas.Handle, PChar(ActionClient.ShortCutText), -1,
FShortCutBounds, DT_CALCRECT);
// Left offset is determined when the item is painted to make it right justified
FShortCutBounds.Top := TextBounds.Top;
FShortCutBounds.Bottom := TextBounds.Bottom;
AWidth := TextBounds.Right + FShortCutBounds.Right + ImageOffset + Spacing;
end
else
AWidth := TextBounds.Right + TextBounds.Left;
SetBounds(Left, Top, AWidth, AHeight);
end;
The 24 is an assumption based on images having 16 or less pixels width. What should be used instead is the ImageOffset value calculated just a few lines above. Replace
OffsetRect(NewTextBounds, 24 - TextBounds.Left,
AHeight div 2 - TextBounds.Bottom div 2 - 1);
with
OffsetRect(NewTextBounds, ImageOffset - TextBounds.Left,
AHeight div 2 - TextBounds.Bottom div 2 - 1);
and you'll have something like this:
You'll notice some other weirdness though, items not having images are still settling for a small image layout. IMO all menu items should have the same basic layout, but the design of action menus allow different layouts for individual items. One other weird thing is the checked state of an item with an image ('Action6'), although I'm not sure if I'm missing a setting here or if it would qualify as a bug otherwise.
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.
I have created one TScrollBox. I have added the Label and Edit Box on it dynamically on Button click. For setting the location of component i have used the height,width,left,top property of components.
But when Scroll Bar gets appeared on screen after 5 components added, the next components location gets disturbed. and the next component is not placed in synchronous manner on ScrollBox.
The Top coordinate for controls placed on a ScrollBox need to take into account the amount of "scroll" that already took place. If you add the controls all at once this is not a problem, because the ScrollBox doesn't get the chance to "scroll".
If you add controls to the ScrollBox after it got a chance to "scroll", you need to take into account the amount of vertical "scroll" that took place. Here's a sample piece of code that will add labels to ScrollBox1, taking vertical scroll into account so controls don't overlap each other. Here I'm using the form's "Tag" property to hold the Top for the next control added, and I'm also using Tag to generate unique names for the labels (so you can see they're going into the ScrollBox at the correct coordinates).
procedure TForm31.Button1Click(Sender: TObject);
var L: TLabel;
begin
L := TLabel.Create(Self);
L.Caption := 'Test: ' + IntToStr(Tag);
L.Parent := ScrollBox1;
L.Top := Tag + ScrollBox1.VertScrollBar.Size - ScrollBox1.VertScrollBar.Position;
Tag := Tag + L.Height;
end;
An other approach I sometimes used is to keep track of the last control added and base the coordinates for the new control on the coordinates of that last added control:
var LastControl: TControl;
procedure TForm31.Button1Click(Sender: TObject);
var L: TLabel;
begin
L := TLabel.Create(Self);
L.Caption := 'Test: ' + IntToStr(Tag);
L.Parent := ScrollBox1;
if Assigned(LastControl) then
L.Top := LastControl.Top + LastControl.Height
else
L.Top := 0;
Tag := Tag + L.Height;
LastControl := L;
end;
And yet an other approach would be to find the lowest control and add your control based on it's coordinates:
procedure TForm31.Button1Click(Sender: TObject);
var L: TLabel;
Bottom, TestBottom: Integer;
i: Integer;
begin
// Find "Bottom"
Bottom := 0;
for i:=0 to ScrollBox1.ControlCount-1 do
with ScrollBox1.Controls[i] do
begin
TestBottom := Top + Height;
if TestBottom > Bottom then
Bottom := TestBottom;
end;
L := TLabel.Create(Self);
L.Caption := 'Test: ' + IntToStr(Tag);
L.Parent := ScrollBox1;
L.Top := Bottom;
Tag := Tag + L.Height;
end;
Ok, here's the problem. I have a label component in a panel. The label is aligned as alClient and has wordwrap enabled. The text can vary from one line to several lines. I would like to re-size the height of the the panel (and the label) to fit all the text.
How do I get the necessary height of a label when I know the text and the width of the panel?
You can use the TCanvas.TextRect method, along with the tfCalcRect and tfWordBreak flags :
var
lRect : TRect;
lText : string;
begin
lRect.Left := 0;
lRect.Right := myWidth;
lRect.Top := 0;
lRect.Bottom := 0;
lText := myLabel.Caption;
myLabel.Canvas.Font := myLabel.Font;
myLabel.Canvas.TextRect(
{var} lRect, //will be modified to fit the text dimensions
{var} lText, //not modified, unless you use the "tfModifyingString" flag
[tfCalcRect, tfWordBreak] //flags to say "compute text dimensions with line breaks"
);
ASSERT( lRect.Top = 0 ); //this shouldn't have moved
myLabel.Height := lRect.Bottom;
end;
TCanvas.TextRect wraps a call to the DrawTextEx function from the Windows API.
The tfCalcRect and tfWordBreak flags are delphi wrappers for the values DT_CALCRECT and DT_WORDBREAK of the windows API. You can find detailed information about their effects in the DrawTextEx documentation on msdn
Use TextWidth and TextHeight.
See an example here:
http://www.greatis.com/delphicb/tips/lib/fonts-widthheight.html
TextWidth will tell you how wide the text would be, and then you can divide that by the control width to see how many rows you need. The remainder of the division should be an additional row.
You can use one line of code for this:
label.width := label.canvas.textwidth(label.caption);
or you can set the label's autosize property to true in the object inspector.
If you can align it alTop and keep AutoSize on then TLabel will auto adjust the height after settign the caption.
in FMX there is a trick to do that simply :
when creating a Label set Autosize := true and use the OnResize Event to update the size of the parent...
Rectangle1 := TRectangle.create(Form1);
Rectangle1.parent := Form1;
Label1 := TLabel.create(Rectangle1);
Label1.parent := Rectangle1;
Label1.Align := TAlignLayout.Top; // keep the same width and auto size parent height
Label1.OnResize := DoReSize;
Label1.WordWrap := true;
Lable1.Autosize := true;
The parent size will be updated here (assuming that the Sender object is the most bottom control in the parent, if not you need to arrange this function to summarize all the components size and verticaly)
procedure DoParentResize(Sender : TObject);
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Height + 4;
end;
if we use Label1.Align := TALignLayout.None;
then we should add the position inside the parent :
procedure DoParentResize(Sender : TObject);
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Position.Y + TControl(Sender).Height + 4;
end;
Wich result in a single function for (almost) all cases :
procedure TForm1.DoParentResize(Sender : TObject);
begin
if TControl(Sender).Align in [TAlignLayout.None, TAlignLayout.Client, TAlignLayout.Center, TAlignLayout.VertCenter ] then
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Position.Y + TControl(Sender).Height + 4;
end
else
begin
TControl(TControl(Sender).parent).Height := TControl(Sender).Height + 4;
end;
end;
You need to reduce the LRect.right by the label left and right margins, and then add the label top and bottom margins to the label height at the end or the text might not fit the label.
procedure TFrm.PatternEditTyping(Sender: TObject);
begin
(Sender as Tedit).Canvas.Font.Size := (Sender as Tedit).Font.Size;
(Sender as Tedit).Width := (Sender as Tedit).Canvas.TextWidth((Sender as Tedit).Text);
end;
This code adjusts Tedit.Width while you type inside it. Just keep the font family in Canvas and in Tedit the same.