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