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**)¤t); 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