0

Create a VCL Forms Application, put a TBalloonHint (Name: balloonhintTest) and a TButton (Name: btnTest) on the form and write this code:

procedure TForm2.FormCreate(Sender: TObject);
begin
  balloonhintTest.HideHint;
  balloonhintTest.Style := bhsStandard;
end;

procedure TForm2.btnTestMouseEnter(Sender: TObject);
begin
  if not balloonhintTest.ShowingHint then
  begin
    balloonhintTest.Title := 'My Title';
    balloonhintTest.Description := 'MyDescription';
    balloonhintTest.ShowHint(Self.ClientToScreen(Point(btnTest.Left + (btnTest.Width div 2), btnTest.Top + btnTest.Height)));
  end;
end;

procedure TForm2.btnTestMouseLeave(Sender: TObject);
begin
  balloonhintTest.HideHint;
end;

Now run the program and hover your mouse pointer over the button.

This is how it looks when the window is on the UPPER PART OF THE SCREEN:

enter image description here

And this is how it looks when the window is on the LOWER PART OF THE SCREEN:

enter image description here

As you can see - although the Hint coordinates are always the same - the hint is displayed DOWNWARDS in the first case (desired result) and UPWARDS in the second case (obviously not the desired result), depending on the vertical position of the window on the screen.

So how can I display the balloon hint in this case always DOWNWARDS independently from the screen position?

(Please note: I am not interested in the other overloadings of the ShowHint method - I just want to know how to display the hint always downwards in the above case, as this is only the simplified scenario of a more complex case).

user1580348
  • 5,721
  • 4
  • 43
  • 105
  • Your caps-lock key is getting stuck randomly. – Sertac Akyuz Jan 13 '17 at 19:13
  • Sorry, it's an old keyboard. But do you know an answer? – user1580348 Jan 13 '17 at 19:28
  • 1
    I can't think of any scenario where you can call ShowHint but you can't call ShowHint. The rect override works quite fine IMO, it displays the hint either below the button or above the button, the button is always visible. – Sertac Akyuz Jan 13 '17 at 19:30
  • Thanks, but how can I get the size of the rectangle? – user1580348 Jan 13 '17 at 19:34
  • You pass the button's rectangle: `GetWindowRect(btnTest.Handle, R); balloonHintTest.ShowHint(R);` . – Sertac Akyuz Jan 13 '17 at 19:35
  • The hint is still showing upwards or downwards depending on the position on the screen (upper half or lower half). The question was: How can I display the balloon hint always downwards independently from the screen position? – user1580348 Jan 13 '17 at 19:42
  • @Remy - I did test that. What's shown that way is not quite a balloon hint any more, only the button's hint is displayed without a title. – Sertac Akyuz Jan 13 '17 at 19:43
  • This has nothing to do with showing the hint of a control. As I wrote, this is only a simplified example. The question is: How can I display the balloon hint always downwards independently from the screen position? – user1580348 Jan 13 '17 at 19:44
  • @user - Your hint will not be shown at all if the button is at the bottom of the screen. – Sertac Akyuz Jan 13 '17 at 19:44
  • Again: This has nothing to do with showing the hint of a control. – user1580348 Jan 13 '17 at 19:45
  • Anyway, simple answer is you pass your point accordingly. Test if it is in the upper or lower part of the screen first. I recently investigated positioning an automatically displayed custom hint: http://stackoverflow.com/questions/41473016/positioning-hints-for-components-in-delphi/41538038#41538038 . Options are not very wide. – Sertac Akyuz Jan 13 '17 at 19:48
  • @Remy - You're wrong. – Sertac Akyuz Jan 13 '17 at 19:48
  • I have also thought about testing whether it is in the upper or lower part of the screen first and then pass the corrected position accordingly. But for this I need to know the height of the hint window. How can I get that? – user1580348 Jan 13 '17 at 19:53
  • Nope. What matters is the position of the point you pass to ShowHint. If it's above or below `Screen.Height div 2`. Exact half passes as top of the screen. – Sertac Akyuz Jan 13 '17 at 19:56
  • As you can see, I am passing the fixed point at the horizontal center of the bottom of the button. This works as intended if I am in the upper part of the screen. But which point should I pass if I am in the lower part of the screen? – user1580348 Jan 13 '17 at 20:03
  • You're right, you need the height of the hint window. – Sertac Akyuz Jan 13 '17 at 20:12
  • But how can I get that? – user1580348 Jan 13 '17 at 20:13
  • I'll post an answer. – Sertac Akyuz Jan 13 '17 at 20:15
  • Didn't work out. Use the answer I linked. If the hint window's top is below the screen half, move it below with an amount of hint window's height. – Sertac Akyuz Jan 13 '17 at 20:52
  • @SertacAkyuz, as a workaround I show and then hide the hint in `FormCreate` which sets `FHintWindowHeight`. It works perfectly in my specific case because there is always a one-line description. – user1580348 Jan 13 '17 at 20:57
  • `TCustomHintWindow` has the the public property `FPopAbove`. Isn't it possible to modify this property in `TBalloonHint`? – user1580348 Jan 13 '17 at 21:20
  • TCustomHintWindow is only accessible through overriden message handlers of TCustomHint. The answer in the question I linked does so. For a balloon hint there's also two overridable methods: one as in the deleted answer (SetHintSize) and one PaintHint. PaintHint amounts to the linked answer. All of them are called after you called ShowHint. – Sertac Akyuz Jan 13 '17 at 21:48

