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 17namespace BPrivate { 18 19 20// #pragma mark - ViewPortLayout 21 22 23class BViewPort::ViewPortLayout : public BAbstractLayout { 24public: 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 145private: 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 207private: 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 220BViewPort::BViewPort(BView* child) 221 : 222 BView(NULL, 0), 223 fChild(NULL) 224{ 225 _Init(); 226 SetChildView(child); 227} 228 229 230BViewPort::BViewPort(BLayoutItem* child) 231 : 232 BView(NULL, 0), 233 fChild(NULL) 234{ 235 _Init(); 236 SetChildItem(child); 237} 238 239 240BViewPort::BViewPort(const char* name, BView* child) 241 : 242 BView(name, 0), 243 fChild(NULL) 244{ 245 _Init(); 246 SetChildView(child); 247} 248 249 250BViewPort::BViewPort(const char* name, BLayoutItem* child) 251 : 252 BView(name, 0), 253 fChild(NULL) 254{ 255 _Init(); 256 SetChildItem(child); 257} 258 259 260BViewPort::~BViewPort() 261{ 262} 263 264 265BView* 266BViewPort::ChildView() const 267{ 268 return fLayout->ChildView(); 269} 270 271 272void 273BViewPort::SetChildView(BView* child) 274{ 275 fLayout->SetChildView(child); 276 InvalidateLayout(); 277} 278 279 280BLayoutItem* 281BViewPort::ChildItem() const 282{ 283 return fLayout->ChildItem(); 284} 285 286 287void 288BViewPort::SetChildItem(BLayoutItem* child) 289{ 290 fLayout->SetChildItem(child); 291 InvalidateLayout(); 292} 293 294 295void 296BViewPort::_Init() 297{ 298 fLayout = new ViewPortLayout(this); 299 SetLayout(fLayout); 300} 301 302 303} // namespace BPrivate 304 305 306using ::BPrivate::BViewPort; 307