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 == B_KNOB_STYLE_NONE) 147 return; 148 149 // draw the scrollbar thumb knobs 150 bool square = fKnobStyle == B_KNOB_STYLE_DOTS; 151 int32 knobWidth = 0; 152 int32 knobHeight = 0; 153 154 if (square) { 155 knobWidth = 2; 156 knobHeight = 2; 157 } else { 158 knobWidth = 1; 159 knobHeight = 3; 160 } 161 162 float hmiddle = bgRect.Width() / 2; 163 float vmiddle = bgRect.Height() / 2; 164 165 BRect middleKnob = BRect(bgRect.left + hmiddle - knobWidth, 166 bgRect.top + vmiddle - knobHeight, 167 bgRect.left + hmiddle + knobWidth, 168 bgRect.top + vmiddle + knobHeight); 169 170 BRect leftKnob = middleKnob.OffsetByCopy(knobWidth * -4, 0); 171 be_control_look->DrawButtonBackground(this, leftKnob, updateRect, 172 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 173 174 BRect rightKnob = middleKnob.OffsetByCopy(knobWidth * 4, 0); 175 be_control_look->DrawButtonBackground(this, rightKnob, updateRect, 176 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 177 178 // draw middle knob last because it modifies middleKnob 179 be_control_look->DrawButtonBackground(this, middleKnob, updateRect, 180 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 181 } 182 183 184 void 185 FakeScrollBar::MouseDown(BPoint point) 186 { 187 BControl::MouseDown(point); 188 } 189 190 191 void 192 FakeScrollBar::MouseMoved(BPoint point, uint32 transit, 193 const BMessage* message) 194 { 195 BControl::MouseMoved(point, transit, message); 196 } 197 198 199 void 200 FakeScrollBar::MouseUp(BPoint point) 201 { 202 SetValue(B_CONTROL_ON); 203 Invoke(); 204 205 Invalidate(); 206 207 BControl::MouseUp(point); 208 } 209 210 211 void 212 FakeScrollBar::SetValue(int32 value) 213 { 214 if (value != Value()) { 215 BControl::SetValueNoUpdate(value); 216 Invalidate(); 217 } 218 219 if (!value) 220 return; 221 222 BView* parent = Parent(); 223 BView* child = NULL; 224 225 if (parent != NULL) { 226 // If the parent is a BBox, the group parent is the parent of the BBox 227 BBox* box = dynamic_cast<BBox*>(parent); 228 229 if (box && box->LabelView() == this) 230 parent = box->Parent(); 231 232 if (parent != NULL) { 233 BBox* box = dynamic_cast<BBox*>(parent); 234 235 // If the parent is a BBox, skip the label if there is one 236 if (box && box->LabelView()) 237 child = parent->ChildAt(1); 238 else 239 child = parent->ChildAt(0); 240 } else 241 child = Window()->ChildAt(0); 242 } else if (Window()) 243 child = Window()->ChildAt(0); 244 245 while (child) { 246 FakeScrollBar* scrollbar = dynamic_cast<FakeScrollBar*>(child); 247 248 if (scrollbar != NULL && (scrollbar != this)) 249 scrollbar->SetValue(B_CONTROL_OFF); 250 else { 251 // If the child is a BBox, check if the label is a scrollbarbutton 252 BBox* box = dynamic_cast<BBox*>(child); 253 254 if (box && box->LabelView()) { 255 scrollbar = dynamic_cast<FakeScrollBar*>(box->LabelView()); 256 257 if (scrollbar != NULL && (scrollbar != this)) 258 scrollbar->SetValue(B_CONTROL_OFF); 259 } 260 } 261 262 child = child->NextSibling(); 263 } 264 265 //ASSERT(Value() == B_CONTROL_ON); 266 } 267 268 269 // #pragma mark - 270 271 272 void 273 FakeScrollBar::SetDoubleArrows(bool doubleArrows) 274 { 275 fDoubleArrows = doubleArrows; 276 Invalidate(); 277 } 278 279 280 void 281 FakeScrollBar::SetKnobStyle(uint32 knobStyle) 282 { 283 fKnobStyle = knobStyle; 284 Invalidate(); 285 } 286 287 288 void 289 FakeScrollBar::SetFromScrollBarInfo(const scroll_bar_info &info) 290 { 291 fDoubleArrows = info.double_arrows; 292 fKnobStyle = info.knob; 293 Invalidate(); 294 } 295 296 297 // #pragma mark - 298 299 300 void 301 FakeScrollBar::_DrawArrowButton(int32 direction, bool doubleArrows, BRect r, 302 const BRect& updateRect) 303 { 304 if (!updateRect.Intersects(r)) 305 return; 306 307 rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR); 308 rgb_color light = tint_color(c, B_LIGHTEN_MAX_TINT); 309 rgb_color dark = tint_color(c, B_DARKEN_1_TINT); 310 rgb_color darker = tint_color(c, B_DARKEN_2_TINT); 311 rgb_color normal = c; 312 rgb_color arrow = tint_color(c, 313 (B_DARKEN_MAX_TINT + B_DARKEN_4_TINT) / 2.0); 314 315 BPoint tri1, tri2, tri3; 316 float hInset = r.Width() / 3; 317 float vInset = r.Height() / 3; 318 r.InsetBy(hInset, vInset); 319 320 switch (direction) { 321 case ARROW_LEFT: 322 tri1.Set(r.right, r.top); 323 tri2.Set(r.right - r.Width() / 1.33, (r.top + r.bottom + 1) / 2); 324 tri3.Set(r.right, r.bottom + 1); 325 break; 326 327 case ARROW_RIGHT: 328 tri1.Set(r.left, r.bottom + 1); 329 tri2.Set(r.left + r.Width() / 1.33, (r.top + r.bottom + 1) / 2); 330 tri3.Set(r.left, r.top); 331 break; 332 333 case ARROW_UP: 334 tri1.Set(r.left, r.bottom); 335 tri2.Set((r.left + r.right + 1) / 2, r.bottom - r.Height() / 1.33); 336 tri3.Set(r.right + 1, r.bottom); 337 break; 338 339 default: 340 tri1.Set(r.left, r.top); 341 tri2.Set((r.left + r.right + 1) / 2, r.top + r.Height() / 1.33); 342 tri3.Set(r.right + 1, r.top); 343 break; 344 } 345 346 r.InsetBy(-(hInset - 1), -(vInset - 1)); 347 BRect temp(r.InsetByCopy(-1, -1)); 348 be_control_look->DrawButtonBackground(this, temp, updateRect, 349 normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL); 350 351 BShape arrowShape; 352 arrowShape.MoveTo(tri1); 353 arrowShape.LineTo(tri2); 354 arrowShape.LineTo(tri3); 355 356 SetHighColor(arrow); 357 SetPenSize(ceilf(hInset / 2.0)); 358 StrokeShape(&arrowShape); 359 SetPenSize(1.0); 360 } 361