Création d'une info-bulle Metropolis UI
Remonter à Développement des applications Metropolis UI
Une info-bulle de style Metropolis UI est une fenêtre contextuelle qui affiche des informations d'aide quand la souris ou un périphérique tactile passe au-dessus ou touche un contrôle. Ce tutoriel explique comment créer et utiliser une info-bulle avec FireMonkey.
Création d'un nouveau projet FireMonkey :
- Créez une nouvelle application de bureau Metropolis UI :
- Pour Delphi : Fichier > Nouveau > Application de bureau FireMonkey Metropolis UI - Delphi > Application de bureau vide Metropolis UI.
- Pour C++ : Fichier > Nouveau > Application de bureau FireMonkey Metropolis UI - C++ > Application de bureau vide Metropolis UI.
- Enregistrez le projet.
Définir l'objet de classe ToolTip
Créez une nouvelle classe TToolTipPanel pour votre application.
Dans votre unité, effectuez les étapes suivantes :
- 1. Dans la section uses, ajoutez les unités suivantes :
- Delphi :
uses
FMX.Edit;
- C++ :
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant :
#include <FMX.Edit.hpp>
- 2. Définissez la classe TToolTipPanel dérivée de TPanel.
- Delphi :
TToolTipPanel = class(TPanel)
- C++ : définissez cette nouvelle classe dans le fichier d'en-tête (.h file) :
class TToolTipPanel : public TPanel {
//class definition goes here
}
- 3. Pour ajouter le texte à l'info-bulle, ajoutez un TLabel en tant que champ privé : FLabel. Pour accéder au texte de l'info-bulle, définissez une propriété Text publique avec les fonctions d'accesseur en lecture et en écriture de la propriété Text.
- Delphi :
type
TToolTipPanel = class(TPanel)
private
FLabel: TLabel;
function GetToolTipText: string;
procedure SetToolTipText(const Value: string);
public
property Text: string read GetToolTipText write SetToolTipText;
end;
- C++ :
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant :
class TToolTipPanel : public TPanel {
private:
TLabel *FLabel;
UnicodeString _fastcall GetToolTipText();
void _fastcall SetToolTipText (const UnicodeString Value);
public:
__published:
__property UnicodeString Text = {read = GetToolTipText, write = SetToolTipText};
};
- 4. Implémentez les fonctions d'accesseur en lecture et en écriture de la propriété Text comme suit :
function TToolTipPanel.GetToolTipText: string;
begin
Result := FLabel.Text;
end;
procedure TToolTipPanel.SetToolTipText(const Value: string);
begin
FLabel.Text := Value ;
end;
- C++ :
- Dans le fichier .cpp, ajoutez le code suivant :
UnicodeString _fastcall TToolTipPanel::GetToolTipText() {
return FLabel->Text;
}
// ---------------------------------------------------------------------------
void _fastcall TToolTipPanel::SetToolTipText(const UnicodeString Value) {
FLabel->Text = Value;
}
- 5. Ajoutez les membres privés suivants à la classe TToolTipPanel, en plus de ceux ajoutés ci-dessus :
- Delphi :
private
FMousePoint : TPointF; //keeps the current position for the mouse cursor
FActiveControl : TFmxObject; //keeps the current active control for which the tooltip is displayed
FTimer : TTimer; //the timer is used to decide when, in time, the tooltip is displayed and for how long,
// for the FActiveControl
FCounter : Cardinal;//keeps a counter used on timer execution
FOnlyInputFields : Boolean ; //this flag is used to decides if the tooltip is displayed only for
// input controls
procedure OnTimer(Sender: TObject); //define the OnTime event handler for the FTimer
- C++ :
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant à la classe TToolTipPanel :
TPointF FMousePoint;
TFmxObject *FActiveControl;
TTimer *FTimer;
unsigned FCounter;
bool FOnlyInputFields;
void _fastcall OnTimer (TObject *Sender);
- Pour exposer l'indicateur FOnlyInputFields :
- Delphi : ajoutez la fonction publique OnlyInputFields comme suit :
public
property OnlyInputFields : Boolean read FOnlyInputFields write FOnlyInputFields;
- C++ : ajoutez OnlyInputFields en tant que propriété publiée, comme suit :
__property bool OnlyInputFields = {read = FOnlyInputFields, write = FOnlyInputFields };
- 6. A des fins de conception, ajoutez un champ privé FBorderWidth et une propriété publique BorderWidth pour définir et obtenir la largeur des bordures de l'info-bulle. Les bordures sont prises en compte lors de l'affichage de l'info-bulle.
- Delphi :
private
FBorderWidth : Single;
public
property BorderWidth : Single read FBorderWidth write FBorderWidth;
- C++ :
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant à la classe TToolTipPanel :
private:
float FBorderWidth;
__published:
__property float BorderWidth = {read = FBorderWidth, write = FBorderWidth };
- 7. Définissez et implémentez le constructeur et le destructeur pour la classe TToolTipPanel comme suit :
-
- Déclaration
-
-
-
- Delphi :
-
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
-
-
- C++ :
-
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant à la classe TToolTipPanel :
-
- C++ :
-
public:
__fastcall TToolTipPanel(TComponent* Owner);
__fastcall virtual ~TToolTipPanel(void);
-
-
- Implémentation
-
-
-
- Delphi :
-
constructor TToolTipPanel.Create(AOwner: TComponent);
begin
inherited; //inherits the behavior from TPanel
Visible := False; //initially, the tootltip is not visible
StyleLookup := 'tooltippanel'; // sets the name of the ToolTip style
//initialize FLabel
FLabel := TLabel.Create(AOwner);
FLabel.Parent := Self;
FLabel.StyleLookup := 'tooltiplabel';
FLabel.Text := Self.ToString;
if assigned(FLabel.Canvas) then
Height := FLabel.Canvas.TextHeight(FLabel.Text);
FLabel.Align := TAlignLayout.Client;
FLabel.TextAlign := TTextAlign.Center;
FLabel.VertTextAlign := TTextAlign.Center;
//initialize FTimer
FTimer := TTimer.Create(AOwner);
FTimer.OnTimer := OnTimer;
FTimer.Enabled := True;
FTimer.Interval := 500;
FActiveControl := nil; //initially, there is no control for which to display the tootltip
FCounter := 1000; //FCounter is initially set to a high value
FBorderWidth := 10; //an initial value for the tooltip borders
end;
destructor TToolTipPanel.Destroy;
begin
inherited;
end;
-
-
- C++ :
- Dans le fichier .cpp, ajoutez le code suivant à la classe TToolTipPanel :
- C++ :
-
__fastcall TToolTipPanel::TToolTipPanel(TComponent* Owner) : TPanel(Owner){
Visible = False;
StyleLookup = "tooltippanel";
FLabel = new TLabel(this);
FLabel->Parent = this;
FLabel->StyleLookup = "tooltiplabel";
FLabel->Text = this->ToString();
if (FLabel->Canvas != NULL) {
Height = FLabel->Canvas->TextHeight(FLabel->Text);
}
FLabel->Align = TAlignLayout::Client;
FLabel->TextAlign = TTextAlign::Center;
FLabel->VertTextAlign = TTextAlign::Center;
FTimer = new TTimer(Owner);
FTimer->OnTimer = OnTimer;
FTimer->Enabled = true;
FTimer->Interval = 500;
FActiveControl = NULL;
FCounter = 1000;
FBorderWidth = 10;
}
//---------------------------------------------------------------------------
__fastcall TToolTipPanel::~TToolTipPanel() {
}
- 8. Ajoutez une méthode publique pour afficher l'info-bulle à une position spécifiée :
- Delphi :
public
procedure ShowToolTip(AX, AY: Single);
implementation
procedure TToolTipPanel.ShowToolTip(AX, AY: Single);
begin
Position.Point := PointF(AX,AY); //sets the tooltip position to the specified point
//calculates the size of the tooltip depending on the text to be displayed
Height := FLabel.Canvas.TextHeight(FLabel.Text) + 2 * FBorderWidth;
Width := FLabel.Canvas.TextWidth(FLabel.Text) + 2 * FBorderWidth;
//sets the tooltip as visible
Visible := True;
end;
- C++ :
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant à la classe TToolTipPanel :
public:
__fastcall void ShowToolTip(float AX, float AY);
-
- Dans le fichier .cpp, ajoutez le code suivant à la classe TToolTipPanel :
void __fastcall TToolTipPanel::ShowToolTip(float AX, float AY){
Position->Point = PointF(AX,AY);
Height = FLabel->Canvas->TextHeight(FLabel->Text)+ 2 * FBorderWidth;
Width = FLabel->Canvas->TextWidth(FLabel->Text) + 2 * FBorderWidth;
Visible = True;
}
- 9. Une info-bulle est typiquement affichée pour la position en cours du curseur. Une info-bulle affiche aussi un texte qui fournit des informations sur la position en cours.
- Dans ce cas, l'info-bulle affiche le texte suivant :
- "ToolTip for component: "
- et le nom du composant sur lequel le curseur de la souris est positionné.
- Si aucun composant n'est trouvé, l'affichage est :
- "ToolTip for mouse pos "
- et les coordonnées du curseur de la souris.
- Si l'info-bulle est définie pour être seulement affichée pour les contrôles d'entrée, elle affiche le texte :
- "ToolTip for "
- et le nom du contrôle d'entrée détenant la focalisation. En outre, l'info-bulle est positionnée sous le contrôle d'entrée, pour éviter de recouvrir le contrôle et permettre à l'utilisateur de définir le texte d'entrée.
- Bien entendu, le texte affiché peut être changé à votre convenance.
L'implémentation OnTimer doit ressembler à ceci :
- Delphi :
procedure TToolTipPanel.OnTimer;
var
LActiveControl : IControl;
LControl : TControl;
LMousePos : TPointF;
LObject : IControl ;
begin
//test to detect for which kind of controls to display the control
if not FOnlyInputFields then
begin
// tests if the FMousePoint is actualized
if Screen.MousePos <> FMousePoint then
begin
FMousePoint := Screen.MousePos ;
FCounter := 0;
Visible := False;
end ;
Inc(FCounter);
case FCounter of
0..2: Visible := False ;//simulates a delay on displaying the tooltip
3:
begin
Text := '';
if Parent is TForm then
begin
//identifies the object on which the mouse cursor is located
LObject := (Parent as TForm).ObjectAtPoint(FMousePoint) ;
if Assigned(LObject) then
Text := LObject.GetObject.Name;
end;
//if no object is found, the tooltip displays the mouse cursor coordinates
if Text = '' then
Text := 'ToolTip for mouse pos ' + PointToString(FMousePoint)
else
Text := 'ToolTip for component: ' + Text ;
LMousePos := (Parent as TForm).ScreenToClient(FMousePoint);
//displays the tooltip
ShowToolTip(LMousePos.X, LMousePos.Y);
end;
// the tooltip is displayed for a limited time. In this case it is displayed until FCounter reaches 10
4..10:;
else
FCounter := 1000;
Visible := False ;
end;
end
else
begin
//identifies the active control (the control in focus)
if Parent is TForm then
LActiveControl := (Parent as TForm).Focused;
// checks if the object in focus is an input control (and a TEdit or a TEdit descendant)
if Assigned(LActiveControl) and (LActiveControl.GetObject <> FActiveControl) then
begin
Visible := False ;
FActiveControl := LActiveControl.GetObject;
if (FActiveControl is TEdit) then
FCounter := 0;
end;
Inc(FCounter);
case FCounter of
0..2: Visible := False ;//simulates a delay on displaying the tooltip
3..10: // the tooltip is displayed for the FActiveControl control, if it exists, under the input control,
// so the tooltip doesn't cover the input area
begin
if assigned(LActiveControl) then
begin
LControl := (LActiveControl as TControl);
Text := 'ToolTip for ' + LControl.Name ;
ShowToolTip(LControl.Position.X + 20, LControl.Position.Y + LControl.Height);
end;
end
else
FCounter := 1000;
Visible := False ;
end;
end;
end;
- C++ :
- Dans le fichier d'en-tête (fichier .h), ajoutez le code suivant à la classe TToolTipPanel :
void __fastcall TToolTipPanel::OnTimer(TObject* Sender) {
IControl *LActiveControl;
TControl *LControl;
TPointF LMousePos;
IControl *LObject;
if (!FOnlyInputFields) {
if (Screen->MousePos() != FMousePoint) {
FMousePoint = Screen->MousePos();
FCounter = 0;
Visible = False;
}
FCounter++;
switch (FCounter) {
case 0:
case 1:
case 2:
Visible = False;
break;
case 3:
Text = "";
if ((dynamic_cast<TForm*>(Parent)) != NULL) {
TForm& ref_object = dynamic_cast<TForm&>(*Parent);
LObject = ref_object.ObjectAtPoint(FMousePoint);
if (LObject != NULL) {
Text = LObject->GetObjectW()->Name;
}
}
if (Text == "") {
Text = "ToolTip for mouse pos " + PointToString(FMousePoint);
}
else {
Text = "ToolTip for component: " + Text;
}
LMousePos = dynamic_cast<TForm*>(Parent)->ScreenToClient(FMousePoint);
ShowToolTip(LMousePos.X, LMousePos.Y);
break;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
break;
default:
FCounter = 1000;
Visible = False; ;
}
}
else {
if ((dynamic_cast<TForm*>(Parent)) != NULL) {
TForm& ref_LObject = dynamic_cast<TForm&>(*Parent);
LActiveControl = ref_LObject.Focused;
if ((LActiveControl != NULL) & (LActiveControl->GetObjectW()!= FActiveControl)) {
Visible = False;
FActiveControl = LActiveControl->GetObjectW();
if ((dynamic_cast<TEdit*>(FActiveControl)) != NULL) {
FCounter = 0;
}
}
FCounter++;
switch (FCounter) {
case 0:
case 1:
case 2:
Visible = False;
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
if (LActiveControl != NULL) {
LControl = System::interface_cast<TControl, IControl>(LActiveControl);
Text = "ToolTip for " + LControl->Name;
ShowToolTip(LControl->Position->X + 20, LControl->Position->Y + LControl->Height);
}
break;
default:
FCounter = 1000;
Visible = False; ;
}
}
}
}
- 10. L'interface finale du TToolTipPanel doit ressembler à ceci :
- Delphi :
type
TToolTipPanel = class(TPanel)
private
FOnlyInputFields: Boolean;
FMousePoint: TPointF;
FCounter: Cardinal;
FActiveControl: TFmxObject;
FLabel: TLabel;
FTimer: TTimer;
FBorderWidth: Single;
function GetToolTipText: string;
procedure SetToolTipText(const Value: string);
procedure OnTimer(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ShowToolTip(AX, AY: Single);
property Text: string read GetToolTipText write SetToolTipText;
property BorderWidth: Single read FBorderWidth write FBorderWidth;
property OnlyInputFields: Boolean read FOnlyInputFields
write FOnlyInputFields;
end;
- C++ :
class TToolTipPanel : public TPanel{
private:
TLabel *FLabel;
TPointF FMousePoint;
TFmxObject *FActiveControl;
TTimer *FTimer;
unsigned FCounter;
bool FOnlyInputFields;
float FBorderWidth;
UnicodeString _fastcall GetToolTipText();
void _fastcall SetToolTipText (const UnicodeString Value);
void _fastcall OnTimer (TObject *Sender);
public:
__fastcall TToolTipPanel(TComponent* Owner);
__fastcall virtual ~TToolTipPanel(void);
__fastcall void ShowToolTip(float AX, float AY);
__published:
__property UnicodeString Text = {read = GetToolTipText, write = SetToolTipText};
__property bool OnlyInputFields = {read = FOnlyInputFields, write = FOnlyInputFields };
__property float BorderWidth = {read = FBorderWidth, write = FBorderWidth };
};
Utilisation de l'info-bulle
- 1. Dans le Concepteur de fiches, ajoutez vos composants, par exemple : un TButton, un TCheckBox, un TEdit, un TMemo et un TRectangle.
- 2. Incluez dans la fiche la déclaration de l'unité ToolTip définie ci-dessus.
- 3. Ajoutez un membre privé, de type TToolTipPanel, à la fiche.
-
- Delphi :
private
TT : TToolTipPanel;
-
- C++ :
- Dans la déclaration private de TForm, dans le fichier d'en-tête (fichier .h) ajoutez le code suivant :
- C++ :
private: // User declarations
TToolTipPanel *TT;
- 4. Dans l'événement OnCreate de la fiche, créez un TToolTipPanel.
-
- Delphi :
procedure TForm1.FormCreate(Sender: TObject);
begin
TT := TToolTipPanel.Create(Form1);
TT.Parent := Self ;
end;
-
- C++ :
__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner){
TT = new TToolTipPanel(this);
TT->Parent = this;
}
- 5. Pour tester le comportement de l'info-bulle quand la propriété OnlyInputFields est définie sur True ou False, ajoutez le code suivant dans l'événement OnChange du TCheckBox :
-
- Delphi :
procedure TForm1.CheckBox1Change(Sender: TObject);
begin
TT.OnlyInputFields := CheckBox1.IsChecked;
end;
-
- C++ :
void __fastcall TForm1::CheckBox1Change(TObject *Sender){
TT->OnlyInputFields = CheckBox1->IsChecked;
}
- 6. Exécutez l'application.
- Voici quelques images exemple de l'info-bulle créée :
-
- L'image suivante montre l'inférence d'une info-bulle d'un composant d'entrée pour la propriété OnlyInputFields définie sur True :