1 /* 2 * Copyright 2010-2012 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * John Scipione <jscipione@gmail.com> 8 */ 9 10 11 #include "FakeScrollBar.h" 12 13 #include <Box.h> 14 #include <ControlLook.h> 15 #include <Message.h> 16 #include <ScrollBar.h> 17 #include <Shape.h> 18 #include <Size.h> 19 #include <Window.h> 20 21 22 typedef enum { 23 ARROW_LEFT = 0, 24 ARROW_RIGHT, 25 ARROW_UP, 26 ARROW_DOWN, 27 ARROW_NONE 28 } arrow_direction; 29 30 31 FakeScrollBar::FakeScrollBar(bool drawArrows, bool doubleArrows, 32 int32 knobStyle, BMessage* message) 33 : 34 BControl("FakeScrollBar", NULL, message, B_WILL_DRAW | B_NAVIGABLE), 35 fDrawArrows(drawArrows), 36 fDoubleArrows(doubleArrows), 37 fKnobStyle(knobStyle) 38 { 39 SetExplicitMinSize(BSize(160, 20)); 40 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 20)); 41 } 42 43 44 FakeScrollBar::~FakeScrollBar(void) 45 { 46 } 47 48 49 void 50 FakeScrollBar::Draw(BRect updateRect) 51 { 52 BRect bounds = Bounds(); 53 54 rgb_color normal = ui_color(B_PANEL_BACKGROUND_COLOR); 55 56 if (IsFocus()) { 57 // draw the focus indicator 58 SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR)); 59 StrokeRect(bounds); 60 bounds.InsetBy(1.0, 1.0); 61 62 // Draw the selected border (1px) 63 if (Value() == B_CONTROL_ON) 64 SetHighColor(ui_color(B_CONTROL_MARK_COLOR)); 65 else 66 SetHighColor(normal); 67 68 StrokeRect(bounds); 69 bounds.InsetBy(1.0, 1.0); 70 } else { 71 // Draw the selected border (2px) 72 if (Value() == B_CONTROL_ON) 73 SetHighColor(ui_color(B_CONTROL_MARK_COLOR)); 74 else 75 SetHighColor(normal); 76 77 StrokeRect(bounds); 78 bounds.InsetBy(1.0, 1.0); 79 StrokeRect(bounds); 80 bounds.InsetBy(1.0, 1.0); 81 } 82 83 // draw a gap (1px) 84 SetHighColor(normal); 85 StrokeRect(bounds); 86 bounds.InsetBy(1.0, 1.0); 87 88 // draw a border around control (1px) 89 SetHighColor(tint_color(normal, B_DARKEN_1_TINT)); 90 StrokeRect(bounds); 91 bounds.InsetBy(1.0, 1.0); 92 93 BRect thumbBG = bounds; 94 BRect bgRect = bounds; 95 96 if (fDrawArrows) { 97 // draw arrows 98 SetDrawingMode(B_OP_OVER); 99 100 BRect buttonFrame(bounds.left, bounds.top, 101 bounds.left + bounds.Height(), bounds.bottom); 102 103 _DrawArrowButton(ARROW_LEFT, fDoubleArrows, buttonFrame, updateRect); 104 105 if (fDoubleArrows) { 106 buttonFrame.OffsetBy(bounds.Height() + 1, 0.0); 107 _DrawArrowButton(ARROW_RIGHT, fDoubleArrows, buttonFrame, 108 updateRect); 109 110 buttonFrame.OffsetTo(bounds.right - ((bounds.Height() * 2) + 1), 111 bounds.top); 112 _DrawArrowButton(ARROW_LEFT, fDoubleArrows, buttonFrame, 113 updateRect); 114 115 thumbBG.left += bounds.Height() * 2 + 2; 116 thumbBG.right -= bounds.Height() * 2 + 2; 117 } else { 118 thumbBG.left += bounds.Height() + 1; 119 thumbBG.right -= bounds.Height() + 1; 120 } 121 122 buttonFrame.OffsetTo(bounds.right - bounds.Height(), bounds.top); 123 _DrawArrowButton(ARROW_RIGHT, fDoubleArrows, buttonFrame, updateRect); 124 125 SetDrawingMode(B_OP_COPY); 126 127 bgRect = bounds.InsetByCopy(48, 0); 128 } else 129 bgRect = bounds.InsetByCopy(16, 0); 130 131 // fill background besides the thumb 132 BRect leftOfThumb(thumbBG.left, thumbBG.top, bgRect.left - 1, 133 thumbBG.bottom); 134 BRect rightOfThumb(bgRect.right + 1, thumbBG.top, thumbBG.right, 135 thumbBG.bottom); 136 137 be_control_look->DrawScrollBarBackground(this, leftOfThumb, 138 rightOfThumb, updateRect, normal, 0, B_HORIZONTAL); 139 140 // Draw scroll thumb 141 142 // fill the clickable surface of the thumb 143 be_control_look->DrawButtonBackground(this, bgRect, updateRect, 144 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 145 146 if (fKnobStyle == KNOB_STYLE_NONE) 147 return; 148 149 // draw the scrollbar thumb knobs 150 bool square = fKnobStyle == KNOB_STYLE_DOTS; 151 int32 hextent = 0; 152 int32 vextent = 0; 153 154 if (square) { 155 hextent = 2; 156 vextent = 2; 157 } else { 158 hextent = 1; 159 vextent = 3; 160 } 161 162 float hmiddle = bgRect.Width() / 2; 163 float vmiddle = bgRect.Height() / 2; 164 165 BRect middleKnob = BRect(bgRect.left + hmiddle - hextent, 166 bgRect.top + vmiddle - vextent, 167 bgRect.left + hmiddle + hextent, 168 bgRect.top + vmiddle + vextent); 169 170 BRect leftKnob = middleKnob.OffsetByCopy(hextent * -4, 0); 171 if (leftKnob.left > bgRect.left + hextent) { 172 be_control_look->DrawButtonBackground(this, leftKnob, updateRect, 173 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 174 } 175 176 BRect rightKnob = middleKnob.OffsetByCopy(hextent * 4, 0); 177 if (rightKnob.right < bgRect.right - hextent) { 178 be_control_look->DrawButtonBackground(this, rightKnob, updateRect, 179 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 180 } 181 182 // draw middle knob last because it modifies middleKnob 183 be_control_look->DrawButtonBackground(this, middleKnob, updateRect, 184 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 185 } 186 187 188 void 189 FakeScrollBar::MouseDown(BPoint point) 190 { 191 BControl::MouseDown(point); 192 } 193 194 195 void 196 FakeScrollBar::MouseMoved(BPoint point, uint32 transit, 197 const BMessage* message) 198 { 199 BControl::MouseMoved(point, transit, message); 200 } 201 202 203 void 204 FakeScrollBar::MouseUp(BPoint point) 205 { 206 SetValue(B_CONTROL_ON); 207 Invoke(); 208 209 Invalidate(); 210 211 BControl::MouseUp(point); 212 } 213 214 215 void 216 FakeScrollBar::SetValue(int32 value) 217 { 218 if (value != Value()) { 219 BControl::SetValueNoUpdate(value); 220 Invalidate(); 221 } 222 223 if (!value) 224 return; 225 226 BView* parent = Parent(); 227 BView* child = NULL; 228 229 if (parent != NULL) { 230 // If the parent is a BBox, the group parent is the parent of the BBox 231 BBox* box = dynamic_cast<BBox*>(parent); 232 233 if (box && box->LabelView() == this) 234 parent = box->Parent(); 235 236 if (parent != NULL) { 237 BBox* box = dynamic_cast<BBox*>(parent); 238 239 // If the parent is a BBox, skip the label if there is one 240 if (box && box->LabelView()) 241 child = parent->ChildAt(1); 242 else 243 child = parent->ChildAt(0); 244 } else 245 child = Window()->ChildAt(0); 246 } else if (Window()) 247 child = Window()->ChildAt(0); 248 249 while (child) { 250 FakeScrollBar* scrollbar = dynamic_cast<FakeScrollBar*>(child); 251 252 if (scrollbar != NULL && (scrollbar != this)) 253 scrollbar->SetValue(B_CONTROL_OFF); 254 else { 255 // If the child is a BBox, check if the label is a scrollbarbutton 256 BBox* box = dynamic_cast<BBox*>(child); 257 258 if (box && box->LabelView()) { 259 scrollbar = dynamic_cast<FakeScrollBar*>(box->LabelView()); 260 261 if (scrollbar != NULL && (scrollbar != this)) 262 scrollbar->SetValue(B_CONTROL_OFF); 263 } 264 } 265 266 child = child->NextSibling(); 267 } 268 269 //ASSERT(Value() == B_CONTROL_ON); 270 } 271 272 273 // #pragma mark - 274 275 276 void 277 FakeScrollBar::SetDoubleArrows(bool doublearrows) 278 { 279 fDoubleArrows = doublearrows; 280 Invalidate(); 281 } 282 283 284 void 285 FakeScrollBar::SetKnobStyle(uint32 style) 286 { 287 fKnobStyle = style; 288 Invalidate(); 289 } 290 291 292 void 293 FakeScrollBar::SetFromScrollBarInfo(const scroll_bar_info &info) 294 { 295 fDoubleArrows = info.double_arrows; 296 fKnobStyle = info.knob; 297 Invalidate(); 298 } 299 300 301 // #pragma mark - 302 303 304 void 305 FakeScrollBar::_DrawArrowButton(int32 direction, bool doubleArrows, BRect r, 306 const BRect& updateRect) 307 { 308 if (!updateRect.Intersects(r)) 309 return; 310 311 rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR); 312 rgb_color light = tint_color(c, B_LIGHTEN_MAX_TINT); 313 rgb_color dark = tint_color(c, B_DARKEN_1_TINT); 314 rgb_color darker = tint_color(c, B_DARKEN_2_TINT); 315 rgb_color normal = c; 316 rgb_color arrow = tint_color(c, 317 (B_DARKEN_MAX_TINT + B_DARKEN_4_TINT) / 2.0); 318 319 BPoint tri1, tri2, tri3; 320 float hInset = r.Width() / 3; 321 float vInset = r.Height() / 3; 322 r.InsetBy(hInset, vInset); 323 324 switch (direction) { 325 case ARROW_LEFT: 326 tri1.Set(r.right, r.top); 327 tri2.Set(r.right - r.Width() / 1.33, (r.top + r.bottom + 1) / 2); 328 tri3.Set(r.right, r.bottom + 1); 329 break; 330 331 case ARROW_RIGHT: 332 tri1.Set(r.left, r.bottom + 1); 333 tri2.Set(r.left + r.Width() / 1.33, (r.top + r.bottom + 1) / 2); 334 tri3.Set(r.left, r.top); 335 break; 336 337 case ARROW_UP: 338 tri1.Set(r.left, r.bottom); 339 tri2.Set((r.left + r.right + 1) / 2, r.bottom - r.Height() / 1.33); 340 tri3.Set(r.right + 1, r.bottom); 341 break; 342 343 default: 344 tri1.Set(r.left, r.top); 345 tri2.Set((r.left + r.right + 1) / 2, r.top + r.Height() / 1.33); 346 tri3.Set(r.right + 1, r.top); 347 break; 348 } 349 350 r.InsetBy(-(hInset - 1), -(vInset - 1)); 351 BRect temp(r.InsetByCopy(-1, -1)); 352 be_control_look->DrawButtonBackground(this, temp, updateRect, 353 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 354 355 BShape arrowShape; 356 arrowShape.MoveTo(tri1); 357 arrowShape.LineTo(tri2); 358 arrowShape.LineTo(tri3); 359 360 SetHighColor(arrow); 361 SetPenSize(ceilf(hInset / 2.0)); 362 StrokeShape(&arrowShape); 363 SetPenSize(1.0); 364 } 365