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