How set Panel position always above of hole region? - delphi

I have a code that creates a hole in Form using the mouse.
var
FormRegion, HoleRegion: HRGN;
begin
FormRegion := CreateRectRgn(0, 0, Form1.Width, Form1.Height);
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form1.Handle, FormRegion, True);
end;
Now i wish put one Panel (that already have a fixed height) always above of hole region (and with same width of hole) to simulate a title bar, something like this:
How can be made?

You did not read carefully my request for additional information, so I leave it to you to adjust however you like.
Anyway, I believe your actual question is concerning the alignment of the panel with the transparent region. You probably do not consider that the region calculations with the form window includes the borders, so you have an offset to the right and downwards.
Since the regions are calculated including the borders of the form, you will need a variable ClientOffset: TPoint to hold the values of width of the left border and the height of the top border (incl. title bar of the form).
var
ClientOffset: TPoint;
To calculate the ClientOffset you can use the predefined ClientOrigin and the forms Left and Top properties.
ClientOffset.X := Form36.ClientOrigin.X - Form36.Left; // Left border width
ClientOffset.Y := Form36.ClientOrigin.Y - Form36.Top; // Top border height (incl. title bar)
Then, either subtract ClientOffset from the panels Left and Top properties, or add ClientOffset to the HoleRegions coordinates. The latter being more correct if you use the mouse (and presumably the forms client coordinates) to define the "hole" region

Related

How to calculate the height of a TCategoryButtons at run-time?

In a Delphi 10.4.2 VCL Application in Windows 10, how can I calculate the height of a TCategoryButtons object at run-time, i.e. the sum of all its button heights and its Category items, as this height could vary depending on its font size?
Measuring the pixel heights at run-time, I have noticed that all buttons have the same height and that the buttons have a different height as the Category items.
Also, note that the buttons do not have a published Height property in the Object Inspector.
But shouldn't it be possible to calculate the sum of all its button heights and its Category items with some low-level methods?
This is a control entirely implemented in Pascal, in Vcl.CategoryButtons.pas.
Therefore, you can see exactly how it is implemented. For instance, in TCategoryButtons.Paint you see its complete drawing code. Similarly, you can investigate the hit testing done in MouseMove (or MouseDown or MouseUp).
Consequently, if nothing else, you can create your own modified version of TCategoryButtons using this code. Your version can save the total height when it has been determined (for instance, certainly after painting).
However, after a quick look, it seems like TButtonCategory.Bounds might be interesting. If you are lucky, this returns the on-screen rect of a category. The Bottom of the last category's rect should be the (effectively used) height of the entire control.
It seems to work for me:
Here I draw a red bar of the same height as the control.
procedure TForm5.FormPaint(Sender: TObject);
begin
var y := CategoryButtons1.Categories[
CategoryButtons1.Categories.Count - 1
].Bounds.Bottom;
Canvas.Brush.Color := clRed;
Canvas.FillRect(Rect(0, 0, ClientWidth, y))
end;

TScaledLayout anchor point

In a form I have a Layout aligned as alContent and inside I have a new button aligned as alRight. The problem is when I reduce the scale, the direction is always from right to left. Is there any way to when I decrease the scale of the Layout the button continue right-aligned in relation to the Form changing the direction of left to right?
I believe that is not a definitive solution but so far I haven't found another alternative to reposition the TScaledLayout proportionally when I change the scale.
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
with ScaledLayout1
do Position.X := OriginalWidth - (OriginalWidth / (1 / TrackBar1.Value));
end;

ManualFloat not using the specified Rect

