* 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
+191 -45
View File
@@ -30,11 +30,35 @@ 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();
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
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), BView("tool tip", B_WILL_DRAW | B_FRAME_EVENTS),
fToolTip(tip), fToolTip(tip),
fHidden(false) fHidden(false)
{ {
@@ -48,18 +72,23 @@ public:
AddChild(fToolTip->View()); AddChild(fToolTip->View());
} }
virtual ~ToolTipView()
ToolTipView::~ToolTipView()
{ {
fToolTip->ReleaseReference(); fToolTip->ReleaseReference();
} }
virtual void AttachedToWindow()
void
ToolTipView::AttachedToWindow()
{ {
SetEventMask(B_POINTER_EVENTS, 0); SetEventMask(B_POINTER_EVENTS, 0);
fToolTip->AttachedToWindow(); fToolTip->AttachedToWindow();
} }
virtual void DetachedFromWindow()
void
ToolTipView::DetachedFromWindow()
{ {
BToolTipManager* manager = BToolTipManager::Manager(); BToolTipManager* manager = BToolTipManager::Manager();
manager->Lock(); manager->Lock();
@@ -71,13 +100,23 @@ public:
manager->Unlock(); manager->Unlock();
} }
virtual void MouseMoved(BPoint where, uint32 transit,
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) const BMessage* dragMessage)
{ {
if (fToolTip->IsSticky()) { if (fToolTip->IsSticky()) {
// TODO: move window with mouse! ResetWindowFrame(ConvertToScreen(where));
Window()->MoveTo(
ConvertToScreen(where) + fToolTip->MouseRelativeLocation());
} else if (transit == B_ENTERED_VIEW) { } else if (transit == B_ENTERED_VIEW) {
// close instantly if the user managed to enter // close instantly if the user managed to enter
Window()->Quit(); Window()->Quit();
@@ -87,7 +126,9 @@ public:
} }
} }
void HideTip()
void
ToolTipView::HideTip()
{ {
if (fHidden) if (fHidden)
return; return;
@@ -98,58 +139,162 @@ public:
fHidden = true; fHidden = true;
} }
void ShowTip()
void
ToolTipView::ShowTip()
{ {
fHidden = false; fHidden = false;
} }
BToolTip* Tip() const { return fToolTip; }
bool IsTipHidden() const { return fHidden; }
private: /*! Tries to find the right frame to show the tool tip in, trying to use the
BToolTip* fToolTip; alignment that the tool tip specifies.
bool fHidden; Makes sure the tool tip can be shown on screen in its entirety, ie. it will
}; resize the window if necessary.
*/
void
ToolTipView::ResetWindowFrame(BPoint where)
{
if (Window() == NULL)
return;
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;
}
}
// Find best alignment, starting with the requested one
BAlignment alignment = fToolTip->Alignment();
BPoint location = where;
bool doesNotFit = false;
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;
}
if ((doesNotFit && alignment.vertical == B_ALIGN_MIDDLE)
|| (alignment.vertical == B_ALIGN_MIDDLE
&& 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;
}
where = location;
// Cut off any out-of-screen areas
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,6 +334,7 @@ ToolTipWindow::MessageReceived(BMessage* message)
} }
} }
} // namespace BPrivate } // namespace BPrivate