Board index » cppbuilder » Highlighting every component and changing its size like BCB6

Highlighting every component and changing its size like BCB6


2004-08-27 01:56:10 AM
cppbuilder39
Hello:
My application mimics BCB6 at a very very small scale. It
allows user to create new buttons, labels, panels etc on a Form
called MainForm using the menu on MenuForm.
Here's what I want to achieve:
When you click on a component in BCB, it surrounds
the component's perimeter or Border with 8 tiny squre black
boxes -- 4 at 4 corners and 4 right in the middle of each side.
this does 2 things: 1) allows user to know which component is
"ACTIVE" or in "focus". 2) Allows user to click on those
boxes and change the size of the component depending on which
box clicks and drags. Also, the cursor changes.
I want to allow same thing for my controls that user has added
on MainForm. Any hints or suggestions?
As always, your help is much appreciated.
cheers,
Veebo
 
 

Re:Highlighting every component and changing its size like BCB6

"Veebo" < XXXX@XXXXX.COM >wrote:
Quote
[...] Here's what I want to achieve:
All of it is a piece of cake ... except knowing when the focus
has changed. At the moment, the only way to do that that I
know of is to subclass *every* control's (not every type of
control but every single control) WndProc method and catch the
WM_KILLFOCUS message.
Keep your eye on my 'ActiveControl changing' thread. Resolve
that issue and you have most of the rest of the code already
in that subclassed TPanel used as a button.
~ JD
 

Re:Highlighting every component and changing its size like BCB6

thanks for your response, Like always, JD.
I don't really understand the following part... what exactly
are you suggesting me to do? i've never really dealt with with
windows messages before, maybe that's why i'm having troubles
understanding.
Quote
All of it is a piece of cake ... except knowing when the focus
has changed. At the moment, the only way to do that that I
know of is to subclass *every* control's (not every type of
control but every single control) WndProc method and catch the
WM_KILLFOCUS message.

I have implemented my own small system that tracks the active
control. My MOST IMPORTANT concern is how can I create those
eight black boxes/squares
that you see around a control in BCB when
you click on it? Given I know the Active Control, what
can I do to create those black boxes, which indicate the user
visually that the control is active and also allows him to
CHANGE THE SIZE of the control by clicking&dragging on those boxes/black squares.
can you understand what I am talking about from my description
(which is not the best)?
basically I want the same effect as BCB6 when u click on a
control.
Please respond.
thanks,
Veebo
 

{smallsort}

Re:Highlighting every component and changing its size like BCB6

"Veebo" < XXXX@XXXXX.COM >wrote:
Quote

thanks for your response, Like always, JD.

I don't really understand the following part... what exactly
are you suggesting me to do? i've never really dealt with with
windows messages before, maybe that's why i'm having troubles
understanding.

