Création d'une info-bulle Metropolis UI

De RAD Studio
Aller à : navigation, rechercher

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 :
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 :
__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  :
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 :
Tooltip over a component.png Tooltip mouse position.png
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 :
OnlyInputFields False.png OnlyInputFields True.png

Voir aussi