1 /* 2 * Copyright 2001-2009, Stephan Aßmus <superstippi@gmx.de> 3 * Copyright 2001-2009, Ingo Weinhold <ingo_weinhold@gmx.de> 4 * All rights reserved. Distributed under the terms of the MIT license. 5 */ 6 7 8 #include "SeparatorView.h" 9 10 #include <new> 11 12 #include <math.h> 13 #include <stdio.h> 14 15 #include <ControlLook.h> 16 #include <LayoutUtils.h> 17 #include <Region.h> 18 19 20 static const float kMinBorderLength = 5.0f; 21 22 23 // TODO: Actually implement alignment support! 24 // TODO: More testing, especially archiving. 25 26 27 BSeparatorView::BSeparatorView(orientation orientation, border_style border) 28 : 29 BView(NULL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 30 { 31 _Init(NULL, NULL, orientation, BAlignment(B_ALIGN_HORIZONTAL_CENTER, 32 B_ALIGN_VERTICAL_CENTER), border); 33 } 34 35 36 BSeparatorView::BSeparatorView(const char* name, const char* label, 37 orientation orientation, border_style border, const BAlignment& alignment) 38 : 39 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 40 { 41 _Init(label, NULL, orientation, alignment, border); 42 } 43 44 45 BSeparatorView::BSeparatorView(const char* name, BView* labelView, 46 orientation orientation, border_style border, const BAlignment& alignment) 47 : 48 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 49 { 50 _Init(NULL, labelView, orientation, alignment, border); 51 } 52 53 54 BSeparatorView::BSeparatorView(const char* label, 55 orientation orientation, border_style border, const BAlignment& alignment) 56 : 57 BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 58 { 59 _Init(label, NULL, orientation, alignment, border); 60 } 61 62 63 BSeparatorView::BSeparatorView(BView* labelView, 64 orientation orientation, border_style border, const BAlignment& alignment) 65 : 66 BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 67 { 68 _Init(NULL, labelView, orientation, alignment, border); 69 } 70 71 72 BSeparatorView::BSeparatorView(BMessage* archive) 73 : 74 BView(archive), 75 fLabel(), 76 fLabelView(NULL), 77 fOrientation(B_HORIZONTAL), 78 fAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, 79 B_ALIGN_VERTICAL_CENTER)), 80 fBorder(B_FANCY_BORDER) 81 { 82 // NULL archives will be caught by BView. 83 84 const char* label; 85 if (archive->FindString("_labelview", &label) == B_OK) { 86 fLabelView = FindView(label); 87 } else if (archive->FindString("_label", &label) == B_OK) { 88 fLabel.SetTo(label); 89 } 90 91 int32 orientation; 92 if (archive->FindInt32("_orientation", &orientation) == B_OK) 93 fOrientation = (enum orientation)orientation; 94 95 int32 hAlignment; 96 int32 vAlignment; 97 if (archive->FindInt32("_halignment", &hAlignment) == B_OK 98 && archive->FindInt32("_valignment", &vAlignment) == B_OK) { 99 fAlignment.horizontal = (alignment)hAlignment; 100 fAlignment.vertical = (vertical_alignment)vAlignment; 101 } 102 103 int32 borderStyle; 104 if (archive->FindInt32("_border", &borderStyle) != B_OK) 105 fBorder = (border_style)borderStyle; 106 } 107 108 109 BSeparatorView::~BSeparatorView() 110 { 111 } 112 113 114 // #pragma mark - Archiving 115 116 117 BArchivable* 118 BSeparatorView::Instantiate(BMessage* archive) 119 { 120 if (validate_instantiation(archive, "BSeparatorView")) 121 return new (std::nothrow) BSeparatorView(archive); 122 123 return NULL; 124 } 125 126 127 status_t 128 BSeparatorView::Archive(BMessage* into, bool deep) const 129 { 130 // TODO: Test this. 131 status_t result = BView::Archive(into, deep); 132 if (result != B_OK) 133 return result; 134 135 if (fLabelView != NULL) 136 result = into->AddString("_labelview", fLabelView->Name()); 137 else 138 result = into->AddString("_label", fLabel.String()); 139 140 if (result == B_OK) 141 result = into->AddInt32("_orientation", fOrientation); 142 143 if (result == B_OK) 144 result = into->AddInt32("_halignment", fAlignment.horizontal); 145 146 if (result == B_OK) 147 result = into->AddInt32("_valignment", fAlignment.vertical); 148 149 if (result == B_OK) 150 result = into->AddInt32("_border", fBorder); 151 152 return result; 153 } 154 155 156 // #pragma mark - 157 158 159 void 160 BSeparatorView::Draw(BRect updateRect) 161 { 162 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 163 if (BView* parent = Parent()) { 164 if (parent->ViewColor() != B_TRANSPARENT_COLOR) 165 base = parent->ViewColor(); 166 } 167 168 BRect labelBounds; 169 if (fLabelView != NULL) { 170 labelBounds = fLabelView->Frame(); 171 } else if (fLabel.CountChars() > 0) { 172 labelBounds = _MaxLabelBounds(); 173 float labelWidth = StringWidth(fLabel.String()); 174 if (fOrientation == B_HORIZONTAL) { 175 switch (fAlignment.horizontal) { 176 case B_ALIGN_LEFT: 177 default: 178 labelBounds.right = labelBounds.left + labelWidth; 179 break; 180 181 case B_ALIGN_RIGHT: 182 labelBounds.left = labelBounds.right - labelWidth; 183 break; 184 185 case B_ALIGN_CENTER: 186 labelBounds.left = (labelBounds.left + labelBounds.right 187 - labelWidth) / 2; 188 labelBounds.right = labelBounds.left + labelWidth; 189 break; 190 } 191 } else { 192 switch (fAlignment.vertical) { 193 case B_ALIGN_TOP: 194 default: 195 labelBounds.bottom = labelBounds.top + labelWidth; 196 break; 197 198 case B_ALIGN_BOTTOM: 199 labelBounds.top = labelBounds.bottom - labelWidth; 200 break; 201 202 case B_ALIGN_MIDDLE: 203 labelBounds.top = (labelBounds.top + labelBounds.bottom 204 - labelWidth) / 2; 205 labelBounds.bottom = labelBounds.top + labelWidth; 206 break; 207 } 208 } 209 } 210 211 BRect bounds = Bounds(); 212 BRegion region(bounds); 213 if (labelBounds.IsValid()) { 214 region.Exclude(labelBounds); 215 ConstrainClippingRegion(®ion); 216 } 217 218 float borderSize = _BorderSize(); 219 if (borderSize > 0.0f) { 220 if (fOrientation == B_HORIZONTAL) { 221 bounds.top = floorf((bounds.top + bounds.bottom + 1 - borderSize) 222 / 2); 223 bounds.bottom = bounds.top + borderSize - 1; 224 region.Exclude(bounds); 225 be_control_look->DrawBorder(this, bounds, updateRect, base, 226 fBorder, 0, BControlLook::B_TOP_BORDER); 227 } else { 228 bounds.left = floorf((bounds.left + bounds.right + 1 - borderSize) 229 / 2); 230 bounds.right = bounds.left + borderSize - 1; 231 region.Exclude(bounds); 232 be_control_look->DrawBorder(this, bounds, updateRect, base, 233 fBorder, 0, BControlLook::B_LEFT_BORDER); 234 } 235 if (labelBounds.IsValid()) 236 region.Include(labelBounds); 237 238 ConstrainClippingRegion(®ion); 239 } 240 241 SetLowColor(base); 242 FillRect(updateRect, B_SOLID_LOW); 243 244 if (fLabel.CountChars() > 0) { 245 font_height fontHeight; 246 GetFontHeight(&fontHeight); 247 248 SetHighColor(0, 0, 0, 255); 249 250 if (fOrientation == B_HORIZONTAL) { 251 DrawString(fLabel.String(), BPoint(labelBounds.left, 252 labelBounds.top + ceilf(fontHeight.ascent))); 253 } else { 254 DrawString(fLabel.String(), BPoint(labelBounds.left 255 + ceilf(fontHeight.ascent), labelBounds.bottom)); 256 } 257 } 258 } 259 260 261 void 262 BSeparatorView::GetPreferredSize(float* _width, float* _height) 263 { 264 float width = 0.0f; 265 float height = 0.0f; 266 267 if (fLabelView != NULL) { 268 fLabelView->GetPreferredSize(&width, &height); 269 } else if (fLabel.CountChars() > 0) { 270 width = StringWidth(fLabel.String()); 271 font_height fontHeight; 272 GetFontHeight(&fontHeight); 273 height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 274 if (fOrientation == B_VERTICAL) { 275 // swap width and height 276 float temp = height; 277 height = width; 278 width = temp; 279 } 280 } 281 282 float borderSize = _BorderSize(); 283 284 // Add some room for the border 285 if (fOrientation == B_HORIZONTAL) { 286 width += kMinBorderLength * 2.0f; 287 height = max_c(height, borderSize - 1.0f); 288 } else { 289 height += kMinBorderLength * 2.0f; 290 width = max_c(width, borderSize - 1.0f); 291 } 292 293 if (_width != NULL) 294 *_width = width; 295 296 if (_height != NULL) 297 *_height = height; 298 } 299 300 301 BSize 302 BSeparatorView::MinSize() 303 { 304 BSize size; 305 GetPreferredSize(&size.width, &size.height); 306 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 307 } 308 309 310 BSize 311 BSeparatorView::MaxSize() 312 { 313 BSize size(MinSize()); 314 if (fOrientation == B_HORIZONTAL) 315 size.width = B_SIZE_UNLIMITED; 316 else 317 size.height = B_SIZE_UNLIMITED; 318 319 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 320 } 321 322 323 BSize 324 BSeparatorView::PreferredSize() 325 { 326 BSize size; 327 GetPreferredSize(&size.width, &size.height); 328 329 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 330 } 331 332 333 // #pragma mark - 334 335 336 void 337 BSeparatorView::SetOrientation(orientation orientation) 338 { 339 if (orientation == fOrientation) 340 return; 341 342 fOrientation = orientation; 343 344 BFont font; 345 GetFont(&font); 346 if (fOrientation == B_VERTICAL) 347 font.SetRotation(90.0f); 348 349 SetFont(&font); 350 351 Invalidate(); 352 InvalidateLayout(); 353 } 354 355 356 void 357 BSeparatorView::SetAlignment(const BAlignment& aligment) 358 { 359 if (aligment == fAlignment) 360 return; 361 362 fAlignment = aligment; 363 364 Invalidate(); 365 InvalidateLayout(); 366 } 367 368 369 void 370 BSeparatorView::SetBorderStyle(border_style border) 371 { 372 if (border == fBorder) 373 return; 374 375 fBorder = border; 376 377 Invalidate(); 378 } 379 380 381 void 382 BSeparatorView::SetLabel(const char* label) 383 { 384 if (label == NULL) 385 label = ""; 386 387 if (fLabel == label) 388 return; 389 390 fLabel.SetTo(label); 391 392 Invalidate(); 393 InvalidateLayout(); 394 } 395 396 397 void 398 BSeparatorView::SetLabel(BView* view, bool deletePrevious) 399 { 400 if (fLabelView == view) 401 return; 402 403 if (fLabelView != NULL) { 404 fLabelView->RemoveSelf(); 405 if (deletePrevious) 406 delete fLabelView; 407 } 408 409 fLabelView = view; 410 411 if (fLabelView != NULL) 412 AddChild(view); 413 } 414 415 416 status_t 417 BSeparatorView::Perform(perform_code code, void* data) 418 { 419 return BView::Perform(code, data); 420 } 421 422 423 // #pragma mark - protected/private 424 425 426 void 427 BSeparatorView::DoLayout() 428 { 429 BView::DoLayout(); 430 431 if (fLabelView == NULL) 432 return; 433 434 BRect bounds = _MaxLabelBounds(); 435 BRect frame = BLayoutUtils::AlignInFrame(bounds, fLabelView->MaxSize(), 436 fAlignment); 437 438 fLabelView->MoveTo(frame.LeftTop()); 439 fLabelView->ResizeTo(frame.Width(), frame.Height()); 440 } 441 442 443 void 444 BSeparatorView::_Init(const char* label, BView* labelView, 445 orientation orientation, BAlignment alignment, border_style border) 446 { 447 fLabel = ""; 448 fLabelView = NULL; 449 450 fOrientation = B_HORIZONTAL; 451 fAlignment = alignment; 452 fBorder = border; 453 454 SetFont(be_bold_font); 455 SetViewColor(B_TRANSPARENT_32_BIT); 456 457 SetLabel(label); 458 SetLabel(labelView, true); 459 SetOrientation(orientation); 460 } 461 462 463 float 464 BSeparatorView::_BorderSize() const 465 { 466 // TODO: Get these values from the BControlLook class. 467 switch (fBorder) { 468 case B_PLAIN_BORDER: 469 return 1.0f; 470 471 case B_FANCY_BORDER: 472 return 2.0f; 473 474 case B_NO_BORDER: 475 default: 476 return 0.0f; 477 } 478 } 479 480 481 BRect 482 BSeparatorView::_MaxLabelBounds() const 483 { 484 BRect bounds = Bounds(); 485 if (fOrientation == B_HORIZONTAL) 486 bounds.InsetBy(kMinBorderLength, 0.0f); 487 else 488 bounds.InsetBy(0.0f, kMinBorderLength); 489 490 return bounds; 491 } 492 493 494 // #pragma mark - FBC padding 495 496 497 void BSeparatorView::_ReservedSeparatorView1() {} 498 void BSeparatorView::_ReservedSeparatorView2() {} 499 void BSeparatorView::_ReservedSeparatorView3() {} 500 void BSeparatorView::_ReservedSeparatorView4() {} 501 void BSeparatorView::_ReservedSeparatorView5() {} 502 void BSeparatorView::_ReservedSeparatorView6() {} 503 void BSeparatorView::_ReservedSeparatorView7() {} 504 void BSeparatorView::_ReservedSeparatorView8() {} 505 void BSeparatorView::_ReservedSeparatorView9() {} 506 void BSeparatorView::_ReservedSeparatorView10() {} 507