How to convert workspace coordinates to screen coordinates? - delphi

I want to convert the workspace coordinates returned by GetWindowPlacement in rcNormalPosition.Left and rcNormalPosition.Top to screen coordinates that I can assign later to MainForm.Left and MainForm.Top. How can I do that ?

You can use the monitor property of your form to determine if the workspace of the monitor that the form is on has got any offset with the monitor's placement. E.g.
ScreenLeft := wplc.rcNormalPosition.Left +
Monitor.WorkareaRect.Left - Monitor.Left;
ScreenTop := wplc.rcNormalPosition.Top +
Monitor.WorkareaRect.Top - Monitor.Top;

The simplest and cleanest way is to use the API function that partners with GetWindowPlacement, namely SetWindowPlacement. That way you don't need to convert between workspace and screen coordinates because you let the system do the work for you.
var
WindowPlacement: TWindowPlacement;
....
WindowPlacement.length := SizeOf(WindowPlacement);
Win32Check(GetWindowPlacement(Handle, WindowPlacement));
....
Win32Check(SetWindowPlacement(Handle, WindowPlacement));
In the above code, Handle is assumed to be the window handle of the form.
If you have persisted the left and top then you'd restore them like this:
var
WindowPlacement: TWindowPlacement;
....
WindowPlacement.length := SizeOf(WindowPlacement);
Win32Check(GetWindowPlacement(Handle, WindowPlacement));
WindowPlacement.rcNormalPosition.Left := NewLeft;
WindowPlacement.rcNormalPosition.Top := NewTop;
Win32Check(SetWindowPlacement(Handle, WindowPlacement));

Related

Font orientation in TDirect2DCanvas is not working?

I need to draw angled text on TDirect2DCanvas, but no success.
procedure TForm1.FormPaint(Sender: TObject);
var
LCanvas: TDirect2DCanvas;
const
myText = 'Kikimor';
begin
LCanvas := TDirect2DCanvas.Create(Canvas, ClientRect);
LCanvas.BeginDraw;
try
LCanvas.Font.Orientation := 90;
LCanvas.TextOut(100,100,myText);
finally
LCanvas.EndDraw;
LCanvas.Free;
end;
end;
No matter what angle I give for orientation, it always draws a straight text.
Is orientation not working or I need to do something else?
Setting TDirect2DCanvas.Font.Orientation does not have any effect (most likely not implemented, sorry, no time to debug). Direct2D wrapper supplied in Delphi is very basic.
To achieve your goal, apply transformation by hand:
procedure TForm1.FormPaint(Sender: TObject);
var
LCanvas: TDirect2DCanvas;
currentTransform: TD2D1Matrix3x2F;
ptf: TD2DPoint2f;
const
myText = 'Kikimor';
begin
LCanvas := TDirect2DCanvas.Create(self.Canvas, ClientRect);
LCanvas.BeginDraw;
try
// backup the current transformation
LCanvas.RenderTarget.GetTransform(currentTransform);
ptf.x:= 100.0; ptf.y:= 100.0; //rotation center point
// apply transformation to rotate text at 90 degrees:
LCanvas.RenderTarget.SetTransform(TD2D1Matrix3x2F.Rotation(90, ptf));
// draw the text (rotated)
LCanvas.TextOut(100, 100, myText);
// restore the original transform
LCanvas.RenderTarget.SetTransform(currentTransform);
finally
LCanvas.EndDraw;
LCanvas.Free;
end;
end;
For more extensive information/effects you can look at:
Drawing text using the IDWriteTextLayout.Draw()
The whole Direct2D category at the same site is also interesting (use Google Translate).
For those using C++ Builder I got this to work:
#include <Vcl.Direct2D.hpp>
// needed for the D2D1::Matrix3x2F::Rotation transform
#ifdef _WIN64
#pragma comment(lib,"D2D1.a")
#else
#pragma comment(lib,"D2D1.lib")
#endif
TD2DPoint2f point; // rotation centre
point.x = 100.0;
point.y = 100.0;
canvas_2d->RenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(90, point));
canvas_2d->TextOut(100, 100, text);
// restore 0 rotation afterwards
canvas_2d->RenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(0, point));
Note that trying to use GetTransform like in the Delphi version causes an exception, so I cleared the transform by passing it a new one with zero rotation, there may be a better way to do this.
The pragma is needed due to a link error, see this answer for details.

Is there an alternative to TBitBtn that scales correctly at higher screen resolutions - or alternative a piece of code that fixes this issue?

