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