I would like to know how to make my second trackbar.position mirror in the opposite direction of trackbar1.position.
eg.
Range from 1 to 100.
So When TrackBar1.Position := 2, then trackbar2.Position := 99
Regardless of which way the trackbars goes, I would like to mirror in the opposite direction.
Heres my code so far: (not interested in using keys to do this), just mouse interaction.
Direction : string;
Skip : boolean;
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
if TrackBar1.Position = TrackBar2.Position then
begin
if Direction = 'up' then TrackBar2.Position := TrackBar2.Position + 1;
if Direction = 'down' then TrackBar2.Position := TrackBar2.Position - 1;
skip := true;
end;
if TrackBar1.Position < TrackBar2.Position then
begin
if skip = false then
begin
TrackBar2.Position := TrackBar2.Position - 1;
Direction := 'down';
end;
end
else
begin
if skip = false then
begin
TrackBar2.Position := TrackBar2.Position + 1;
Direction := 'up';
end;
end;
end;
Im probably overdoing this. Maybe there is a simpler way. I prefer the simpler way.
Thanks,
Ben
The 2 trackbars OnChange events are linked to this code:
procedure TForm1.TrackBarChange(Sender: TObject);
var
tbSource, tbTarget: TTrackBar;
begin
if Sender = TrackBar1 then // Check the Trackbar which triggers the event
begin
tbSource := TrackBar1;
tbTarget := TrackBar2;
end
else
begin
tbSource := TrackBar2;
tbTarget := TrackBar1;
end;
tbTarget.OnChange := nil; // disable the event on the other trackbar
tbTarget.Position := tbSource.Max + tbSource.Min - tbSource.Position; // set the position on the other trackbar
tbTarget.OnChange := TrackBarChange; // define the event back to the other trackbar
// Call a function or whatever after this line if you need to do something when it changes
// lbl1.Caption := IntToStr(TrackBar1.Position);
// lbl2.Caption := IntToStr(TrackBar2.Position);
end;
Alternative start (suggested by Ken White and comments from me ;o)):
procedure TForm1.TrackBarChange(Sender: TObject);
var
tbSource, tbTarget: TTrackBar;
begin
// if Sender is TTrackBar then // is it called 'from' a trackbar?
// begin
tbSource := TTrackBar(Sender); // Set the source
if tbSource = TrackBar1 then // Check the Trackbar which triggers the event
tbTarget := TrackBar2
else
tbTarget := TrackBar1;
tbTarget.OnChange := nil; // disable the event on the other trackbar
tbTarget.Position := tbSource.Max + tbSource.Min - tbSource.Position; // set the position on the other trackbar
tbTarget.OnChange := TrackBarChange; // define the event back to the other trackbar
// Call a function or whatever after this line if you need to do something when it changes
// lbl1.Caption := IntToStr(TrackBar1.Position);
// lbl2.Caption := IntToStr(TrackBar2.Position);
// end;
end;
Related
How would I be able to do a continuous action while a button is held down? For example, I have made a custom 'Numpad' for my application, which has a Delete button. As of right now, I have to click it separately, but I want it to keep deleting while it is held down.
procedure TFrame1.deleteClick(Sender: TObject);
var
MiString: string;
begin
global_gotten_mode := precheck.global_edit_mode;
precheck.Form2.input_field.SetFocus;
MiString := Copy(precheck.Form2.input_field.Text, 0, (length(precheck.Form2.input_field.Text) - 1));
precheck.Form2.input_field.Text := MiString;
Form2.input_field.SelStart := high(integer);
end;
Add a timer and activate it on the OnMouseDown event.
As long as the button is held down, the timer will kick in at a rate of your choice.
When the button is released, the OnMouseUp event disables the timer.
Something in this way:
procedure TFrame1.BtnMouseDown(Sender : TObject);
begin
global_gotten_mode := precheck.global_edit_mode;
precheck.Form2.input_field.SetFocus;
fMyBtnTimer.Interval := 500; // Initial repetition rate
fMyBtnTimer.Enabled := true;
end;
procedure TFrame1.BtnMouseUp(Sender : TObject);
begin
fMyBtnTimer.Enabled := false;
end;
procedure TFrame1.MyBtnTimerEvent(Sender : TObject);
var
MiString: string;
begin
fMyBtnTimer.Interval := 200; // Increase repetition rate
MiString := Copy( precheck.Form2.input_field.Text,
0,
length(precheck.Form2.input_field.Text) - 1);
precheck.Form2.input_field.Text := MiString;
Form2.input_field.SelStart := high(integer);
end;
Problem
My TListBox is getting blank when its last TListBoxItem is programatically checked. To illustrate it better, hereby what I mean by getting blank:
Context
I'm generating a list from a TJSONArray. Each item looks like {"event_code","event_name"}.
Then, I compare if the event_code is written on a second TJSONArray : json_response_available_events. If it does, the ListBoxItem will be checked.
Code
procedure TFormHome.TimerGetEventsTimer(Sender: TObject);
var
K : Integer;
Z : Integer;
ListCount : Integer;
AvailableList_Count: Integer;
lb_item: TListBoxItem;
event_code_first_array: string;
event_code : string;
event_name : string;
begin
// Disable this timer for now
TimerGetEvents.Enabled := false;
// Get List of Notifications
json_response_events := DM_Auth0.ServerMethods1Client.GetEventsCodeAndDescription(communication_token);
json_response_available_events := DM_Auth0.ServerMethods1Client.GetAllowedNotificationsList(communication_token, genset_id);
ListCount := json_response_events.Count -1;
AvailableList_Count := json_response_available_events.Count - 1;
for K := 0 to (ListCount) do
begin
// Get complete Event Code and Name
event_name := json_response_events.Items[K].toString;
// Get Event Code
event_code_first_array := StringReplace(event_name.Split([':'])[0], '"', '', [rfReplaceAll]);
// Get Event Name
event_name := StringReplace(event_name.Split([':'])[1], '"', '', [rfReplaceAll]);
// Create ListBoxItem
lb_item := TListBoxItem.Create(self);
lb_item.Parent := lb_notifications;
lb_item.Text := event_name;
lb_item.StyleLookup := 'listboxitemleftdetail';
// Check if this Item code is available
for Z := 0 to (AvailableList_Count) do
begin
if json_response_available_events.Items[Z] <> nil then
begin
// Get Event Code
event_code := json_response_available_events.Items[Z].toString;
// Format
event_code := StringReplace(event_code, '"', '', [rfReplaceAll]);
if event_code_first_array.Contains(event_code) then
begin
if K <= ListCount then
begin
lb_item.IsChecked := true;
lb_item.IsSelected := false;
end;
end;
end;
end;
end;
end;
Analysis
If we set to < only, it displays the list correctly but the last item will remain unchecked.
if K < ListCount then
begin
lb_item.IsChecked := true;
lb_item.IsSelected := false;
end;
I can even change it's properties when its = like
if K = ListCount then
begin
lb_item.Text := 'Deadpool for President';
end;
and lb_item.isChecked := false works fine, but when setting lb_item.isChecked := true it gets all weirdly blank.
Why is it happening? And if there's a better way to do what I'm doing, the help will be appreciated.
In Delphi I'm having trouble preserving the SelStart and SelLength in a Memo that updates it text every 5 seconds when the selection is negative/reverse.
With negative/reverse selection I mean that I have started the selection somewhere and while holding shift pressed the left arrow key some times.
Code:
var
caret: TPoint;
sel_start, sel_length: Integer;
begin
sel_start := Memo1.SelStart; // = 5
sel_length := Memo1.SelLength; // = 10
caret := Memo1.CaretPos; // caret.x = 15
//'adi and bl' selected
caret.x := sel_start;
Memo1.Lines.Clear;
Memo1.Lines.Add('laditadi and blah blah');
Memo1.SelStart := sel_start;
Memo1.SelLength := sel_length;
Memo1.CaretPos := caret;
end;
The thing is that setting the SelLength seems to actually move the caret. And setting the caret after setting SelLength makes SelLength := 0;. Since the text keeps changing I can't use TMemo.SelText / TMemo.SetSelText before and after.
I can't find a way to preserve the caret pos...any clues?
If sel_start has the same value as characterposition of the Caret, selection will be reversed by setting selstart to selstart+sellength and setting sellength to -sellength.
procedure TForm1.Button1Click(Sender: TObject);
var
caret: TPoint;
sel_start, sel_length,CharFromPos: Integer;
begin
Memo1.SetFocus;
GetCaretPos(Caret);
CharFromPos := SendMessage(Memo1.Handle, EM_CHARFROMPOS, 0 ,Caret.Y*$FFFF + Caret.X) AND $FFFF;
sel_start := Memo1.SelStart; // = 5
sel_length := Memo1.SelLength; // = 10
Memo1.Lines.Clear;
Memo1.Lines.Add('laditadi and blah blah'#13#10'laditadi and blah blah');
if sel_start<>CharFromPos then
begin
Memo1.SelStart := sel_start;
Memo1.SelLength := sel_length;
end
else
begin
Memo1.SelStart := sel_start + sel_length;
Memo1.SelLength := - sel_length;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
p:Tpoint;
b:Boolean;
CharFromPos:Integer;
begin
b := GetCaretPos(p);
CharFromPos := SendMessage(Memo1.Handle, EM_CHARFROMPOS, 0 ,p.Y*$FFFF + p.X) AND $FFFF;
Caption := Format('SelStart %d CharFromPos %d',[Memo1.SelStart,CharFromPos])
end;
Is there a sliding (animated) panel component for Delphi?
For instance it can be found in Raize Components (a left panel with a "Hot Spot" or hide/show button).
I need not a resizeable panel but a panel that can slide horizontally and smoothly from the left to the right + that has a hide/show button (it's not a big deal if without that button).
Thanks!
Try NLDSideBar, a container component written by myself that is collapsable and aligned along the left or right side of its parent.
Interface:
property Align: TSideBarAlign default alLeft;
property AutoHide: Boolean default False;
property Hint: String;
property MinWidth: Integer default DefWidth;
property OnAutoHideChanged: TNotifyEvent;
property OnHide: TNotifyEvent;
property PinButtonDownHint: String;
property PinButtonUpHint: String;
property PinButtonVisible: Boolean default True;
property Resizable: Boolean default True;
property SideButtonWidth: Integer default DefSideButtonWidth;
property Caption;
property Color default clBtnFace;
property Font;
property ParentColor default False;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property TabOrder;
property TabStop;
Or maybe this older version which is animated. Free to use, free to modify.
Sorry for being self-promotive, but I think it's an answer to the question.
We ended up building our own control. We could not find anything that worked quite how we wanted. It ended up not being that hard. I'm sure there are situations we are not handling correctly, but so for this is working good for us.
The code below is using cxGroupBox because we needed that look to match the rest of our application. That can be switched out for a normal GroupBox.
We are using this in two places. In one case we have a number of these panels inside a standard Delphi Flow Panel (I'm not sure what version that was added). When our DynPanel collapses everything automatically moves up and fills the space.
In the other case we have a window that is split between a main section and a toolbox. The two are separated by a standard splitter. The main window is set to align to client. When our panel collapses or expands. the splitter automatically moves and expands the main section.
We never did quite get the "container" control stuff to work so items you add to the panel can be moved outside the bounds you would normally expect in a group box. But that does not cause us any major problems so we just left it. This also does not account for DPI changes in relation to the button size. The caption will get bigger but the button will not.
unit DynPanel;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, cxGroupBox;
const
DEFAULTBUTTONWIDTH = 16;
DEFAULTWIDTH = 161;
DEFAULTHEIGHT = 81;
cButtonPadding = 8;
cCollapsePadding = 3;
cCaptionPadding = ' ';
cCollapsedSize = DEFAULTBUTTONWIDTH + cCollapsePadding;
cAutoCollapseSize = DEFAULTBUTTONWIDTH + cButtonPadding;
type
TCollapseDirection = (cdUp, cdRight, cdLeft);
TMinDemension = cAutoCollapseSize..High(Integer);
TDynPanel = class(TPanel)
private
FGroupBox: TcxGroupBox;
FButtonPanel: TPanel;
FButtonImage: TImage;
FExpand: Boolean;
FOldHeight: Integer;
FOldWidth: Integer;
FCollapseDirection: TCollapseDirection;
FOrigGroupBoxCaption: String;
FAutoCollapseHeight: TMinDemension;
FAutoCollapseWidth: TMinDemension;
FButtonPadding: integer;
FCollapsePadding: integer;
FCollapsedSize: integer;
procedure SetExpand(Value: Boolean);
procedure SetGroupBoxCaption(Value: string);
procedure ButtonMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
procedure EnableControls(Value: Boolean);
procedure SetCollapseDirection(Value: TCollapseDirection);
procedure ConfigurePanel;
procedure SetMinHeight(Value: TMinDemension);
procedure SetMinWidth(Value: TMinDemension);
procedure UpdateImage();
protected
procedure Resize; override;
procedure ChangeScale(M, D: Integer); override;
public
constructor Create(AOwner: TComponent); override;
property OldHeight: Integer read FOldHeight write FOldHeight;
property OldWidth: Integer read FOldWidth write FOldWidth;
property GroupBox: TcxGroupBox read FGroupBox;
published
property Caption: string read FOrigGroupBoxCaption write SetGroupBoxCaption;
property Expand: Boolean read FExpand write SetExpand;
property BevelOuter default bvNone;
property CollapseDirection: TCollapseDirection read FCollapseDirection write SetCollapseDirection default cdUp;
property AutoCollapseHeight: TMinDemension read FAutoCollapseHeight write SetMinHeight default cAutoCollapseSize;
property AutoCollapseWidth: TMinDemension read FAutoCollapseWidth write SetMinWidth default cAutoCollapseSize;
end;
procedure Register;
implementation
{$R 'ButtonImages\ButtonImages.res' 'ButtonImages\ButtonImages.rc'}
uses cxEdit;
procedure Register;
begin
RegisterComponents('AgWare', [TDynPanel]);
end;
{ TDynPanel }
{
TDynPanel.Create
---------------------------------------------------------------------------
}
constructor TDynPanel.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Self.ControlStyle := ControlStyle - [csSetCaption];
Self.Width := DEFAULTWIDTH;
Self.Height := DEFAULTHEIGHT;
BevelOuter := bvNone;
FExpand := True;
FOldHeight := Self.Height;
FOldWidth := Self.Width;
FOrigGroupBoxCaption := 'AgDynPanel';
FCollapseDirection := cdUp;
FAutoCollapseHeight := cAutoCollapseSize;
FAutoCollapseWidth := cAutoCollapseSize;
FGroupBox := TcxGroupBox.Create(Self);
FGroupBox.Parent := Self;
FGroupBox.Align := alClient;
FGroupBox.Alignment := alTopLeft;
FButtonPanel := TPanel.Create(Self);
FButtonPanel.Parent := Self;
FButtonPanel.Top := 0;
FButtonPanel.Width := DEFAULTBUTTONWIDTH;
FButtonPanel.Height := DEFAULTBUTTONWIDTH;
FButtonPanel.Left := Width - DEFAULTBUTTONWIDTH - FButtonPadding;
FButtonPanel.OnMouseDown := ButtonMouseDown;
FButtonImage := TImage.Create(Self);
FButtonImage.Parent := FButtonPanel;
FButtonImage.Align := alClient;
FButtonImage.Stretch := false;
FButtonImage.Center := true;
FButtonImage.OnMouseDown := ButtonMouseDown;
UpdateImage;
// The click should also work for the entire top of the group box.
FGroupBox.OnMouseDown := ButtonMouseDown;
FGroupBox.Caption := FOrigGroupBoxCaption;
FGroupBox.Style.Font.Style := FGroupBox.Style.Font.Style + [fsBold];
FButtonPadding := cButtonPadding;
FCollapsePadding := cCollapsePadding;
FCollapsedSize := cCollapsedSize;
end;
{
TDynPanel.SetGroupBoxCaption
---------------------------------------------------------------------------
}
procedure TDynPanel.SetGroupBoxCaption(Value: String);
begin
FOrigGroupBoxCaption := Value;
ConfigurePanel;
end;
{
TDynPanel.SetMinHeight
---------------------------------------------------------------------------
}
procedure TDynPanel.SetMinHeight(Value: TMinDemension);
begin
if Value = FAutoCollapseHeight then
Exit; // >>----->
FAutoCollapseHeight := Value;
if Showing then
Resize;
end;
{
TDynPanel.SetMinWidth
---------------------------------------------------------------------------
}
procedure TDynPanel.SetMinWidth(Value: TMinDemension);
begin
if Value = FAutoCollapseWidth then
Exit; // >>----->
FAutoCollapseWidth := Value;
if Showing then
Resize;
end;
{
TDynPanel.ButtonMouseDown
---------------------------------------------------------------------------
}
procedure TDynPanel.ButtonMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button mbLeft then
Exit; // >>----->
if ((FExpand = True) and (Y FCollapsePadding)) or
((FExpand = False) and (FCollapseDirection = cdLeft) and (X >----->
FExpand := Value;
//ConfigurePanel;
//--------------------------------------------------------------------------
// Set the group box size
//--------------------------------------------------------------------------
//
// I chose to do the resizing of the control here rather than in
// ConfigurePanel because if you do it there the SetBounds will call ReSize
// which will call ConfigurePanel again so that you would need to keep track
// of a boolean variable to determine if you are making recursive calls into
// ConfigurePanel. That is one reason. Another is that when the dfm values
// are streamed in and the properties get set you will resize the control
// before the actual Height and Width properties are set. This will cause
// bogus default values to be stored for FOldHeight and FOldWidth and when
// the control is displayed the dimensions will be wrong. If you size the
// control here then, on creation, Resize will not get called and the
// FOldHeight and FOldWidth values will not get saved off until
// CMShowingChanged will explicitly call ReSize after the dimensions are
// properly set. If you move this code into ConfigurePanel then when the
// caption is streamed in and set from the dfm then ConfigurePanel would get
// called, we would SetBounds there and then Resize would fire storing off the
// default invalid values for the FOld variables as mentioned above.
// Hope this makes sense. Leave the SetBounds calls here and make your life
// easier. :)
//--------------------------------------------------------------------------
// Changing to Expanded
if FExpand = True then
begin
// Up
if FCollapseDirection = cdUp then
SetBounds(Left, Top, Width, FOldHeight)
// Right
else if FCollapseDirection = cdRight then
SetBounds((Left + Width) - FOldWidth, Top, FOldWidth, Height)
// Left
else if FCollapseDirection = cdLeft then
SetBounds(Left, Top, FOldWidth, Height);
end
// Changing to Collapsed
else
begin
// Up
if FCollapseDirection = cdUp then
begin
// Reset the AutoCollapseHeight just to make sure we don't try to
// recollapse on resize.
if FAutoCollapseHeight FGroupBox) and
(Self.Controls[i] FButtonPanel) then
begin
Self.Controls[i].Enabled := Value;
Self.Controls[i].Visible := Value;
end;
end;
end;
{
TDynPanel.CMShowingChanged
---------------------------------------------------------------------------
}
procedure TDynPanel.CMShowingChanged(var Message: TMessage);
begin
inherited;
if Showing then
Resize;
end;
{
TDynPanel.Resize
---------------------------------------------------------------------------
}
procedure TDynPanel.Resize;
begin
if FExpand = True then
begin
if (FCollapseDirection = cdUp) and (Height FAutoCollapseHeight then
begin
FOldHeight := Height;
Expand := True;
end
else
Height := FCollapsedSize;
end
else if (FCollapseDirection = cdLeft) or (FCollapseDirection = cdRight) then
begin
if (Width > FAutoCollapseWidth) then
begin
FOldWidth := Width;
Expand := True;
end
else
Width := FCollapsedSize;
end;
end;
ConfigurePanel;
end;
{
TDynPanel.ChangeScale
---------------------------------------------------------------------------
}
procedure TDynPanel.ChangeScale(M, D: Integer);
begin
FAutoCollapseHeight := MulDiv(FAutoCollapseHeight, M, D);
FAutoCollapseWidth := MulDiv(FAutoCollapseWidth, M, D);
FButtonPadding := MulDiv(FButtonPadding, M, D);
FCollapsePadding := MulDiv(FCollapsePadding, M, D);
FCollapsedSize := MulDiv(FCollapsedSize, M, D);
FOldHeight := MulDiv(FOldHeight, M, D);
FOldWidth := MulDiv(FOldWidth, M, D);
// inherited will cause resize to be called. I need to update
// my internal values before that happens, otherwise I will resize based
// on the old values.
inherited;
end;
{
TDynPanel.SetCollapseDirection
---------------------------------------------------------------------------
}
procedure TDynPanel.SetCollapseDirection(Value: TCollapseDirection);
begin
if Value = FCollapseDirection then
Exit; // >>----->
FCollapseDirection := Value;
ConfigurePanel;
end;
{
TDynPanel.ConfigurePanel
---------------------------------------------------------------------------
}
procedure TDynPanel.ConfigurePanel;
begin
//--------------------------------------------------------------------------
// Set the group box style, caption alignment, caption, button position, and
// button image
//--------------------------------------------------------------------------
// Changing to Expanded
if FExpand = True then
begin
FGroupBox.Style.Color := clWhite;
// Up
if FCollapseDirection = cdUp then
begin
FGroupBox.Alignment := alTopLeft;
FGroupBox.Caption := FOrigGroupBoxCaption;
FButtonPanel.Top := 0;
FButtonPanel.Left := Width - FButtonPanel.Width - FButtonPadding;
end
// Right
else if FCollapseDirection = cdRight then
begin
FGroupBox.Alignment := alTopLeft;
FGroupBox.Caption := ' ' + FOrigGroupBoxCaption;
FButtonPanel.Top := 0;
FButtonPanel.Left := FButtonPadding;
end
// Left
else if FCollapseDirection = cdLeft then
begin
FGroupBox.Alignment := alTopLeft;
FGroupBox.Caption := FOrigGroupBoxCaption;
FButtonPanel.Top := 0;
FButtonPanel.Left := Width - FButtonPanel.Width - FButtonPadding;
end;
end
// Changing to Collapsed
else
begin
FGroupBox.Style.Color := clGradientActiveCaption;
// Up
if FCollapseDirection = cdUp then
begin
FGroupBox.Alignment := alTopLeft;
FGroupBox.Caption := FOrigGroupBoxCaption;
FButtonPanel.Top := 0;
FButtonPanel.Left := Width - FButtonPanel.Width - FButtonPadding;
end
// Right
else if FCollapseDirection = cdRight then
begin
FGroupBox.Alignment := alRightTop;
FGroupBox.Caption := ' ' + FOrigGroupBoxCaption;
FButtonPanel.Top := FButtonPadding;
FButtonPanel.Left := FCollapsePadding;
end
// Left
else if FCollapseDirection = cdLeft then
begin
FGroupBox.Alignment := alLeftTop;
FGroupBox.Caption := FOrigGroupBoxCaption + ' ';
FButtonPanel.Top := FButtonPadding;
FButtonPanel.Left := 0;
end;
end;
UpdateImage;
// Now draw the button and invalidate Self
Self.Invalidate;
end;
{
TDynPanel.UpdateImage
---------------------------------------------------------------------------
}
procedure TDynPanel.UpdateImage();
begin
case FCollapseDirection of
cdUp:
begin
if FExpand = true then
FButtonImage.Picture.Bitmap.LoadFromResourceName(HInstance, 'ButtonImageUp')
else
FButtonImage.Picture.Bitmap.LoadFromResourceName(HInstance, 'ButtonImageDown');
end;
cdLeft:
begin
if FExpand = true then
FButtonImage.Picture.Bitmap.LoadFromResourceName(HInstance, 'ButtonImageLeft')
else
FButtonImage.Picture.Bitmap.LoadFromResourceName(HInstance, 'ButtonImageRight');
end;
cdRight:
begin
if FExpand = true then
FButtonImage.Picture.Bitmap.LoadFromResourceName(HInstance, 'ButtonImageRight')
else
FButtonImage.Picture.Bitmap.LoadFromResourceName(HInstance, 'ButtonImageLeft');
end;
end;
end;
end.
Close to the Left
Close to the Top
The new Delphi version will include such kind of sliding panels ( trough the integration of FireMonkey, formely vgScene/dxScene ). You'll just have to click on height or position props and an option will allow to create a animation for this, with various option ( interpolation kind, duration etc).
Since version 2009, there is a TCategoryPanelGroup where you add TCategoryPanel.
FoldingPanel
See FoldingPanel v1.3 here: http://www.torry.net/authorsmore.php?id=2386
I used it for years.
It comes with nice chevron bitmap too.
Small issue: it does not support composite controls (like TLabeledEdit).
Advantage: The component comes as a single PAS file (easy to install into the Palette). They say that it is for Delphi 5 but I have it installed in XE7 and works without problems (which indicates quality).
Freeware
TSplitView
If you have a newer version of Delphi (like Tokyo) you can use TSplitView.
Note: The Align property it missing. And at the beginning it might seems that it can only be aligned to Left. But it is not true. Instead of an Align property it has a Placement property (with two values: svpRight / svpLeft).
Note: It has some small glitches related to control size/placement.
Note: It is not (NOT EVEN BY FAR) as complete as FoldingPanel. You still have to write some cod eto implement some kind of chevron to collapse/expend the panel.
https://www.youtube.com/watch?v=3hUG8o7PpCU
Freeware (if you have Delphi Tokyo).
TCategoryPanelGroup
Also take a look at TCategoryPanelGroup. It might or might not work, depending on what you need it for.
Freeware (if you have Delphi XE7)
Sorry if there is already made such question earlier, but I have no time at the moment to dig in stackoverflow db ...
So, I have this code:
procedure TForm1.GraphPrevBtnMouseEnter(Sender: TObject);
var frm_PrevBtn : TForm;
begin
GraphPrevBtn.Width := 75;
if z = 0 then begin
frm_PrevBtn := TForm.Create(nil);
with frm_PrevBtn do begin
Name := 'frm_PrevBtn';
BorderStyle := bsNone;
Position := poDesigned;
Top := Form1.Top + GraphprevBtn.Top + (form1.Height - Form1.ClientHeight) - 3;
Left := Form1.Left + GraphprevBtn.Left + 3;
Width := GraphprevBtn.Width; Height := GraphprevBtn.Height; transparentColor := True; TransparentColorValue := clbtnFace;
Show;
end;
GraphPrevBtn.Parent := frm_PrevBtn;
if GetLastError = 0 then z := frm_prevBtn.GetHashCode;
end;
end;
procedure TForm1.GraphPrevBtnMouseLeave(Sender: TObject);
var frm_PrevBtn_H : THandle;
begin
// if form is created then- if mouse is under button then- if z = formshashcode ( form is on creatin stage )
if not (FindVCLWindow(Mouse.CursorPos) = GraphPrevBtn) and ((FindControl(FindWindow('TForm','frm_PrevBtn')) as TForm).Visible = True) and (GraphPrevBtn.Parent = FindControl(FindWindow('TForm','frm_PrevBtn')) as TForm) then begin // if mouse is not under graphprevBtn
ShowMessage(FindVCLWindow(Mouse.CursorPos).Name); //
if z = 112 // then if form is created
then begin
GraphPrevBtn.Parent := Form1;
GraphPrevBtn.bringtoFront;
GraphPrevBtn.Top := 29; GraphPrevBtn.Left := 226;
(FindControl(FindWindow('TForm','frm_PrevBtn')) as TForm).Free;
if GetLastError = 0 then z := 0;
end;
end;
end;
So, my wish is the following:
When I enter this GraphPrevBtn with mouse, form is created. As for is created, the focus goes from Control to new form. As focus is to new form, the OnMouseLeave event is fired. As event is fired, it should destroy the form, BUT ONLY IF user ( NOT active control / focus ) actually leaves control by mouse.
What happens now is that either new forms is not destroyed at all or both events goes infinite loop ( *frm_PrevBtn* is created and destroyed again and again and again...).
What would be best solution?
My idea is to get new forms rect and check whenever mouse is inside this rect. If it is, then perform allow OnMouseLeave event, otherwise deattach it ... would it work?
As much I tried with these samples:
http://delphi.about.com/od/windowsshellapi/a/get-active-ctrl.htm
http://delphi.about.com/od/delphitips2010/qt/is-some-delphi-tcontrol-under-the-mouse.htm
No luck. Where is the problem ... ?
Remarks: global var z : byte;
P.S. Thanks for negative votes ... great motivation to use this site in future ...
Mouse enters on 'GraphPrevBtn', you create a form over the button. As soon as this form becomes visible, since mouse is not anymore over 'GraphPrevBtn', 'OnMouseLeave' is fired. You destroy the new form and now mouse is again on the button so 'OnMouseEnter' is fired, hence the infinite loop.
As a solution, you can move the form disposing code to 'OnMouseEnter' of Form1:
procedure TForm1.FormMouseEnter(Sender: TObject);
begin
if z = 112
then begin
GraphPrevBtn.Parent := Form1;
[...]
.. and what's with the 'GetLastError', it seems fully irrelevant. If you're going to use it, at least set last error to '0' by calling GetLastError or SetLastErrorbefore beginning your operation.
Maybe something more like this will help you:
var
frm_PrevBtn : TForm = nil;
procedure TForm1.GraphPrevBtnMouseEnter(Sender: TObject);
var
P: TPoint;
begin
GraphPrevBtn.Width := 75;
if frm_PrevBtn = nil then begin
P := GraphPrevBtn.ClientOrigin;
frm_PrevBtn := TForm.Create(nil);
with frm_PrevBtn do begin
BorderStyle := bsNone;
Position := poDesigned;
SetBounds(P.X, P.Y, GraphPrevBtn.Width, GraphPrevBtn.Height);
TransparentColor := True;
TransparentColorValue := clBtnFace;
GraphPrevBtn.Parent := frm_PrevBtn;
GraphPrevBtn.Top := 0;
GraphPrevBtn.Left := 0;
Show;
end;
end;
end;
procedure TForm1.GraphPrevBtnMouseLeave(Sender: TObject);
begin
if (FindVCLWindow(Mouse.CursorPos) <> GraphPrevBtn) and (frm_PrevBtn <> nil) then begin
GraphPrevBtn.Parent := Self;
GraphPrevBtn.BringToFront;
GraphPrevBtn.Top := 29;
GraphPrevBtn.Left := 226;
FreeAndNil(frm_PrevBtn);
end;
end;
Why don't you do it like this:
MainForm.OnMouseOver: Create a secondary form.
SecondaryForm.OnMouseOver: Set FLAG_ON_SECONDARY.
SecondaryForm.OnMouseLeave: Clear FLAG_ON_SECONDARY.
MainForm.OnMouseLeave: if not FLAG_ON_SECONDARY then destroy the secondary form.
This might not work in case SecondaryForm.OnMouseOver fires after MainForm.OnMouseLeave. Well, think of something similar. Another solution is to start a timer which destroys SecondaryForm and disables itself if mouse is neither on Main nor on SecondaryForm.