1 /* 2 * Copyright (c) 2001-2005, Haiku. 3 * 4 * Authors: 5 * Marc Flerackers (mflerackers@androme.be) 6 * Mike Wilber 7 * Stefano Ceccherini (burton666@libero.it) 8 * Ivan Tonizza 9 */ 10 11 #include <Button.h> 12 #include <Font.h> 13 #include <String.h> 14 #include <Window.h> 15 16 BButton::BButton(BRect frame, const char *name, const char *label, BMessage *message, 17 uint32 resizingMode, uint32 flags) 18 : BControl(frame, name, label, message, resizingMode, flags |= B_WILL_DRAW), 19 fDrawAsDefault(false) 20 { 21 // Resize to minimum height if needed 22 font_height fh; 23 GetFontHeight(&fh); 24 float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent); 25 if (Bounds().Height() < minHeight) 26 ResizeTo(Bounds().Width(), minHeight); 27 } 28 29 30 BButton::~BButton() 31 { 32 } 33 34 35 BButton::BButton(BMessage *archive) 36 : BControl (archive) 37 { 38 if (archive->FindBool("_default", &fDrawAsDefault) != B_OK) 39 fDrawAsDefault = false; 40 } 41 42 43 BArchivable * 44 BButton::Instantiate(BMessage *archive) 45 { 46 if (validate_instantiation(archive, "BButton")) 47 return new BButton(archive); 48 else 49 return NULL; 50 } 51 52 53 status_t 54 BButton::Archive(BMessage* archive, bool deep) const 55 { 56 status_t err = BControl::Archive(archive, deep); 57 58 if (err != B_OK) 59 return err; 60 61 if (IsDefault()) 62 err = archive->AddBool("_default", true); 63 64 return err; 65 } 66 67 68 void 69 BButton::Draw(BRect updateRect) 70 { 71 font_height fh; 72 GetFontHeight(&fh); 73 74 const BRect bounds = Bounds(); 75 BRect rect = bounds; 76 77 const bool enabled = IsEnabled(); 78 const bool pushed = Value() == B_CONTROL_ON; 79 80 // Default indicator 81 if (IsDefault()) 82 rect = DrawDefault(rect,enabled); 83 else 84 rect.InsetBy(1.0f,1.0f); 85 86 BRect fillArea = rect; 87 fillArea.InsetBy(3.0f,3.0f); 88 89 BString text = Label(); 90 91 #if 1 92 // Label truncation 93 BFont font; 94 GetFont(&font); 95 font.TruncateString(&text, B_TRUNCATE_END, fillArea.Width()); 96 #endif 97 98 // Label position 99 const float stringWidth = StringWidth(text.String()); 100 const float x = (bounds.right - stringWidth) / 2.0f; 101 const float labelY = bounds.top 102 + ((bounds.Height() - fh.ascent - fh.descent) / 2.0f) 103 + fh.ascent + 1.0f; 104 const float focusLineY = labelY + fh.descent; 105 106 /* speed trick: 107 if the focus changes but the button is not pressed then we can 108 redraw only the focus line, 109 if the focus changes and the button is pressed invert the internal rect 110 this block takes care of all the focus changes 111 */ 112 if (IsFocusChanging()) { 113 if (pushed) { 114 rect.InsetBy(2.0,2.0); 115 InvertRect(rect); 116 } else 117 DrawFocusLine(x, focusLineY, stringWidth, IsFocus() && Window()->IsActive()); 118 119 return; 120 } 121 122 // Colors 123 const rgb_color panelBgColor = ui_color(B_PANEL_BACKGROUND_COLOR); 124 const rgb_color buttonBgColor=tint_color(panelBgColor, B_LIGHTEN_1_TINT); 125 const rgb_color maxLightColor=tint_color(panelBgColor, B_LIGHTEN_MAX_TINT); 126 const rgb_color maxShadowColor=tint_color(panelBgColor, B_DARKEN_MAX_TINT); 127 const rgb_color darkBorderColor = tint_color(panelBgColor, 128 enabled ? B_DARKEN_4_TINT : B_DARKEN_2_TINT); 129 const rgb_color firstBevelColor = enabled ? tint_color(panelBgColor, B_DARKEN_2_TINT) 130 : panelBgColor; 131 const rgb_color cornerColor = IsDefault() ? firstBevelColor : panelBgColor; 132 133 // Fill the button area 134 SetHighColor(buttonBgColor); 135 FillRect(fillArea); 136 137 // external border 138 SetHighColor(darkBorderColor); 139 StrokeRect(rect); 140 141 BeginLineArray(14); 142 143 // Corners 144 AddLine(rect.LeftTop(), rect.LeftTop(), cornerColor); 145 AddLine(rect.LeftBottom(), rect.LeftBottom(), cornerColor); 146 AddLine(rect.RightTop(), rect.RightTop(), cornerColor); 147 AddLine(rect.RightBottom(), rect.RightBottom(), cornerColor); 148 149 rect.InsetBy(1.0f,1.0f); 150 151 // Shadow 152 AddLine(rect.LeftBottom(), rect.RightBottom(), firstBevelColor); 153 AddLine(rect.RightBottom(), rect.RightTop(), firstBevelColor); 154 // Light 155 AddLine(rect.LeftTop(), rect.LeftBottom(),buttonBgColor); 156 AddLine(rect.LeftTop(), rect.RightTop(), buttonBgColor); 157 158 rect.InsetBy(1.0f, 1.0f); 159 160 // Shadow 161 AddLine(rect.LeftBottom(), rect.RightBottom(), panelBgColor); 162 AddLine(rect.RightBottom(), rect.RightTop(), panelBgColor); 163 // Light 164 AddLine(rect.LeftTop(), rect.LeftBottom(),maxLightColor); 165 AddLine(rect.LeftTop(), rect.RightTop(), maxLightColor); 166 167 rect.InsetBy(1.0f,1.0f); 168 169 // Light 170 AddLine(rect.LeftTop(), rect.LeftBottom(),maxLightColor); 171 AddLine(rect.LeftTop(), rect.RightTop(), maxLightColor); 172 173 EndLineArray(); 174 175 // Invert if clicked 176 if (enabled && pushed) { 177 rect.InsetBy(-2.0f,-2.0f); 178 InvertRect(rect); 179 } 180 181 // Label color 182 if (enabled) { 183 if (pushed) { 184 SetHighColor(maxLightColor); 185 SetLowColor(maxShadowColor); 186 } else { 187 SetHighColor(maxShadowColor); 188 SetLowColor(tint_color(panelBgColor, B_LIGHTEN_2_TINT)); 189 } 190 } else { 191 SetHighColor(tint_color(panelBgColor, B_DISABLED_LABEL_TINT)); 192 SetLowColor(tint_color(panelBgColor, B_LIGHTEN_2_TINT)); 193 } 194 195 // Draw the label 196 DrawString(text.String(), BPoint(x, labelY)); 197 198 // Focus line 199 if (enabled && IsFocus() && Window()->IsActive() && !pushed) 200 DrawFocusLine(x,focusLineY,stringWidth,true); 201 202 } 203 204 205 void 206 BButton::MouseDown(BPoint point) 207 { 208 if (!IsEnabled()) 209 return; 210 211 SetValue(B_CONTROL_ON); 212 213 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 214 SetTracking(true); 215 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 216 217 } else { 218 BRect bounds = Bounds(); 219 uint32 buttons; 220 221 do { 222 Window()->UpdateIfNeeded(); 223 224 snooze(40000); 225 226 GetMouse(&point, &buttons, true); 227 228 bool inside = bounds.Contains(point); 229 230 if ((Value() == B_CONTROL_ON) != inside) 231 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF); 232 } while (buttons != 0); 233 234 if (Value() == B_CONTROL_ON) 235 Invoke(); 236 } 237 } 238 239 240 void 241 BButton::AttachedToWindow() 242 { 243 BControl::AttachedToWindow(); 244 245 if (IsDefault()) 246 Window()->SetDefaultButton(this); 247 } 248 249 250 void 251 BButton::KeyDown(const char *bytes, int32 numBytes) 252 { 253 if (*bytes == B_ENTER || *bytes == B_SPACE) { 254 if (!IsEnabled()) 255 return; 256 257 SetValue(B_CONTROL_ON); 258 Invoke(); 259 260 } else 261 BControl::KeyDown(bytes, numBytes); 262 } 263 264 265 void 266 BButton::MakeDefault(bool flag) 267 { 268 BButton *oldDefault = NULL; 269 BWindow *window = Window(); 270 271 if (window) 272 oldDefault = window->DefaultButton(); 273 274 if (flag) { 275 if (fDrawAsDefault && oldDefault == this) 276 return; 277 278 if (!fDrawAsDefault) { 279 fDrawAsDefault = true; 280 281 ResizeBy(6.0f, 6.0f); 282 MoveBy(-3.0f, -3.0f); 283 } 284 285 if (window && oldDefault != this) 286 window->SetDefaultButton(this); 287 } else { 288 if (!fDrawAsDefault) 289 return; 290 291 fDrawAsDefault = false; 292 293 ResizeBy(-6.0f, -6.0f); 294 MoveBy(3.0f, 3.0f); 295 296 if (window && oldDefault == this) 297 window->SetDefaultButton(NULL); 298 } 299 } 300 301 302 void 303 BButton::SetLabel(const char *string) 304 { 305 BControl::SetLabel(string); 306 } 307 308 309 bool 310 BButton::IsDefault() const 311 { 312 return fDrawAsDefault; 313 } 314 315 316 void 317 BButton::MessageReceived(BMessage *message) 318 { 319 BControl::MessageReceived(message); 320 } 321 322 323 void 324 BButton::WindowActivated(bool active) 325 { 326 BControl::WindowActivated(active); 327 } 328 329 330 void 331 BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 332 { 333 if (!IsTracking()) 334 return; 335 336 bool inside = Bounds().Contains(point); 337 338 if ((Value() == B_CONTROL_ON) != inside) 339 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF); 340 } 341 342 343 void 344 BButton::MouseUp(BPoint point) 345 { 346 if (!IsTracking()) 347 return; 348 349 if (Bounds().Contains(point)) 350 Invoke(); 351 352 SetTracking(false); 353 } 354 355 356 void 357 BButton::DetachedFromWindow() 358 { 359 BControl::DetachedFromWindow(); 360 } 361 362 363 void 364 BButton::SetValue(int32 value) 365 { 366 if (value != Value()) { 367 BControl::SetValue(value); 368 Invalidate(); 369 } 370 } 371 372 373 void 374 BButton::GetPreferredSize(float *width, float *height) 375 { 376 font_height fh; 377 GetFontHeight(&fh); 378 379 *height = 12.0f + (float)ceil(fh.ascent + fh.descent); 380 *width = 20.0f + (float)ceil(StringWidth(Label())); 381 382 if (*width < 75.0f) 383 *width = 75.0f; 384 385 if (fDrawAsDefault) { 386 *width += 6.0f; 387 *height += 6.0f; 388 } 389 } 390 391 392 void 393 BButton::ResizeToPreferred() 394 { 395 BControl::ResizeToPreferred(); 396 } 397 398 399 status_t 400 BButton::Invoke(BMessage *message) 401 { 402 Sync(); 403 snooze(50000); 404 405 status_t err = BControl::Invoke(message); 406 407 SetValue(B_CONTROL_OFF); 408 409 return err; 410 } 411 412 413 void 414 BButton::FrameMoved(BPoint newLocation) 415 { 416 BControl::FrameMoved(newLocation); 417 } 418 419 420 void 421 BButton::FrameResized(float width, float height) 422 { 423 BControl::FrameResized(width, height); 424 } 425 426 427 void 428 BButton::MakeFocus(bool focused) 429 { 430 BControl::MakeFocus(focused); 431 } 432 433 434 void 435 BButton::AllAttached() 436 { 437 BControl::AllAttached(); 438 } 439 440 441 void 442 BButton::AllDetached() 443 { 444 BControl::AllDetached(); 445 } 446 447 448 BHandler * 449 BButton::ResolveSpecifier(BMessage *message, int32 index, 450 BMessage *specifier, int32 what, 451 const char *property) 452 { 453 return BControl::ResolveSpecifier(message, index, specifier, what, property); 454 } 455 456 457 status_t 458 BButton::GetSupportedSuites(BMessage *message) 459 { 460 return BControl::GetSupportedSuites(message); 461 } 462 463 464 status_t 465 BButton::Perform(perform_code d, void *arg) 466 { 467 return BControl::Perform(d, arg); 468 } 469 470 471 void BButton::_ReservedButton1() {} 472 void BButton::_ReservedButton2() {} 473 void BButton::_ReservedButton3() {} 474 475 476 BButton & 477 BButton::operator=(const BButton &) 478 { 479 return *this; 480 } 481 482 483 BRect 484 BButton::DrawDefault(BRect bounds, bool enabled) 485 { 486 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), 487 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), 488 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 489 490 rgb_color borderColor; 491 if (enabled) 492 borderColor = tint_color(no_tint, B_DARKEN_4_TINT); 493 else 494 borderColor = darken1; 495 496 // Dark border 497 BeginLineArray(4); 498 AddLine(BPoint(bounds.left, bounds.bottom - 1.0f), 499 BPoint(bounds.left, bounds.top + 1.0f), borderColor); 500 AddLine(BPoint(bounds.left + 1.0f, bounds.top), 501 BPoint(bounds.right - 1.0f, bounds.top), borderColor); 502 AddLine(BPoint(bounds.right, bounds.top + 1.0f), 503 BPoint(bounds.right, bounds.bottom - 1.0f), borderColor); 504 AddLine(BPoint(bounds.left + 1.0f, bounds.bottom), 505 BPoint(bounds.right - 1.0f, bounds.bottom), borderColor); 506 EndLineArray(); 507 508 if (enabled) { 509 // Bevel 510 bounds.InsetBy(1.0f, 1.0f); 511 SetHighColor(darken1); 512 StrokeRect(bounds); 513 } 514 515 bounds.InsetBy(1.0f, 1.0f); 516 517 // Filling 518 float inset = enabled? 2.0f : 3.0f; 519 SetHighColor(lighten1); 520 521 FillRect(BRect(bounds.left, bounds.top, 522 bounds.right, bounds.top+inset-1.0f)); 523 FillRect(BRect(bounds.left, bounds.bottom-inset+1.0f, 524 bounds.right, bounds.bottom)); 525 FillRect(BRect(bounds.left, bounds.top+inset-1.0f, 526 bounds.left+inset-1.0f, bounds.bottom-inset+1.0f)); 527 FillRect(BRect(bounds.right-inset+1.0f, bounds.top+inset-1.0f, 528 bounds.right, bounds.bottom-inset+1.0f)); 529 530 bounds.InsetBy(inset,inset); 531 532 return bounds; 533 } 534 535 536 status_t 537 BButton::Execute() 538 { 539 if (!IsEnabled()) 540 return B_ERROR; 541 542 SetValue(B_CONTROL_ON); 543 return Invoke(); 544 } 545 546 547 void 548 BButton::DrawFocusLine(float x, float y, float width, bool visible) 549 { 550 if (visible) 551 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 552 else 553 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 554 B_LIGHTEN_1_TINT)); 555 // Blue Line 556 StrokeLine(BPoint(x, y), BPoint(x + width, y)); 557 558 if (visible) 559 SetHighColor(255, 255, 255); 560 // White Line 561 StrokeLine(BPoint(x, y + 1.0f), BPoint(x + width, y + 1.0f)); 562 } 563