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