2 Answers2

1

Probably easiest way is to create your own class based on TBalloonHint as

type
  TMyHint = class(TBalloonHint)
  strict private
    FControl: TControl;
  public
    procedure PaintHint(HintWindow: TCustomHintWindow); override;
    constructor Create(AOwner: TComponent; const AControl: TControl);
  end;

constructor TMyHint.Create(AOwner: TComponent; const AControl: TControl);
begin
  inherited Create(AOwner);
  FControl := AControl;
end;

procedure TMyHint.PaintHint(HintWindow: TCustomHintWindow);
var
  Point: TPoint;
begin
  Point := FControl.Parent.ClientToScreen(TPoint.Create(FControl.Left, FControl.Top + FControl.Height));
  HintWindow.Top := Point.Y;

  inherited;
end;

create it as

procedure TMainForm.FormCreate(Sender: TObject);
begin
  balloonHintTest := TMyHint.Create(Self, btnTest);
  balloonHintTest.Style := bhsStandard;
end;
EugeneK
  • 2,164
  • 1
  • 16
  • 21
0

I think it is even easier if you just apply a check on whether the showing point is on the lower part of the form to which the balloonhint belongs, and if yes add, sth like that. The height of the balloonHint can be calculated via textHeight

 procedure TForm2.FormCreate(Sender: TObject);
    begin
      balloonhintTest.HideHint;
      balloonhintTest.Style := bhsStandard;
    end;
    
    procedure TForm2.btnTestMouseEnter(Sender: TObject);
    begin
      if not balloonhintTest.ShowingHint then
      begin
        balloonhintTest.Title := 'My Title';
        balloonhintTest.Description := 'MyDescription';
        TPoint pointCheck = Self.ClientToScreen(Point(btnTest.Left + (btnTest.Width div 2), btnTest.Top + btnTest.Height)); 
        if(pointCheck.y>0.5*Self.Height)
            int yShift = balloonhintTest.Height; 
            pointCheck.y = pointCheck.y - yShift;  
        end if 
        balloonhintTest.ShowHint(pointCheck);
      end;
    end;
    
    procedure TForm2.btnTestMouseLeave(Sender: TObject);
    begin
      balloonhintTest.HideHint;
    end;

  
yg86
  • 1
  • 2