>All of it is a piece of cake ... except knowing when the focus
>has changed. At the moment, the only way to do that that I
>know of is to subclass *every* control's (not every type of
>control but every single control) WndProc method and catch the
>WM_KILLFOCUS message.
>
[...] Given I know the Active Control, [...]
Knowing the active control isn't the problem - It's knowing
when the control has changed because you have to remove the
sizer from the old active control and add it to the new active control.
I have that part sorta working and I'll pass that onto to you to get straight (and I'll do the resizing).
The problems with this code is that it selects any control to
size. Maybe you want that - maybe not. It also changes
controls when the form looses and regains focus like when
minimized and restored. It seems to follow the tab order, for
a while at least and sometimes not. All you want to do is get
the active control tracking to your satisfaction and post that
code.
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MainMod.h"
#include "Sizer.h"
#include "Unit2.h"
//-------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TSizer *Sizer;
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
Sizer = NULL;
Sizer = new TSizer( this );
Screen->OnActiveControlChange = ActiveControlChanged;
Screen->OnActiveFormChange = ActiveFormChanged;
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
Screen->OnActiveControlChange = NULL;
Screen->OnActiveFormChange = NULL;
delete Sizer;
}
//-------------------------------------------------------------
void __fastcall TForm1::ActiveFormChanged(TObject *Sender)
{
Sizer->ParentFormHandle = Screen->ActiveForm->Handle;
}
//-------------------------------------------------------------
void __fastcall TForm1::ActiveControlChanged(TObject *Sender)
{
Sizer->ChangeFocus( Screen->ActiveForm->ActiveControl );
}
//-------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// just drop some different controls on a second (and maybe 3rd) form
Form2->ShowModal();
Form2->Visible = false;
}
//-------------------------------------------------------------
//-------------------------------------------------------------
#ifndef SizerH
#define SizerH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <ExtCtrls.hpp>
//-------------------------------------------------------------
class TSizer : public TPanel
{
protected:
private:
typedef TPanel inherited;
HWND FParentFormHandle;
TWinControl *FActiveControl;
TWinControl *FPriorActiveControl;
TWinControl *SaveParent;
int SaveLeft;
int SaveTop;
void __fastcall SetTheColor( TWinControl *Control );
void __fastcall SetParentFormHandle( HWND Handle );
public:
__property HWND ParentFormHandle = { read = FParentFormHandle, write = SetParentFormHandle };
void __fastcall ChangeFocus( TWinControl *NewControl );
__fastcall TSizer( TComponent* Owner );
__fastcall TSizer::~TSizer();
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#pragma hdrstop
#include "Sizer.h"
//-------------------------------------------------------------
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TSizer::TSizer(TComponent* Owner) : TPanel( Owner )
{
TForm *pForm = dynamic_cast<TForm*>( Owner );
FParentFormHandle = pForm->Handle;
FActiveControl = NULL;
FPriorActiveControl = NULL;
BevelWidth = 2;
}
//-------------------------------------------------------------
__fastcall TSizer::~TSizer()
{
//
}
//-------------------------------------------------------------
void __fastcall TSizer::SetTheColor( TWinControl *Control )
{
// Control is the Parent control of the control about to be focused
// arrange the if blocks so that the most common parent type is checked first
TForm *pForm = dynamic_cast<TForm*>( Control );
if( pForm ) Color = pForm->Color;
else
{
TPanel *pPanel = dynamic_cast<TPanel*>( Control );
if( pPanel ) Color = pPanel->Color;
else
{
// continue testing
}
}
}
//-------------------------------------------------------------
void __fastcall TSizer::ChangeFocus( TWinControl *Control )
{
LockWindowUpdate( FParentFormHandle );
if( Control && Control != FActiveControl )
{
if( FActiveControl )
{
FActiveControl->Top = SaveTop;
FActiveControl->Left = SaveLeft;
FActiveControl->Parent = SaveParent;
}
FActiveControl = Control;
SaveTop = Control->Top;
SaveLeft = Control->Left;
SaveParent = Control->Parent;
Left = Control->Left - 3;
Top = Control->Top - 3;
Height = Control->Height + 6;
Width = Control->Width + 6;
Parent = Control->Parent;
SetTheColor( Control->Parent );
Control->Left = 3;
Control->Top = 3;
Control->Parent = this;
}
LockWindowUpdate( NULL );
}
//-------------------------------------------------------------
void __fastcall TSizer::SetParentFormHandle( HWND Handle )
{
if( FActiveControl )
{
LockWindowUpdate( FParentFormHandle );
FActiveControl->Top = SaveTop;
FActiveControl->Left = SaveLeft;
FActiveControl->Parent = SaveParent;
LockWindowUpdate( NULL );
}
FActiveControl = NULL;
FParentFormHandle = Handle;
}
//-------------------------------------------------------------
~ JD
 

Re:Highlighting every component and changing its size like BCB6

"JD" < XXXX@XXXXX.COM >wrote:
Quote

Work on this instead. I worked on it off and on today but
won't have any more time until maybe after I get home tonight
so here's what I have so far.
There's a problem with the subclassed panel getting refreshed
100% of the time. If the form is minimized or hidden by
another window, it doesn't paint correctly. I'm thinking that
you should look for a different way to make the panel
transparent. If you can find a free one (with source is better)
that will solve that.
The only thing that's left to code is in the TSizer::MouseMove.
You need to resize the control (FActiveControl) and the panel
at the same time.
Good Luck.
~ JD
//-------------------------------------------------------------
#ifndef MainModH
#define MainModH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//-------------------------------------------------------------
#include "Sizer.h"
//-------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TEdit *Edit2;
TEdit *Edit3;
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
TSizer *Sizer;
void __fastcall ActiveFormChanged(TObject *Sender);
void __fastcall ActiveControlChanged(TObject *Sender);
void __fastcall AppOnActivate(TObject *Sender);
public: // User declarations
__fastcall TForm1(TComponent* Owner);
__fastcall TForm1::~TForm1();
};
//-------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MainMod.h"
#include "Unit2.h"
//-------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
Sizer = NULL;
Sizer = new TSizer( this );
Screen->OnActiveControlChange = ActiveControlChanged;
Screen->OnActiveFormChange = ActiveFormChanged;
Application->OnActivate = AppOnActivate;
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
Screen->OnActiveControlChange = NULL;
Screen->OnActiveFormChange = NULL;
delete Sizer;
}
//-------------------------------------------------------------
void __fastcall TForm1::AppOnActivate(TObject *Sender)
{
Screen->ActiveForm->Refresh();
}
//-------------------------------------------------------------
void __fastcall TForm1::ActiveFormChanged(TObject *Sender)
{
Sizer->ParentFormHandle = Screen->ActiveForm->Handle;
Screen->ActiveForm->Refresh();
}
//-------------------------------------------------------------
void __fastcall TForm1::ActiveControlChanged(TObject *Sender)
{
Sizer->ChangeFocus( Screen->ActiveForm->ActiveControl );
}
//-------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form2->ShowModal();
Form2->Visible = false;
}
//-------------------------------------------------------------
//-------------------------------------------------------------
#ifndef SizerH
#define SizerH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <ExtCtrls.hpp>
//-------------------------------------------------------------
class TSizer : public TPanel
{
protected:
DYNAMIC void __fastcall MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y);
void __fastcall Paint();
private:
typedef TPanel inherited;
HWND FParentFormHandle;
TWinControl *FActiveControl;
void __fastcall WndProc( TMessage &Message );
void __fastcall SetParentFormHandle( HWND Handle );
int __fastcall MouseOverSizer( TPoint P );
public:
__property HWND ParentFormHandle = { read = FParentFormHandle, write = SetParentFormHandle };
void __fastcall ChangeFocus( TWinControl *NewControl );
__fastcall TSizer( TComponent* Owner );
__fastcall TSizer::~TSizer();
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#pragma hdrstop
#include "Sizer.h"
//-------------------------------------------------------------
#pragma package(smart_init)
Graphics::TBitmap* pBitmap = NULL;
TPoint Positions[ 8 ];
TPoint ClickPoint;
//-------------------------------------------------------------
__fastcall TSizer::TSizer(TComponent* Owner) : TPanel( Owner )
{
TForm *pForm = dynamic_cast<TForm*>( Owner );
FParentFormHandle = pForm->Handle;
FActiveControl = NULL;
BevelInner = bvNone;
BevelOuter = bvNone;
pBitmap = new Graphics::TBitmap();
pBitmap->Height = 5;
pBitmap->Width = 5;
pBitmap->Canvas->Brush->Color = clBlack;
pBitmap->Canvas->FillRect( TRect(0, 0, 5, 5) );
for( int x = 0; x < 8; ++x ) Positions[ x ] = Point( 0, 0 );
}
//-------------------------------------------------------------
__fastcall TSizer::~TSizer()
{
delete pBitmap;
}
//-------------------------------------------------------------
void __fastcall TSizer::WndProc(TMessage &Message)
{
if( Message.Msg == WM_ERASEBKGND ) Message.Result = 0;
else
{
if( Message.Msg == CM_MOUSELEAVE ) Screen->Cursor = crDefault;
inherited::WndProc( Message );
}
}
//-------------------------------------------------------------
int __fastcall TSizer::MouseOverSizer( TPoint P )
{
for( int x = 0; x < 8; ++x )
{
TRect R = Rect( Positions[x].x, Positions[x].y, Positions[x].x + 5, Positions[x].y + 5 );
if( ::PtInRect(&R,P) ) return x;
}
return -1;
}
//-------------------------------------------------------------
void __fastcall TSizer::MouseDown(TMouseButton Button, TShiftState Shift, int X, int Y)
{
if( MouseOverSizer(Point(X,Y))>-1 ) ClickPoint = Point(X,Y);
inherited::MouseDown(Button, Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TSizer::MouseMove(TShiftState Shift, int X, int Y)
{
if( Shift.Contains(ssLeft) )
{
if( Screen->Cursor != crDefault )
{
switch( MouseOverSizer(Point(X,Y)) )
{
// 0------3-------5
// 1 6
// 2------4-------7
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
}
ClickPoint = Point(X,Y);
}
}
else
{
int PosIndex = MouseOverSizer( Point(X,Y) );
if( PosIndex>-1 )
{
if( Screen->Cursor == crDefault )
{
switch( PosIndex )
{
case 0:
case 7: Screen->Cursor = crSizeNWSE; break;
case 1:
case 6: Screen->Cursor = crSizeWE; break;
case 2:
case 5: Screen->Cursor = crSizeNESW; break;
case 3:
case 4: Screen->Cursor = crSizeNS; break;
}
}
}
else if( Screen->Cursor != crDefault ) Screen->Cursor = crDefault;
}
inherited::MouseMove(Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TSizer::MouseUp(TMouseButton Button, TShiftState Shift, int X, int Y)
{
inherited::MouseUp(Button, Shift, X, Y);
}
//-------------------------------------------------------------
void __fastcall TSizer::ChangeFocus( TWinControl *Control )
{
LockWindowUpdate( FParentFormHandle );
if( Control && Control != FActiveControl )
{
FActiveControl = Control;
Left = Control->Left - 2;
Top = Control->Top - 2;
Height = Control->Height + 4;
Width = Control->Width + 4;
Parent = Control->Parent;
//-------------------------
//
// 0------3-------5
// 1 6
// 2------4-------7
//
//-------------------------
int hWidth = ((Width-1)/2) - 2;
int hHeight = ((Height-1)/2) - 2;
int fRight = Width - 6;
int fBottom = Height - 6;
Positions[ 1 ] = Point( 0, hHeight );
Positions[ 2 ] = Point( 0, fBottom );
Positions[ 3 ] = Point( hWidth, 0 );
Positions[ 4 ] = Point( hWidth, fBottom );
Positions[ 5 ] = Point( fRight, 0 );
Positions[ 6 ] = Point( fRight, hHeight );
Positions[ 7 ] = Point( fRight, fBottom );
}
LockWindowUpdate( NULL );
}
//-------------------------------------------------------------
void __fastcall TSizer::SetParentFormHandle( HWND Handle )
{
Parent = NULL;
FActiveControl = NULL;
FParentFormHandle = Handle;
}
//-------------------------------------------------------------
void __fastcall TSizer::Paint()
{
int SaveIndex = SaveDC( Canvas->Handle );
MoveWindowOrg( Canvas->Handle, -Left, -Top );
Parent->Perform( WM_PAINT, (WPARAM)Canvas->Handle, (LPARAM)0 );
RestoreDC( Canvas->Handle, SaveIndex );
for( int x = 0; x < 8; ++x )
{
Canvas->Draw( Positions[x].x, Positions[x].y, pBitmap );
}
}
//-------------------------------------------------------------
~ JD
 

Re:Highlighting every component and changing its size like BCB6

Quote
Work on this instead.
JD, that's awesome. Really appreciate your help. I tried the
code from your previous code also. I had a similar solution
to that one, but I really wanted those black square things --
it seems to be industry standard. I saw that in BCB, MSVisio
MS Word etc.
In previous solution, I was tracking how the Active Control
changes focus by having different OnMouseDown events. So,
only when a control was clicked, I made it the ActiveControl.
At that time I was not allowing Tab to change focus. Now I am
planning to allow Tab to change focus from Control to Control.
That's why those black square things will be very helpful
since you can see exactly which control is active.
So, this solution rocks.
My application is a bit simple and not super intelligent
or powerful, so, I'm gonna implement sort of a smaller version
of solution you gave. Just like what I did with the Button
derived from TPanel. I took your ideas and wrote a bit
smaller simpler version of it. So, major props to you for
coming up with great ideas everytime. You've been very very
helpful.
Quote

There's a problem with the subclassed panel getting refreshed
100% of the time.
I have experienced problems in past with refreshing (for ex
after changing a Brush color etc). Why isn't there a simple
function in BCB that takes care of refreshing -- I wonder.
Let's see what happens with that. I did try minimizing the
form etc and the panel disappears and only the frame with
black square stays.
Thanks a bunch again. Just curious? How do you find time
to contribute to the news group so much?
cheers,
Veebo
 

Re:Highlighting every component and changing its size like BCB6

JD,
can you please explain the Paint() function?
1)where is it called?
2) Which component's property is Canvas??
3)what do SaveDC and RestoreDC functions do? where are they
defined?
thanks,
Veebo
 

Re:Highlighting every component and changing its size like BCB6

"Veebo" < XXXX@XXXXX.COM >wrote:
Quote

can you please explain the Paint() function?
It's an override of the default method.
Quote
1)where is it called?
Internally from within TPanel. The neat thing about subclassing
an object is that you only need to code the changes that you
want. The rest (unchanged code) is transparent and need not be
included.
Quote
2) Which component's property is Canvas??
When ever you see (in a subclassed object) a referrence to an
object that hasn't been defined, it *must* be a property of that
class. IOW, it's a property of the subclassed TPanel (TSizer).
Quote
3)what do SaveDC and RestoreDC functions do?
The names are some what telling. SaveDC saves the current state
of the specified device context (DC) by copying data describing
selected objects and graphic modes (such as the bitmap, brush,
palette, font, pen, region, drawing mode, and mapping mode) to
a context stack.
Quote
where are they defined?
In the final version, I would have the code read:
::SaveDC( Canvas->Handle );
which should tell you that it's a Win32 API call.
~ JD
 

Re:Highlighting every component and changing its size like BCB6

"Veebo" < XXXX@XXXXX.COM >wrote:
Quote

[...] How do you find time to contribute to the news group
so much?
Strange that you should ask that. I've often wondered the same
about Gambit.
My job is my hobby and some day's I'm very lucky and some nights
(like tonight) I'm very unlucky. ;-)
~ JD