if (in Delphi) I do
Panel1.ManualFloat(Rect(500,500,600,600));
the panel is floated not at the specified Rect location, but instead in a sort of windows default location. How do I get a panel (or other control) to float at a specified location. It does seem to have the correct shape however. Is there some other property I need to set to make it work correctly?
Edit: Just to make things clear. I would expect the above code to make the panel a 100x100 square located at (500x500) relative to the top left hand corner of the screen, which it doesn't. The shape is correct but location is not. If subsequent controls are floated they are cascaded down the screen.
Edit2: This doesn't seem to be a problem in Delphi 7, but is in Delphi 2007 through XE2 (and possibly earlier)
Don't look further: Its a bug in the VCL.
ManualFloat creates a floating window and sets its Top, Left values in TControl.CreateFloatingDockSite(Bounds: TRect) and later sets its ClientWidth.
That is a mistake because doing that forces the WindowHandle creation (it didn't have a Handle yet) in
function TCustomForm.GetClientRect: TRect;
begin
if IsIconic(Handle) then // <===
And that calls the default positioning of the Window (cascading yadda yadda...) resetting the Top and Left
The fix would be to set the ClientWidth and ClientHeight before setting the Top and Left properties in TControl.CreateFloatingDockSite(Bounds: TRect)
Update: the fixed code in Controls.pas
function TControl.CreateFloatingDockSite(Bounds: TRect): TWinControl;
begin
Result := nil;
if (FloatingDockSiteClass <> nil) and
(FloatingDockSiteClass <> TWinControlClass(ClassType)) then
begin
Result := FloatingDockSiteClass.Create(Application);
with Bounds do
begin
// Setting Client area can create the window handle and reset Top and Left
Result.ClientWidth := Right - Left;
Result.ClientHeight := Bottom - Top;
// It is now safe to position the window where asked
Result.Top := Top;
Result.Left := Left;
end;
end;
end;
Like the TRect parameter's name of the function - ScreenPos - kind of says it already, the coordinates are in screen units rather then that of the parent.
If you want the panel to stay at the same place where it was, translate the coordinates relative to the screen:
with Panel1.ClientToScreen(Point(0, 0)) do
Panel1.ManualFloat(Bounds(X, Y, 100, 100));
Or, to include the panel's border:
if Panel1.HasParent then
with Panel1.Parent.ClientToScreen(Panel1.BoundsRect.TopLeft) do
Panel1.ManualFloat(Bounds(X, Y, 100, 100));
Or, to translate to a specific coordinate relative to the parent, use:
if Panel1.HasParent then
with Panel1.Parent.ClientOrigin do
Panel1.ManualFloat(Bounds(X + 500, Y + 500, 100, 100));

How to find the real size ("logical area") of a TScrollBox

I need to find the entire size (also called "logical area") of a TScrollBox - as opposite to visible area that you get via Width and Height property (or ClientWidth ClientHeight).
I want to create some controls inside that TScrollBox. One of them (called TViewer) needs to be as high as the TScrollBox itself. The thing is that during creation, the TScrollBox is scrolled down to show last created control. So, using Top=1 will not work because my control will have top=1 which is not the top of the logical area.
Delphi 7
Drop a component, like a TLabel, on the TScrollBox.
Set the component's Left and Top properties to 0.
Set the component's Visible property to False.
Now you always have the origin. The "logical height" is now:
myScrollBox.Height + (myOriginControl.Top * -1);
Maybe ScrollBox.HorzScrollBar.Range and ScrollBox.VertScrollBar.Range + the corresponding .Positions are what you need.
Look at Scrollbars:
ScrollBox1.VertScrollBar.Range
ScrollBox1.HorzScrollBar.Range
It can be less than height and width if the scrollbox logical area is not bigger than phisical area (scrollbars not visible in that case)
Or use this to get the max of both:
var
AHeight, AWidth: Integer;
begin
AHeight := Max(ScrollBox1.VertScrollBar.Range, ScrollBox1.Height);
AWidth := Max(ScrollBox1.HorzScrollBar.Range, ScrollBox1.Width);
ShowMessageFmt('%d,%d', [AHeight, AWidth]);
end;
Edit
From #Altar comments, I can add the logical height and/or width is not the problem. If you want to add any control which occupies all the height of the scrollbar, use the AHeight from the above calculation, but set the Top to the negative of VertScrollBar.Position, something like this:
procedure TForm2.Button3Click(Sender: TObject);
var
AHeight, AWidth: Integer;
Btn: TButton;
begin
AHeight := Max(ScrollBox1.VertScrollBar.Range, Height);
AWidth := Max(ScrollBox1.HorzScrollBar.Range, Width);
Btn := TButton.Create(Self);
Btn.Parent := ScrollBox1;
Btn.Left := 15;
Btn.Top := -ScrollBox1.VertScrollBar.Position;
Btn.Height := AHeight;
end;
Im not sure i understand exactly what you want to do, but to get the complete area defined as "scrollable" you would have to write ScrollBox.HorScrollBar.Range + ScrollBox.Clientwidth (and the same thing for the vertical section). The scrollbox always deducts the visible "page" size from the total. So if you define a height of 1000 pixels, with 100 pixels showing - it will have a scrolling range of 900. You have to add the clientheight to get the rest.
Also, to get the correct "top" position you will have to read Canvas.Cliprect.Top, since a scrolling window does not change the top position of sub-controls. Windows handles this for you and only tells you what to re-draw once the scrollbars are initialized.
Since you want to create a control that is just as high as the complete scrollable area, i presume you are creating an editor of sorts?
If so you would probably get better results taking a look at SynEdit and extract the base-class that adds scrollbars to a normal TCustomControl (it's quite easy). That way you can control both the painting and the layout of your controls.
Here is one I wrote for Lazarus and FreePascal a while back. If you add messages to the uses clause and prefix the message handlers with WM rather than TLM it will compile under Delphi.
(code to long, had to use external link): http://delphimax.wordpress.com/2010/09/20/platform-independent-image-component-for-lazarus/
I have tried to do that, and believe me, I was not able to.
If you have the instances of the controls that are inside TScrollBox, you can use them to calculate (not precisely) the area.
Here is a complicated (but complete) solution:
I create my first child control during TScrollBox.Create (when TScrollBox does not have yet a scrollbar)
Set Child.Top:= 1
Create the rest of the child controls (this may force the TScrollBox to show the scrollbar)
Scroll the TScrollBox up
Use one of the above solutions to calculate the height of the TScrollBox

How do I StretchDraw two graphics beside each other on a custom TGraphicControl?

I'm writing my Delphi TGraphicControl paint procedure.
I create a canvas and I stretchdraw it onto the graphic area. It works well.
Then I repeat this with another Stretchdraw onto the graphic area but it is drawn in the area of the first Stretchdraw and not onto the graphic area as I direct it.
Is there a way that can place both stretchdraws beside each other in the TGraphicControl's canvas?
TCanvas.StretchDraw paints a graphic onto a canvas in a given rectangular area. The rectangle should, but does not need to be, within the bounds of the canvas. The owner of the canvas determines those bounds. In your case, it sounds like the canvas owner is the TGraphicControl object.
If you want two graphics to be painted beside each other, then the TRect you use to draw the first graphic should represent a rectangle that is adjacent to the TRect you use for the second graphic. You haven't shown any code yet, so it's hard to tell what's going wrong.
If you use the same TRect variable for both calls to StretchDraw, then you need to make sure you modify the rectangle's position between the calls — change the Left property, for starters.
For example:
var
r: TRect;
begin
r := ClientRect;
// First rectangle takes up left half of control
r.Right := r.Right div 2;
Canvas.StretchDraw(r, graphic1);
// Shift the rectangle to the right half
r.Left := r.Right;
r.Right := ClientRect.Right;
Canvas.StretchDraw(r, graphic2);
end;

Resources