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 fDrawAsDefault = true; 279 280 ResizeBy(6.0f, 6.0f); 281 MoveBy(-3.0f, -3.0f); 282 283 if (window && oldDefault != this) 284 window->SetDefaultButton(this); 285 } else { 286 if (!fDrawAsDefault) 287 return; 288 289 fDrawAsDefault = false; 290 291 ResizeBy(-6.0f, -6.0f); 292 MoveBy(3.0f, 3.0f); 293 294 if (window && oldDefault == this) 295 window->SetDefaultButton(NULL); 296 } 297 } 298 299 300 void 301 BButton::SetLabel(const char *string) 302 { 303 BControl::SetLabel(string); 304 } 305 306 307 bool 308 BButton::IsDefault() const 309 { 310 return fDrawAsDefault; 311 } 312 313 314 void 315 BButton::MessageReceived(BMessage *message) 316 { 317 BControl::MessageReceived(message); 318 } 319 320 321 void 322 BButton::WindowActivated(bool active) 323 { 324 BControl::WindowActivated(active); 325 } 326 327 328 void 329 BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 330 { 331 if (!IsTracking()) 332 return; 333 334 bool inside = Bounds().Contains(point); 335 336 if ((Value() == B_CONTROL_ON) != inside) 337 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF); 338 } 339 340 341 void 342 BButton::MouseUp(BPoint point) 343 { 344 if (!IsTracking()) 345 return; 346 347 if (Bounds().Contains(point)) 348 Invoke(); 349 350 SetTracking(false); 351 } 352 353 354 void 355 BButton::DetachedFromWindow() 356 { 357 BControl::DetachedFromWindow(); 358 } 359 360 361 void 362 BButton::SetValue(int32 value) 363 { 364 if (value != Value()) { 365 BControl::SetValue(value); 366 Invalidate(); 367 } 368 } 369 370 371 void 372 BButton::GetPreferredSize(float *width, float *height) 373 { 374 font_height fh; 375 GetFontHeight(&fh); 376 377 *height = 12.0f + (float)ceil(fh.ascent + fh.descent); 378 *width = 20.0f + (float)ceil(StringWidth(Label())); 379 380 if (*width < 75.0f) 381 *width = 75.0f; 382 383 if (fDrawAsDefault) { 384 *width += 6.0f; 385 *height += 6.0f; 386 } 387 } 388 389 390 void 391 BButton::ResizeToPreferred() 392 { 393 BControl::ResizeToPreferred(); 394 } 395 396 397 status_t 398 BButton::Invoke(BMessage *message) 399 { 400 Sync(); 401 snooze(50000); 402 403 status_t err = BControl::Invoke(message); 404 405 SetValue(B_CONTROL_OFF); 406 407 return err; 408 } 409 410 411 void 412 BButton::FrameMoved(BPoint newLocation) 413 { 414 BControl::FrameMoved(newLocation); 415 } 416 417 418 void 419 BButton::FrameResized(float width, float height) 420 { 421 BControl::FrameResized(width, height); 422 } 423 424 425 void 426 BButton::MakeFocus(bool focused) 427 { 428 BControl::MakeFocus(focused); 429 } 430 431 432 void 433 BButton::AllAttached() 434 { 435 BControl::AllAttached(); 436 } 437 438 439 void 440 BButton::AllDetached() 441 { 442 BControl::AllDetached(); 443 } 444 445 446 BHandler * 447 BButton::ResolveSpecifier(BMessage *message, int32 index, 448 BMessage *specifier, int32 what, 449 const char *property) 450 { 451 return BControl::ResolveSpecifier(message, index, specifier, what, property); 452 } 453 454 455 status_t 456 BButton::GetSupportedSuites(BMessage *message) 457 { 458 return BControl::GetSupportedSuites(message); 459 } 460 461 462 status_t 463 BButton::Perform(perform_code d, void *arg) 464 { 465 return BControl::Perform(d, arg); 466 } 467 468 469 void BButton::_ReservedButton1() {} 470 void BButton::_ReservedButton2() {} 471 void BButton::_ReservedButton3() {} 472 473 474 BButton & 475 BButton::operator=(const BButton &) 476 { 477 return *this; 478 } 479 480 481 BRect 482 BButton::DrawDefault(BRect bounds, bool enabled) 483 { 484 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), 485 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), 486 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 487 488 rgb_color borderColor; 489 if (enabled) 490 borderColor = tint_color(no_tint, B_DARKEN_4_TINT); 491 else 492 borderColor = darken1; 493 494 // Dark border 495 BeginLineArray(4); 496 AddLine(BPoint(bounds.left, bounds.bottom - 1.0f), 497 BPoint(bounds.left, bounds.top + 1.0f), borderColor); 498 AddLine(BPoint(bounds.left + 1.0f, bounds.top), 499 BPoint(bounds.right - 1.0f, bounds.top), borderColor); 500 AddLine(BPoint(bounds.right, bounds.top + 1.0f), 501 BPoint(bounds.right, bounds.bottom - 1.0f), borderColor); 502 AddLine(BPoint(bounds.left + 1.0f, bounds.bottom), 503 BPoint(bounds.right - 1.0f, bounds.bottom), borderColor); 504 EndLineArray(); 505 506 if (enabled) { 507 // Bevel 508 bounds.InsetBy(1.0f, 1.0f); 509 SetHighColor(darken1); 510 StrokeRect(bounds); 511 } 512 513 bounds.InsetBy(1.0f, 1.0f); 514 515 // Filling 516 float inset = enabled? 2.0f : 3.0f; 517 SetHighColor(lighten1); 518 519 FillRect(BRect(bounds.left, bounds.top, 520 bounds.right, bounds.top+inset-1.0f)); 521 FillRect(BRect(bounds.left, bounds.bottom-inset+1.0f, 522 bounds.right, bounds.bottom)); 523 FillRect(BRect(bounds.left, bounds.top+inset-1.0f, 524 bounds.left+inset-1.0f, bounds.bottom-inset+1.0f)); 525 FillRect(BRect(bounds.right-inset+1.0f, bounds.top+inset-1.0f, 526 bounds.right, bounds.bottom-inset+1.0f)); 527 528 bounds.InsetBy(inset,inset); 529 530 return bounds; 531 } 532 533 534 status_t 535 BButton::Execute() 536 { 537 if (!IsEnabled()) 538 return B_ERROR; 539 540 SetValue(B_CONTROL_ON); 541 return Invoke(); 542 } 543 544 545 void 546 BButton::DrawFocusLine(float x, float y, float width, bool visible) 547 { 548 if (visible) 549 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 550 else 551 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 552 B_LIGHTEN_1_TINT)); 553 // Blue Line 554 StrokeLine(BPoint(x, y), BPoint(x + width, y)); 555 556 if (visible) 557 SetHighColor(255, 255, 255); 558 // White Line 559 StrokeLine(BPoint(x, y + 1.0f), BPoint(x + width, y + 1.0f)); 560 } 561