1 /************************************************************************ 2 * 3 * Copyright 2012 Jakob Leben (jakob.leben@gmail.com) 4 * 5 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 6 * All rights reserved. 7 * Contact: Nokia Corporation (qt-info@nokia.com) 8 * 9 * This file is part of SuperCollider Qt GUI. 10 * 11 * This program is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation, either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program. If not, see <http://www.gnu.org/licenses/>. 23 * 24 ************************************************************************/ 25 26 #include "stack_layout.hpp" 27 28 #include <QWidget> 29 30 namespace QtCollider { 31 32 StackLayout::StackLayout(): _index(-1), _mode(StackOne), _gotParent(false) {} 33 34 StackLayout::~StackLayout() { qDeleteAll(_list); } 35 36 int StackLayout::addWidget(QWidget* widget) { return insertWidget(_list.count(), widget); } 37 38 int StackLayout::insertWidget(int index, QWidget* widget) { 39 addChildWidget(widget); 40 index = qMin(index, _list.count()); 41 if (index < 0) 42 index = _list.count(); 43 QWidgetItem* wi = new QWidgetItem(widget); 44 _list.insert(index, wi); 45 invalidate(); 46 if (_index < 0) { 47 setCurrentIndex(index); 48 } else { 49 if (index <= _index) 50 ++_index; 51 if (_mode == StackOne) 52 widget->hide(); 53 widget->lower(); 54 } 55 return index; 56 } 57 58 QLayoutItem* StackLayout::itemAt(int index) const { return _list.value(index); } 59 60 QLayoutItem* StackLayout::takeAt(int index) { 61 if (index < 0 || index >= _list.size()) 62 return 0; 63 QLayoutItem* item = _list.takeAt(index); 64 if (index == _index) { 65 _index = -1; 66 if (_list.count() > 0) { 67 int newIndex = (index == _list.count()) ? index - 1 : index; 68 setCurrentIndex(newIndex); 69 } 70 } else if (index < _index) { 71 --_index; 72 } 73 // NOTE: Here, the Qt implementation hides item->widget() if it exists and is not being 74 // deleted. We can't reproduce this, because we can't access private info on whether the 75 // widget is being deleted. 76 return item; 77 } 78 79 void StackLayout::setCurrentIndex(int index) { 80 QWidget* prev = currentWidget(); 81 QWidget* next = widget(index); 82 if (!next || next == prev) 83 return; 84 85 _index = index; 86 87 if (!parent()) 88 return; 89 90 bool reenableUpdates = false; 91 QWidget* parent = parentWidget(); 92 93 if (parent && parent->updatesEnabled()) { 94 reenableUpdates = true; 95 parent->setUpdatesEnabled(false); 96 } 97 98 QWidget* fw = parent ? parent->window()->focusWidget() : 0; 99 if (prev) { 100 prev->clearFocus(); 101 if (_mode == StackOne) 102 prev->hide(); 103 } 104 105 next->raise(); 106 next->show(); 107 108 // try to move focus onto the incoming widget if focus 109 // was somewhere on the outgoing widget. 110 111 if (parent) { 112 if (fw && (prev && prev->isAncestorOf(fw))) { // focus was on old page 113 // look for the best focus widget we can find 114 if (QWidget* nfw = next->focusWidget()) 115 nfw->setFocus(); 116 else { 117 // second best: first child widget in the focus chain 118 QWidget* i = fw; 119 while ((i = i->nextInFocusChain()) != fw) { 120 if (((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && !i->focusProxy() && i->isVisibleTo(next) 121 && i->isEnabled() && next->isAncestorOf(i)) { 122 i->setFocus(); 123 break; 124 } 125 } 126 // third best: incoming widget 127 if (i == fw) 128 next->setFocus(); 129 } 130 } 131 } 132 if (reenableUpdates) 133 parent->setUpdatesEnabled(true); 134 135 if (_mode == StackOne) 136 // expandingDirections() might have changed, so invalidate(): 137 invalidate(); 138 } 139 140 int StackLayout::currentIndex() const { return _index; } 141 142 void StackLayout::setCurrentWidget(QWidget* widget) { 143 int index = indexOf(widget); 144 if (index == -1) { 145 qWarning("StackLayout::setCurrentWidget: Widget %p not contained in stack", widget); 146 return; 147 } 148 setCurrentIndex(index); 149 } 150 151 QWidget* StackLayout::currentWidget() const { return _index >= 0 ? _list.at(_index)->widget() : 0; } 152 153 QWidget* StackLayout::widget(int index) const { 154 if (index < 0 || index >= _list.size()) 155 return 0; 156 return _list.at(index)->widget(); 157 } 158 159 int StackLayout::count() const { return _list.size(); } 160 161 void StackLayout::addItem(QLayoutItem* item) { 162 QWidget* widget = item->widget(); 163 if (widget) { 164 addWidget(widget); 165 delete item; 166 } else { 167 qWarning("StackLayout::addItem: Only widgets can be added"); 168 } 169 } 170 171 QSize StackLayout::sizeHint() const { 172 QSize s(0, 0); 173 174 switch (_mode) { 175 case StackOne: 176 if (_index >= 0) 177 if (QWidget* w = _list.at(_index)->widget()) { 178 if (w->sizePolicy().horizontalPolicy() != QSizePolicy::Ignored) 179 s.setWidth(w->sizeHint().width()); 180 if (w->sizePolicy().verticalPolicy() != QSizePolicy::Ignored) 181 s.setHeight(w->sizeHint().height()); 182 } 183 break; 184 185 case StackAll: { 186 int n = _list.count(); 187 for (int i = 0; i < n; ++i) 188 if (QWidget* w = _list.at(i)->widget()) { 189 QSize ws(w->sizeHint()); 190 if (w->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored) 191 ws.setWidth(0); 192 if (w->sizePolicy().verticalPolicy() == QSizePolicy::Ignored) 193 ws.setHeight(0); 194 s = s.expandedTo(ws); 195 } 196 break; 197 } 198 } 199 200 return s; 201 } 202 203 static QSize smartMinSize(const QSize& sizeHint, const QSize& minSizeHint, const QSize& minSize, const QSize& maxSize, 204 const QSizePolicy& sizePolicy) { 205 QSize s(0, 0); 206 207 if (sizePolicy.horizontalPolicy() != QSizePolicy::Ignored) { 208 if (sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag) 209 s.setWidth(minSizeHint.width()); 210 else 211 s.setWidth(qMax(sizeHint.width(), minSizeHint.width())); 212 } 213 214 if (sizePolicy.verticalPolicy() != QSizePolicy::Ignored) { 215 if (sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag) { 216 s.setHeight(minSizeHint.height()); 217 } else { 218 s.setHeight(qMax(sizeHint.height(), minSizeHint.height())); 219 } 220 } 221 222 s = s.boundedTo(maxSize); 223 if (minSize.width() > 0) 224 s.setWidth(minSize.width()); 225 if (minSize.height() > 0) 226 s.setHeight(minSize.height()); 227 228 return s.expandedTo(QSize(0, 0)); 229 } 230 231 QSize StackLayout::minimumSize() const { 232 QSize s(0, 0); 233 234 switch (_mode) { 235 case StackOne: 236 if (_index >= 0) 237 if (QWidget* w = _list.at(_index)->widget()) 238 s = smartMinSize(w->sizeHint(), w->minimumSizeHint(), w->minimumSize(), w->maximumSize(), 239 w->sizePolicy()); 240 break; 241 242 case StackAll: { 243 int n = _list.count(); 244 for (int i = 0; i < n; ++i) 245 if (QWidget* w = _list.at(i)->widget()) 246 s = s.expandedTo(smartMinSize(w->sizeHint(), w->minimumSizeHint(), w->minimumSize(), w->maximumSize(), 247 w->sizePolicy())); 248 break; 249 } 250 } 251 252 return s; 253 } 254 255 Qt::Orientations StackLayout::expandingDirections() const { 256 Qt::Orientations directions; 257 258 switch (_mode) { 259 case StackOne: 260 directions = _index >= 0 ? _list.at(_index)->expandingDirections() : (Qt::Orientations)0; 261 break; 262 263 case StackAll: { 264 Qt::Orientations directions = 0; 265 int n = _list.count(); 266 for (int i = 0; i < n; ++i) 267 directions |= _list.at(i)->expandingDirections(); 268 break; 269 } 270 } 271 272 return directions; 273 } 274 275 void StackLayout::setGeometry(const QRect& rect) { 276 switch (_mode) { 277 case StackOne: 278 if (QWidget* widget = currentWidget()) 279 widget->setGeometry(rect); 280 break; 281 case StackAll: 282 if (const int n = _list.count()) 283 for (int i = 0; i < n; ++i) 284 if (QWidget* widget = _list.at(i)->widget()) 285 widget->setGeometry(rect); 286 break; 287 } 288 } 289 290 StackLayout::StackingMode StackLayout::stackingMode() const { return _mode; } 291 292 void StackLayout::setStackingMode(StackingMode stackingMode) { 293 if (_mode == stackingMode) 294 return; 295 _mode = stackingMode; 296 297 if (!parent()) 298 return; 299 300 const int n = _list.count(); 301 if (n == 0) 302 return; 303 304 switch (_mode) { 305 case StackOne: { 306 const int idx = currentIndex(); 307 if (idx >= 0) 308 for (int i = 0; i < n; ++i) 309 if (QWidget* widget = _list.at(i)->widget()) 310 widget->setVisible(i == idx); 311 break; 312 } 313 case StackAll: { // Turn overlay on: Make sure all widgets are the same size 314 QRect geometry; 315 if (const QWidget* widget = currentWidget()) 316 geometry = widget->geometry(); 317 for (int i = 0; i < n; ++i) 318 if (QWidget* widget = _list.at(i)->widget()) { 319 if (!geometry.isNull()) 320 widget->setGeometry(geometry); 321 widget->setVisible(true); 322 } 323 } break; 324 } 325 326 invalidate(); 327 } 328 329 void StackLayout::invalidate() { 330 QWidget* pw = parentWidget(); 331 332 if (pw && !_gotParent) { 333 _gotParent = true; 334 335 const int n = _list.count(); 336 if (n == 0) 337 return; 338 339 QWidget* cw = currentWidget(); 340 341 switch (_mode) { 342 case StackOne: { 343 if (cw) 344 for (int i = 0; i < n; ++i) 345 if (QWidget* widget = _list.at(i)->widget()) 346 widget->setVisible(widget == cw); 347 break; 348 } 349 case StackAll: { 350 for (int i = 0; i < n; ++i) 351 if (QWidget* widget = _list.at(i)->widget()) 352 widget->setVisible(true); 353 break; 354 } 355 } 356 357 // NOTE: re-order the widgets after setting visibility, since the latter 358 // may affect the order itself (at least on Mac OS X) 359 if (cw) { 360 for (int i = 0; i < n; ++i) { 361 QWidget* w = _list.at(i)->widget(); 362 if (w && w != cw) 363 w->lower(); 364 } 365 } 366 } 367 368 QLayout::invalidate(); 369 } 370 371 } // namespace QtCollider 372