Resizing buttons with keeping the same width when some buttons are invisible - delphi

I would like implement automatically resizing buttons keeping the same width when some buttons are invisible. I used code prepared by Andreas Rejbrand at this link, but the issue is more complicated when I set some buttons invisible. In places where we have invisible buttons there are gaps.
My idea is to check how many buttons are invisible and next set btnWidth depending on the amount of visible buttons. I don't actually know how I can check if buttons are invisible in this case.
I want to use TAdvGlowButton component for the buttons and TPanel component for the panel and add OnResize procedure to panel like below:
procedure TForm3.Panel4Resize(Sender: TObject);
var
i: Integer;
btnWidth: Integer;
begin
btnWidth := Panel4.Width div Panel4.ControlCount;
for i := 0 to Panel4.ControlCount - 1 do
begin
Panel4.Controls[i].Left := i * btnWidth;
Panel4.Controls[i].Width := btnWidth;
end;
end;
Could you give me any idea how to solve this issue?

procedure TForm3.Panel4Resize(Sender: TObject);
const
cLeftMargin = 10; //Margin at the left side of the group of buttons
cSpacing = 10; //Spacing/Margin between the buttons
cRightMargin = 10; //Margin at the right side of the group of buttons
var
i, VisibleControls, lLeft: Integer;
btnWidth: Integer;
begin
//count number of visible controls
VisibleControls := 0;
for i := 0 to Panel4.ControlCount - 1 do
if Panel4.Controls[i].Visible then
inc(VisibleControls);
btnWidth := (Panel4.Width-cLeftMargin-cRightMargin - cSpacing*(VisibleControls-1)) div VisibleControls;
//distribute the visible controls
lLeft := cLeftMargin;
for i := 0 to Panel4.ControlCount - 1 do
if Panel4.Controls[i].Visible then
begin
Panel4.Controls[i].Left := lLeft;
Panel4.Controls[i].Width := btnWidth;
lLeft := lLeft + btnWidth + cSpacing;
end;
end;

Related

Screen becomes black when repositioning Form to second monitor using Parallels VM

I am working with Delphi 10.4.2 in Windows 10 (virtualized in Parallels) on a dual monitor system. To recreate the problem on a multi-monitor system, create a new Windows VCL Application and place two buttons on the form: btnPrimaryMonitor and btnSecondaryMonitor. Then insert this code by creating click handlers for the two buttons:
procedure TForm1.btnPrimaryMonitorClick(Sender: TObject);
begin
RepositionFormToMonitor(0);
EnableDisableButtons;
end;
procedure TForm1.RepositionFormToMonitor(const aMonitor: Integer);
const
offset = 2;
begin
Self.Width := Screen.Monitors[aMonitor].Width - offset;
Self.Height := Screen.Monitors[aMonitor].Height - offset;
Self.Top := Screen.Monitors[aMonitor].Top;
Self.Left := Screen.Monitors[aMonitor].Left;
end;
procedure TForm1.btnSecondaryMonitorClick(Sender: TObject);
begin
RepositionFormToMonitor(1);
EnableDisableButtons;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
EnableDisableButtons;
Self.BorderStyle := bsNone;
Self.FormStyle := fsStayOnTop;
RepositionFormToMonitor(0);
end;
procedure TForm1.EnableDisableButtons;
begin
btnPrimaryMonitor.Enabled := (Self.Monitor.MonitorNum = 1);
btnSecondMonitor.Enabled := (Self.Monitor.MonitorNum = 0);
end;
This works perfectly, but as soon as I set offset = 1 or offset = 0 the screen becomes black!
The purpose of the code is to reposition the maximized stay-on-top Form from the primary monitor to the secondary monitor by clicking on the btnSecondMonitor button and then back to the primary monitor by clicking on the btnPrimaryMonitor button.
How can this problem be avoided?
A few issues:
You should not set WindowState to wsMaximized. In fact, you shouldn't touch this property at all.
Setting BoundsRect will set Left, Top, Width, and Height, so there is no need to set Left and Top separately.
To go back to the primary monitor, just set the form's BoundsRect.
Here's an example:
Create a new VCL project. Set the main form's BorderStyle to bsNone.
Then add the following code:
procedure TForm1.FormCreate(Sender: TObject);
begin
for var i := 0 to Screen.MonitorCount - 1 do
begin
var btn := TButton.Create(Self);
btn.Parent := Self;
btn.Caption := i.ToString;
btn.Tag := i;
btn.OnClick := MonitorButtonClick;
btn.Top := 8;
btn.Left := 8 + (btn.Width + 8) * i;
end;
end;
procedure TForm1.MonitorButtonClick(Sender: TObject);
begin
BoundsRect := Screen.Monitors[(Sender as TButton).Tag].BoundsRect;
end;
If this code doesn't work properly on your system, you probably have some problem with that Windows system. This should work flawlessly.