I am dynamically creating a number of TBitBtn with custom bitmaps. Works nice, except if the screen is high res - which causes positioning and size to change. The other controls on the form are not affected.
Do not know what to try.
BitBbegersopp:= TbitBtn.Create(Form2);
with BitBbegersopp do
begin
Parent:=Form2;
Glyph.LoadFromFile('beger.bmp');
OnClick:= BitBbegersoppClick;
Left:= Start.Left + HDistStartB + 0*HSpacingBitB;
Height:= HSizeBitB;
Width:= VSizeBitB;
Top:= Start.Top + VDistStartB + 0*VSpacingBitB;
Hint:= 'Begersopp, sporer på oversiden';
ShowHint:= True;
Tag:= 1;
end;
Although you didn't provide an example, which leaves us with a lot of guessing, I can see two problems in your code.
The scaling to the current PPI is done inside the assignment to Parent.
In case Start is an existing control, its Left and Top properties are already scaled while the offsets used as well as the values for Width and Height are probably not.
To tackle both problems I suggest the following code sequence:
BitBbegersopp:= TbitBtn.Create(Form2);
with BitBbegersopp do
begin
Glyph.LoadFromFile('beger.bmp');
OnClick:= BitBbegersoppClick;
{ use unscaled values }
Left:= HDistStartB + 0*HSpacingBitB;
Height:= HSizeBitB;
Width:= VSizeBitB;
Top:= VDistStartB + 0*VSpacingBitB;
{ this will scale the control }
Parent:=Form2;
{ Now uses scaled values }
Left:= Start.Left + Left;
Top:= Start.Top + Top;
Hint:= 'Begersopp, sporer på oversiden';
ShowHint:= True;
Tag:= 1;
end;
BTW, please avoid with!

DELPHI Table cell split & merge

How can I make something like this in Delphi:
I know I can make it from 3 tables so it would be easier, but how can I make Table cells split & merge and how to get the text to turn 90deg.?
Is there some good content libraries that have split & merge built in?
Check out woll2woll or infopower. They will do the grid for sure. The font can be achieved by overriding the OnDrawDataCell, OnDrawGroupHeaderCell and OnDrawTitleCell events and writing the text with rotated font.
{****************************************************************
* Create angled font. Procedure writen by Keith Wood *
****************************************************************}
procedure CreateAngledFont (AFont : TFont; const AAngle : Integer);
var
FntLogRec: TLogFont { Storage area for font information } ;
begin
{ Get the current font information. We only want to modify the angle }
fillchar (FntLogRec, sizeof(FntLogRec), #0);
GetObject (AFont.Handle, SizeOf(FntLogRec), Addr(FntLogRec));
{ Modify the angle. "The angle, in tenths of a degrees, between the base
line of a character and the x-axis." (Windows API Help file.) }
FntLogRec.lfEscapement := (AAngle * 10);
FntLogRec.lfOrientation := (AAngle * 10);
FntLogRec.lfOutPrecision := OUT_TT_PRECIS; { Request TrueType precision }
{ Delphi will handle the deallocation of the old font handle }
AFont.Handle := CreateFontIndirect (FntLogRec);
end;

How to programmatically change the resolution of a specific monitor?

How do you programmatically change the resolution of a specific monitor? For instance can the secondary monitor resolution be programmatically changed?
The following function might be your starting point. It tries to change the resolution of a display device with index specified by the Index parameter (if exists such) to a width and height (in pixels) given by the Width, Height parameters. The function returns True, if the display device with given index is found and the resolution of it has been successfully changed, False otherwise.
You haven't specified whether you want to change the resolution permanently (if you want to store the setting changes), or change it only temporarily. The following example does it temporarily, but you can quite simply change this behavior if you use in the second ChangeDisplaySettingsEx function call the CDS_UPDATEREGISTRY value for the dwflags parameter:
function ChangeMonitorResolution(Index, Width, Height: DWORD): Boolean;
var
DeviceMode: TDeviceMode;
DisplayDevice: TDisplayDevice;
begin
Result := False;
ZeroMemory(#DisplayDevice, SizeOf(DisplayDevice));
DisplayDevice.cb := SizeOf(TDisplayDevice);
// get the name of a device by the given index
if EnumDisplayDevices(nil, Index, DisplayDevice, 0) then
begin
ZeroMemory(#DeviceMode, SizeOf(DeviceMode));
DeviceMode.dmSize := SizeOf(TDeviceMode);
DeviceMode.dmPelsWidth := Width;
DeviceMode.dmPelsHeight := Height;
DeviceMode.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT;
// check if it's possible to set a given resolution; if so, then...
if (ChangeDisplaySettingsEx(PChar(#DisplayDevice.DeviceName[0]),
DeviceMode, 0, CDS_TEST, nil) = DISP_CHANGE_SUCCESSFUL)
then
// change the resolution temporarily (if you use CDS_UPDATEREGISTRY
// value for the penultimate parameter, the graphics mode will also
// be saved to the registry under the user's profile; for more info
// see the ChangeDisplaySettingsEx reference, dwflags parameter)
Result := ChangeDisplaySettingsEx(PChar(#DisplayDevice.DeviceName[0]),
DeviceMode, 0, 0, nil) = DISP_CHANGE_SUCCESSFUL;
end;
end;
An example how to change resolution of a secondary display device (device with index 1) to 800x600:
procedure TForm1.Button1Click(Sender: TObject);
begin
if ChangeMonitorResolution(1, 800, 600) then
ShowMessage('Resolution of display device with index 1 has been changed!')
else
ShowMessage('Display device with index 1 doesn''t exist, doesn''t support ' +
'resolution 800x600 or changing failed due to a reason, which you might ' +
'know if the author of this function wouldn''t be so lazy!');
end;

Display value on the bar chart

I want to know how to display value(in percent) on the bar series
Thanks..
Use ArrowLength properties of marks and give negative value. For example:
TheChart.Series[0].Marks.ArrowLength := -60;
TheChart.Series[1].Marks.ArrowLength := -60;
TheChart.Series[2].Marks.ArrowLength := -60;

Resources