xref: /haiku/src/kits/interface/ToolTipManager.cpp (revision 239222b2369c39dc52df52b0a7cdd6cc0a91bc92)
1 /*
2  * Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <ToolTipManager.h>
9 #include <ToolTipWindow.h>
10 
11 #include <Autolock.h>
12 #include <LayoutBuilder.h>
13 #include <MessageRunner.h>
14 #include <Screen.h>
15 
16 #include <WindowPrivate.h>
17 #include <ToolTip.h>
18 
19 
20 BLocker BToolTipManager::sLock("tool tip manager");
21 BToolTipManager* BToolTipManager::sDefaultInstance;
22 
23 static const uint32 kMsgHideToolTip = 'hide';
24 static const uint32 kMsgShowToolTip = 'show';
25 static const uint32 kMsgCurrentToolTip = 'curr';
26 static const uint32 kMsgCloseToolTip = 'clos';
27 
28 
29 namespace BPrivate {
30 
31 class ToolTipView : public BView {
32 public:
33 	ToolTipView(BToolTip* tip)
34 		:
35 		BView("tool tip", B_WILL_DRAW),
36 		fToolTip(tip),
37 		fHidden(false)
38 	{
39 		fToolTip->AcquireReference();
40 		SetViewColor(ui_color(B_TOOL_TIP_BACKGROUND_COLOR));
41 
42 		BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
43 		layout->SetInsets(5, 5, 5, 5);
44 		SetLayout(layout);
45 
46 		AddChild(fToolTip->View());
47 	}
48 
49 	virtual ~ToolTipView()
50 	{
51 		fToolTip->ReleaseReference();
52 	}
53 
54 	virtual void AttachedToWindow()
55 	{
56 		SetEventMask(B_POINTER_EVENTS, 0);
57 		fToolTip->AttachedToWindow();
58 	}
59 
60 	virtual void DetachedFromWindow()
61 	{
62 		BToolTipManager::Lock();
63 
64 		RemoveChild(fToolTip->View());
65 			// don't delete this one!
66 		fToolTip->DetachedFromWindow();
67 
68 		BToolTipManager::Unlock();
69 	}
70 
71 	virtual void MouseMoved(BPoint where, uint32 transit,
72 		const BMessage* dragMessage)
73 	{
74 		if (fToolTip->IsSticky()) {
75 			// TODO: move window with mouse!
76 			Window()->MoveTo(
77 				ConvertToScreen(where) + fToolTip->MouseRelativeLocation());
78 		} else if (transit == B_ENTERED_VIEW) {
79 			// close instantly if the user managed to enter
80 			Window()->Quit();
81 		} else {
82 			// close with the preferred delay in case the mouse just moved
83 			HideTip();
84 		}
85 	}
86 
87 	void HideTip()
88 	{
89 		if (fHidden)
90 			return;
91 
92 		BMessage quit(kMsgCloseToolTip);
93 		BMessageRunner::StartSending(Window(), &quit,
94 			BToolTipManager::Manager()->HideDelay(), 1);
95 		fHidden = true;
96 	}
97 
98 	void ShowTip()
99 	{
100 		fHidden = false;
101 	}
102 
103 	BToolTip* Tip() const { return fToolTip; }
104 	bool IsTipHidden() const { return fHidden; }
105 
106 private:
107 	BToolTip*	fToolTip;
108 	bool		fHidden;
109 };
110 
111 
112 ToolTipWindow::ToolTipWindow(BToolTip* tip, BPoint where)
113 	:
114 	BWindow(BRect(0, 0, 250, 10), "tool tip", B_BORDERED_WINDOW_LOOK,
115 		kMenuWindowFeel, B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE
116 			| B_AUTO_UPDATE_SIZE_LIMITS | B_AVOID_FRONT | B_AVOID_FOCUS)
117 {
118 	SetLayout(new BGroupLayout(B_VERTICAL));
119 
120 	BToolTipManager::Lock();
121 	AddChild(new ToolTipView(tip));
122 	BToolTipManager::Unlock();
123 
124 	BSize size = ChildAt(0)->PreferredSize();
125 	ResizeTo(size.width, size.height);
126 	//AddChild(BLayoutBuilder::Group<>(B_VERTICAL).Add(new ToolTipView(tip)));
127 
128 	// figure out location
129 	// TODO: take alignment into account!
130 	where += tip->MouseRelativeLocation();
131 
132 	BScreen screen(this);
133 	if (screen.IsValid()) {
134 		BRect screenFrame = screen.Frame().InsetBySelf(5, 5);
135 		BRect frame = Frame().OffsetToSelf(where);
136 		if (!screenFrame.Contains(frame)) {
137 			if (screenFrame.top > frame.top)
138 				where.y -= frame.top - screenFrame.top;
139 			else if (screenFrame.bottom < frame.bottom)
140 				where.y -= frame.bottom - screenFrame.bottom;
141 			if (screenFrame.left > frame.left)
142 				where.x -= frame.left - screenFrame.left;
143 			else if (screenFrame.right < frame.right)
144 				where.x -= frame.right - screenFrame.right;
145 		}
146 	}
147 
148 	MoveTo(where);
149 }
150 
151 
152 void
153 ToolTipWindow::MessageReceived(BMessage* message)
154 {
155 	ToolTipView* view = static_cast<ToolTipView*>(ChildAt(0));
156 
157 	switch (message->what) {
158 		case kMsgHideToolTip:
159 			view->HideTip();
160 			break;
161 
162 		case kMsgCurrentToolTip:
163 		{
164 			BToolTip* tip = view->Tip();
165 
166 			BMessage reply(B_REPLY);
167 			reply.AddPointer("current", tip);
168 
169 			if (message->SendReply(&reply) == B_OK)
170 				tip->AcquireReference();
171 			break;
172 		}
173 
174 		case kMsgShowToolTip:
175 			view->ShowTip();
176 			break;
177 
178 		case kMsgCloseToolTip:
179 			if (view->IsTipHidden())
180 				Quit();
181 			break;
182 
183 		default:
184 			BWindow::MessageReceived(message);
185 	}
186 }
187 
188 }	// namespace BPrivate
189 
190 
191 //	#pragma mark -
192 
193 
194 /*static*/ BToolTipManager*
195 BToolTipManager::Manager()
196 {
197 	BAutolock _(sLock);
198 
199 	if (sDefaultInstance == NULL)
200 		sDefaultInstance = new BToolTipManager();
201 
202 	return sDefaultInstance;
203 }
204 
205 
206 void
207 BToolTipManager::ShowTip(BToolTip* tip, BPoint point)
208 {
209 	BToolTip* current = NULL;
210 	BMessage reply;
211 	if (fWindow.SendMessage(kMsgCurrentToolTip, &reply) == B_OK)
212 		reply.FindPointer("current", (void**)&current);
213 
214 	if (current != NULL)
215 		current->ReleaseReference();
216 
217 	if (current == tip) {
218 		fWindow.SendMessage(kMsgShowToolTip);
219 		return;
220 	}
221 
222 	fWindow.SendMessage(kMsgHideToolTip);
223 
224 	if (current != NULL)
225 		current->ReleaseReference();
226 
227 	if (tip != NULL) {
228 		BWindow* window = new BPrivate::ToolTipWindow(tip, point);
229 		window->Show();
230 
231 		fWindow = BMessenger(window);
232 	}
233 }
234 
235 
236 void
237 BToolTipManager::HideTip()
238 {
239 	fWindow.SendMessage(kMsgHideToolTip);
240 }
241 
242 
243 void
244 BToolTipManager::SetShowDelay(bigtime_t time)
245 {
246 	// between 10ms and 3s
247 	if (time < 10000)
248 		time = 10000;
249 	else if (time > 3000000)
250 		time = 3000000;
251 
252 	fShowDelay = time;
253 }
254 
255 
256 bigtime_t
257 BToolTipManager::ShowDelay() const
258 {
259 	return fShowDelay;
260 }
261 
262 
263 void
264 BToolTipManager::SetHideDelay(bigtime_t time)
265 {
266 	// between 0 and 0.5s
267 	if (time < 0)
268 		time = 0;
269 	else if (time > 500000)
270 		time = 500000;
271 
272 	fHideDelay = time;
273 }
274 
275 
276 bigtime_t
277 BToolTipManager::HideDelay() const
278 {
279 	return fHideDelay;
280 }
281 
282 
283 BToolTipManager::BToolTipManager()
284 	:
285 	fShowDelay(750000),
286 	fHideDelay(50000)
287 {
288 }
289 
290 
291 BToolTipManager::~BToolTipManager()
292 {
293 }
294