* Made sure the tool tip stays on screen without moving itself below the cursor;

I'm afraid there is nothing left of stippi's earlier solution. This fixes
  bug #5097.
* The alignment as specified in the BToolTip is now respected.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34616 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2009-12-10 14:31:58 +00:00
parent 3533b6597d
commit 9d6dc833bd

View File

@ -30,126 +30,271 @@ static const uint32 kMsgCloseToolTip = 'clos';
namespace BPrivate { namespace BPrivate {
class ToolTipView : public BView { class ToolTipView : public BView {
public: public:
ToolTipView(BToolTip* tip) ToolTipView(BToolTip* tip);
: virtual ~ToolTipView();
BView("tool tip", B_WILL_DRAW),
fToolTip(tip),
fHidden(false)
{
fToolTip->AcquireReference();
SetViewColor(ui_color(B_TOOL_TIP_BACKGROUND_COLOR));
BGroupLayout* layout = new BGroupLayout(B_VERTICAL); virtual void AttachedToWindow();
layout->SetInsets(5, 5, 5, 5); virtual void DetachedFromWindow();
SetLayout(layout);
AddChild(fToolTip->View()); virtual void FrameResized(float width, float height);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
void HideTip();
void ShowTip();
void ResetWindowFrame(BPoint where);
BToolTip* Tip() const { return fToolTip; }
bool IsTipHidden() const { return fHidden; }
private:
BToolTip* fToolTip;
bool fHidden;
};
ToolTipView::ToolTipView(BToolTip* tip)
:
BView("tool tip", B_WILL_DRAW | B_FRAME_EVENTS),
fToolTip(tip),
fHidden(false)
{
fToolTip->AcquireReference();
SetViewColor(ui_color(B_TOOL_TIP_BACKGROUND_COLOR));
BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
layout->SetInsets(5, 5, 5, 5);
SetLayout(layout);
AddChild(fToolTip->View());
}
ToolTipView::~ToolTipView()
{
fToolTip->ReleaseReference();
}
void
ToolTipView::AttachedToWindow()
{
SetEventMask(B_POINTER_EVENTS, 0);
fToolTip->AttachedToWindow();
}
void
ToolTipView::DetachedFromWindow()
{
BToolTipManager* manager = BToolTipManager::Manager();
manager->Lock();
RemoveChild(fToolTip->View());
// don't delete this one!
fToolTip->DetachedFromWindow();
manager->Unlock();
}
void
ToolTipView::FrameResized(float width, float height)
{
BPoint where;
GetMouse(&where, NULL, false);
ResetWindowFrame(ConvertToScreen(where));
}
void
ToolTipView::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (fToolTip->IsSticky()) {
ResetWindowFrame(ConvertToScreen(where));
} else if (transit == B_ENTERED_VIEW) {
// close instantly if the user managed to enter
Window()->Quit();
} else {
// close with the preferred delay in case the mouse just moved
HideTip();
} }
}
virtual ~ToolTipView()
{
fToolTip->ReleaseReference();
}
virtual void AttachedToWindow() void
{ ToolTipView::HideTip()
SetEventMask(B_POINTER_EVENTS, 0); {
fToolTip->AttachedToWindow(); if (fHidden)
} return;
virtual void DetachedFromWindow() BMessage quit(kMsgCloseToolTip);
{ BMessageRunner::StartSending(Window(), &quit,
BToolTipManager* manager = BToolTipManager::Manager(); BToolTipManager::Manager()->HideDelay(), 1);
manager->Lock(); fHidden = true;
}
RemoveChild(fToolTip->View());
// don't delete this one!
fToolTip->DetachedFromWindow();
manager->Unlock(); void
} ToolTipView::ShowTip()
{
fHidden = false;
}
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage) /*! Tries to find the right frame to show the tool tip in, trying to use the
{ alignment that the tool tip specifies.
if (fToolTip->IsSticky()) { Makes sure the tool tip can be shown on screen in its entirety, ie. it will
// TODO: move window with mouse! resize the window if necessary.
Window()->MoveTo( */
ConvertToScreen(where) + fToolTip->MouseRelativeLocation()); void
} else if (transit == B_ENTERED_VIEW) { ToolTipView::ResetWindowFrame(BPoint where)
// close instantly if the user managed to enter {
Window()->Quit(); if (Window() == NULL)
} else { return;
// close with the preferred delay in case the mouse just moved
HideTip(); BSize size = PreferredSize();
BScreen screen(Window());
BRect screenFrame = screen.Frame().InsetBySelf(2, 2);
BPoint offset = fToolTip->MouseRelativeLocation();
// Ensure that the tip can be placed on screen completely
if (size.width > screenFrame.Width())
size.width = screenFrame.Width();
if (size.width > where.x - screenFrame.left
&& size.width > screenFrame.right - where.x) {
// There is no space to put the tip to the left or the right of the
// cursor, it can either be below or above it
if (size.height > where.y - screenFrame.top
&& where.y - screenFrame.top > screenFrame.Height() / 2) {
size.height = where.y - offset.y - screenFrame.top;
} else if (size.height > screenFrame.bottom - where.y
&& screenFrame.bottom - where.y > screenFrame.Height() / 2) {
size.height = screenFrame.bottom - where.y - offset.y;
} }
} }
void HideTip() // Find best alignment, starting with the requested one
{
if (fHidden)
return;
BMessage quit(kMsgCloseToolTip); BAlignment alignment = fToolTip->Alignment();
BMessageRunner::StartSending(Window(), &quit, BPoint location = where;
BToolTipManager::Manager()->HideDelay(), 1); bool doesNotFit = false;
fHidden = true;
switch (alignment.horizontal) {
case B_ALIGN_LEFT:
location.x -= size.width + offset.x;
if (location.x < screenFrame.left) {
location.x = screenFrame.left;
doesNotFit = true;
}
break;
case B_ALIGN_CENTER:
location.x -= size.width / 2 - offset.x;
if (location.x < screenFrame.left) {
location.x = screenFrame.left;
doesNotFit = true;
} else if (location.x + size.width > screenFrame.right) {
location.x = screenFrame.right - size.width;
doesNotFit = true;
}
break;
default:
location.x += offset.x;
if (location.x + size.width > screenFrame.right) {
location.x = screenFrame.right - size.width;
doesNotFit = true;
}
break;
} }
void ShowTip() if ((doesNotFit && alignment.vertical == B_ALIGN_MIDDLE)
{ || (alignment.vertical == B_ALIGN_MIDDLE
fHidden = false; && alignment.horizontal == B_ALIGN_CENTER))
alignment.vertical = B_ALIGN_BOTTOM;
while (true) {
switch (alignment.vertical) {
case B_ALIGN_TOP:
location.y = where.y - size.height - offset.y;
if (location.y < screenFrame.top) {
alignment.vertical = B_ALIGN_BOTTOM;
continue;
}
break;
case B_ALIGN_MIDDLE:
location.y -= size.height / 2 - offset.y;
if (location.y < screenFrame.top)
location.y = screenFrame.top;
else if (location.y + size.height > screenFrame.bottom)
location.y = screenFrame.bottom - size.height;
break;
default:
location.y = where.y + offset.y;
if (location.y + size.height > screenFrame.bottom) {
alignment.vertical = B_ALIGN_TOP;
continue;
}
break;
}
break;
} }
BToolTip* Tip() const { return fToolTip; } where = location;
bool IsTipHidden() const { return fHidden; }
private: // Cut off any out-of-screen areas
BToolTip* fToolTip;
bool fHidden; if (screenFrame.left > where.x) {
}; size.width -= where.x - screenFrame.left;
where.x = screenFrame.left;
} else if (screenFrame.right < where.x + size.width)
size.width = screenFrame.right - where.x;
if (screenFrame.top > where.y) {
size.height -= where.y - screenFrame.top;
where.y = screenFrame.top;
} else if (screenFrame.bottom < where.y + size.height)
size.height -= screenFrame.bottom - where.y;
// Change window frame
Window()->ResizeTo(size.width, size.height);
Window()->MoveTo(where);
}
// #pragma mark -
ToolTipWindow::ToolTipWindow(BToolTip* tip, BPoint where) ToolTipWindow::ToolTipWindow(BToolTip* tip, BPoint where)
: :
BWindow(BRect(0, 0, 250, 10), "tool tip", B_BORDERED_WINDOW_LOOK, BWindow(BRect(0, 0, 250, 10).OffsetBySelf(where), "tool tip",
kMenuWindowFeel, B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE B_BORDERED_WINDOW_LOOK, kMenuWindowFeel,
| B_AUTO_UPDATE_SIZE_LIMITS | B_AVOID_FRONT | B_AVOID_FOCUS) B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_AUTO_UPDATE_SIZE_LIMITS
| B_AVOID_FRONT | B_AVOID_FOCUS)
{ {
SetLayout(new BGroupLayout(B_VERTICAL)); SetLayout(new BGroupLayout(B_VERTICAL));
BToolTipManager* manager = BToolTipManager::Manager(); BToolTipManager* manager = BToolTipManager::Manager();
ToolTipView* view = new ToolTipView(tip);
manager->Lock(); manager->Lock();
AddChild(new ToolTipView(tip)); AddChild(view);
manager->Unlock(); manager->Unlock();
BSize size = ChildAt(0)->PreferredSize(); // figure out size and location
ResizeTo(size.width, size.height);
//AddChild(BLayoutBuilder::Group<>(B_VERTICAL).Add(new ToolTipView(tip)));
// figure out location view->ResetWindowFrame(where);
// TODO: take alignment into account!
where += tip->MouseRelativeLocation();
BScreen screen(this);
if (screen.IsValid()) {
BRect screenFrame = screen.Frame().InsetBySelf(5, 5);
BRect frame = Frame().OffsetToSelf(where);
if (!screenFrame.Contains(frame)) {
if (screenFrame.top > frame.top)
where.y -= frame.top - screenFrame.top;
else if (screenFrame.bottom < frame.bottom)
where.y -= frame.bottom - screenFrame.bottom;
if (screenFrame.left > frame.left)
where.x -= frame.left - screenFrame.left;
else if (screenFrame.right < frame.right)
where.x -= frame.right - screenFrame.right;
}
}
MoveTo(where);
} }
@ -189,10 +334,11 @@ ToolTipWindow::MessageReceived(BMessage* message)
} }
} }
} // namespace BPrivate } // namespace BPrivate
// #pragma mark - // #pragma mark -
/*static*/ BToolTipManager* /*static*/ BToolTipManager*