Removing Items from TFramedVertScrollBar Correctly?

I am trying to create a Previewlist where i add an image and a remove button on top of it in TFramedVertScrollbar with this code:
var
PreviewList: TFramedVertScrollBox;
i: integer;
...
procedure TDashboard.AddClick(Sender: TObject);
var
sImg: TImage;
sBtn: TButton;
sbit: TBitmap;
begin
sbit := TBitmap.Create;
try
with sbit do
begin
Width := Image1.Bitmap.Width;
Height := Image1.Bitmap.Height;
Assign(Image1.Bitmap);
end;
sImg := TImage.Create(PreviewList);
with sImg do
begin
Align := TAlignLayout.Top;
Position.X := i * Height;
Height := 60;
Margins.Bottom := 2;
Bitmap.Assign(sbit);
Parent := PreviewList;
WrapMode := TImageWrapMode.Stretch;
onClick := PreviewItemClick;
end;
sBtn := TButton.Create(sImg);
with sBtn do
begin
StyleLookup := 'listboxdeleteitem';
Position.X := sImg.Width - 25;
Position.Y := 5;
Width := 15;
Height := 15;
Text := 'X';
Parent := sImg;
onClick := PreviewItemClick;
end;
i := i + 1;
finally
sbit.Free;
Image1.Bitmap.Assign(nil);
end;
end;
The Creation of PreviewItem works but not removal as on removing the PreviewItem the SystemBar does not responds(ex can't move or close or click) or don't takes mouse events and i have to click on other components in the form to make it responsive again.
I tried to two version of PreviewListClick first is below:
begin
PreviewList.BeginUpdate;
Obj := TButton(Sender).Parent;
FreeAndNil(Obj);
PreviewList.EndUpdate;
end;
The Above Makes the SystemBar Not Responding so i did like this :
begin
PreviewList.BeginUpdate;
PreviewList.RemoveObject(TButton(Sender).Parent);
PreviewList.EndUpdate;
end;
SystemBar is responding in this case and item is removed but there is one problem, after clicking on the Remove button of PreviewItem the PreviewList items are not updated.
For Ex. if there are four item in list and if i remove second one then item is removed but the list is not updated as the position of second item is still kept. now the PreviewList Looks like this:
Item1->BlankSPace->Item2->Item3
how to correctly add and remove items ?
Removing Object from content worked fine
PreviewList.Content.RemoveObject(TButton(Sender).Parent);

How to set size of inactive (hidden) dock clients' tabs in JVCL Docking component?

The only dock style in JVCL that I know that has the auto hide function (to pin the dock clients) is JvDockVSNetStyle. I'm using it but I can't set the size of the inactive pinned panes' tabs. When hidden, the tabs don't show the title of the pane, only the name of the active pane is shown. Sorry, I can't post an example image because that's my first question.
In the object inpector there is an option called ChannelOption with the ActivePaneSize property. Is there a way to set the inactive pane size so it can show its name? Or maybe there is another dock style that I'm missing that has the same functions?
I'm using C++Builder and JVCL 3.45.
i did it by commenting out these code parts:
procedure TJvDockVSChannel.GetBlockRect(Block: TJvDockVSBlock; Index: Integer;
var ARect: TRect);
var
BlockWidth: Integer;
begin
// HERE
// if Block.VSPane[Index] <> Block.ActivePane then
// BlockWidth := Block.InactiveBlockWidth
// else
BlockWidth := Block.ActiveBlockWidth;
<snip>
procedure TJvDockVSChannel.Paint;
var
I: Integer;
<snip>
begin
VisiblePaneCount := 0;
for I := 0 to Block.VSPaneCount - 1 do
begin
if not Block.VSPane[I].FVisible then
Continue;
GetBlockRect(Block, I, DrawRect);
Canvas.Brush.Color := TabColor;
Canvas.FillRect(DrawRect);
Canvas.Brush.Color := clGray;
Canvas.FrameRect(DrawRect);
AdjustImagePos;
Block.FImageList.Draw(Canvas, DrawRect.Left, DrawRect.Top, I, dsTransparent, itImage);
// HERE
// if Block.ActivePane = Block.VSPane[I] then
begin
if Align in [alTop, alBottom] then
Inc(DrawRect.Left, Block.InactiveBlockWidth)
else
if Align in [alLeft, alRight] then
begin
Inc(DrawRect.Top, Block.InactiveBlockWidth);
if Align = alLeft then
DrawRect.Left := 15
else
DrawRect.Left := 20;
DrawRect.Right := DrawRect.Left + (DrawRect.Bottom - DrawRect.Top);
end;
Canvas.Brush.Color := TabColor;
Canvas.Pen.Color := clBlack;
Dec(DrawRect.Right, 3);
OldGraphicsMode := SetGraphicsMode(Canvas.Handle, GM_ADVANCED);
Canvas.Brush.Style := bsClear;
// HERE (changed options)
DrawText(Canvas.Handle, PChar(Block.VSPane[I].FDockForm.Caption), -1, DrawRect, {DT_END_ELLIPSIS or} DT_NOCLIP);
There is an event in TJvDockServer called DoFinishSetDockPanelSize.
Within the function you create for that event you can access the size of a form using Dockpanel. There may be a way from here to set the size of the tabs.

How to add components to TScrollBox dynamically one below another on button click?

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;

Resizing buttons so they are all the same width

I have a "wide" TPanel with several buttons on it (essentially a tool bar). All the buttons have Align=Left. I have created a function which will resize the buttons to the same size and calculate the width of them so they fill the entire TPanel. I call this function in the OnResize event handler of the TPanel.
procedure ScaleButtonsOnPanel;
var i: Integer;
begin
for i:=0 to mPanel.ControlCount-1 do begin
mPanel.Controls[i].Width := round(mPanel.width/mPanel.ControlCount-1)
end;
end;
The problem is if I minimize and then restore the form the layout of the buttons change from the design layout.
Can anyone offer a solution to having buttons on a panel which can be resized but maintain the design time order (in terms of left to right placement) ?
I do not really see your problem. But of course, you must set the position of the buttons, not only their size.
procedure TForm1.Panel1Resize(Sender: TObject);
var
i: Integer;
btnWidth: integer;
begin
btnWidth := Panel1.Width div Panel1.ControlCount;
for i := 0 to Panel1.ControlCount - 1 do
begin
Panel1.Controls[i].Left := i * btnWidth;
Panel1.Controls[i].Width := btnWidth;
end;
end;
This works very well.
See https://privat.rejbrand.se/panelresize.wmv.
OK, now I see. I think the alLeft is actually your problem. Controls with the same align tend to change their order. This is a well-known Delphi annoyance. Do it like I do above, instead. Just make sure that you go through the buttons in the right order. If you cannot rely on the ordering of Panel1.Controls, then you can do like this: Set the Tag property of each toolbar button to its position (0, 1, ...) in the toolbar then do
procedure TForm1.Panel1Resize(Sender: TObject);
var
i: Integer;
btnWidth: integer;
begin
btnWidth := Panel1.Width div Panel1.ControlCount;
for i := 0 to Panel1.ControlCount - 1 do
begin
Panel1.Controls[i].Left := Panel1.Controls[i].Tag * btnWidth;
Panel1.Controls[i].Width := btnWidth;
end;
end;
Have you tried to see if a TFlowPanel doesn't better suit your needs?

Resources