1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ViewPort.h> 8 9 #include <algorithm> 10 11 #include <AbstractLayout.h> 12 #include <ScrollBar.h> 13 14 #include "ViewLayoutItem.h" 15 16 17 namespace BPrivate { 18 19 20 // #pragma mark - ViewPortLayout 21 22 23 class BViewPort::ViewPortLayout : public BAbstractLayout { 24 public: 25 ViewPortLayout(BViewPort* viewPort) 26 : 27 BAbstractLayout(), 28 fViewPort(viewPort), 29 fHasViewChild(false), 30 fIsCacheValid(false), 31 fMin(), 32 fMax(), 33 fPreferred() 34 { 35 } 36 37 BView* ChildView() const 38 { 39 if (!fHasViewChild) 40 return NULL; 41 if (BViewLayoutItem* item = dynamic_cast<BViewLayoutItem*>(ItemAt(0))) 42 return item->View(); 43 return NULL; 44 } 45 46 void SetChildView(BView* view) 47 { 48 _UnsetChild(); 49 50 if (view != NULL && AddView(0, view) != NULL) 51 fHasViewChild = true; 52 } 53 54 BLayoutItem* ChildItem() const 55 { 56 return ItemAt(0); 57 } 58 59 void SetChildItem(BLayoutItem* item) 60 { 61 _UnsetChild(); 62 63 if (item != NULL) 64 AddItem(0, item); 65 } 66 67 virtual BSize BaseMinSize() 68 { 69 _ValidateMinMax(); 70 return fMin; 71 } 72 73 virtual BSize BaseMaxSize() 74 { 75 _ValidateMinMax(); 76 return fMax; 77 } 78 79 virtual BSize BasePreferredSize() 80 { 81 _ValidateMinMax(); 82 return fPreferred; 83 } 84 85 virtual BAlignment BaseAlignment() 86 { 87 return BAbstractLayout::BaseAlignment(); 88 } 89 90 virtual bool HasHeightForWidth() 91 { 92 _ValidateMinMax(); 93 return false; 94 // TODO: Support height-for-width! 95 } 96 97 virtual void GetHeightForWidth(float width, float* min, float* max, 98 float* preferred) 99 { 100 if (!HasHeightForWidth()) 101 return; 102 103 // TODO: Support height-for-width! 104 } 105 106 virtual void LayoutInvalidated(bool children) 107 { 108 fIsCacheValid = false; 109 } 110 111 virtual void DoLayout() 112 { 113 _ValidateMinMax(); 114 115 BLayoutItem* child = ItemAt(0); 116 if (child == NULL) 117 return; 118 119 // Determine the layout area: LayoutArea() will only give us the size 120 // of the view port's frame. 121 BSize viewSize = LayoutArea().Size(); 122 BSize layoutSize = viewSize; 123 124 BSize childMin = child->MinSize(); 125 BSize childMax = child->MaxSize(); 126 127 // apply the maximum constraints 128 layoutSize.width = std::min(layoutSize.width, childMax.width); 129 layoutSize.height = std::min(layoutSize.height, childMax.height); 130 131 // apply the minimum constraints 132 layoutSize.width = std::max(layoutSize.width, childMin.width); 133 layoutSize.height = std::max(layoutSize.height, childMin.height); 134 135 // TODO: Support height-for-width! 136 137 child->AlignInFrame(BRect(BPoint(0, 0), layoutSize)); 138 139 _UpdateScrollBar(fViewPort->ScrollBar(B_HORIZONTAL), viewSize.width, 140 layoutSize.width); 141 _UpdateScrollBar(fViewPort->ScrollBar(B_VERTICAL), viewSize.height, 142 layoutSize.height); 143 } 144 145 private: 146 void _UnsetChild() 147 { 148 if (CountItems() > 0) { 149 BLayoutItem* item = RemoveItem((int32)0); 150 if (fHasViewChild) 151 delete item; 152 fHasViewChild = false; 153 } 154 } 155 156 void _ValidateMinMax() 157 { 158 if (fIsCacheValid) 159 return; 160 161 if (BLayoutItem* child = ItemAt(0)) { 162 fMin = child->MinSize(); 163 if (_IsHorizontallyScrollable()) 164 fMin.width = -1; 165 if (_IsVerticallyScrollable()) 166 fMin.height = -1; 167 fMax = child->MaxSize(); 168 fPreferred = child->PreferredSize(); 169 // TODO: Support height-for-width! 170 } else { 171 fMin.Set(-1, -1); 172 fMax.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 173 fPreferred.Set(20, 20); 174 } 175 176 fIsCacheValid = true; 177 } 178 179 bool _IsHorizontallyScrollable() const 180 { 181 return fViewPort->ScrollBar(B_HORIZONTAL) != NULL; 182 } 183 184 bool _IsVerticallyScrollable() const 185 { 186 return fViewPort->ScrollBar(B_VERTICAL) != NULL; 187 } 188 189 void _UpdateScrollBar(BScrollBar* scrollBar, float viewPortSize, 190 float dataSize) 191 { 192 if (scrollBar == NULL) 193 return; 194 195 if (viewPortSize < dataSize) { 196 scrollBar->SetRange(0, dataSize - viewPortSize); 197 scrollBar->SetProportion(viewPortSize / dataSize); 198 float smallStep; 199 scrollBar->GetSteps(&smallStep, NULL); 200 scrollBar->SetSteps(smallStep, viewPortSize); 201 } else { 202 scrollBar->SetRange(0, 0); 203 scrollBar->SetProportion(1); 204 } 205 } 206 207 private: 208 BViewPort* fViewPort; 209 bool fHasViewChild; 210 bool fIsCacheValid; 211 BSize fMin; 212 BSize fMax; 213 BSize fPreferred; 214 }; 215 216 217 // #pragma mark - BViewPort 218 219 220 BViewPort::BViewPort(BView* child) 221 : 222 BView(NULL, 0), 223 fChild(NULL) 224 { 225 _Init(); 226 SetChildView(child); 227 } 228 229 230 BViewPort::BViewPort(BLayoutItem* child) 231 : 232 BView(NULL, 0), 233 fChild(NULL) 234 { 235 _Init(); 236 SetChildItem(child); 237 } 238 239 240 BViewPort::BViewPort(const char* name, BView* child) 241 : 242 BView(name, 0), 243 fChild(NULL) 244 { 245 _Init(); 246 SetChildView(child); 247 } 248 249 250 BViewPort::BViewPort(const char* name, BLayoutItem* child) 251 : 252 BView(name, 0), 253 fChild(NULL) 254 { 255 _Init(); 256 SetChildItem(child); 257 } 258 259 260 BViewPort::~BViewPort() 261 { 262 } 263 264 265 BView* 266 BViewPort::ChildView() const 267 { 268 return fLayout->ChildView(); 269 } 270 271 272 void 273 BViewPort::SetChildView(BView* child) 274 { 275 fLayout->SetChildView(child); 276 InvalidateLayout(); 277 } 278 279 280 BLayoutItem* 281 BViewPort::ChildItem() const 282 { 283 return fLayout->ChildItem(); 284 } 285 286 287 void 288 BViewPort::SetChildItem(BLayoutItem* child) 289 { 290 fLayout->SetChildItem(child); 291 InvalidateLayout(); 292 } 293 294 295 void 296 BViewPort::_Init() 297 { 298 fLayout = new ViewPortLayout(this); 299 SetLayout(fLayout); 300 } 301 302 303 } // namespace BPrivate 304 305 306 using ::BPrivate::BViewPort; 307