DockableWidgetLayout.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. #include "DockableWidgetLayout.h"
  2. #include "DockableWidget.h"
  3. #include <QLayoutItem>
  4. #include <QtGlobal>
  5. #include <QSet>
  6. static const int SNAP_DISTANCE = 16;
  7. DockableWidgetLayout::DockableWidgetLayout(QWidget* parent, int margin, int spacing)
  8. : QLayout(parent)
  9. {
  10. setMargin(margin);
  11. setSpacing(spacing);
  12. minWidth = minHeight = 0;
  13. maxWidth = maxHeight = 0;
  14. }
  15. DockableWidgetLayout::DockableWidgetLayout(int spacing)
  16. {
  17. setSpacing(spacing);
  18. minWidth = minHeight = 0;
  19. maxWidth = maxHeight = 0;
  20. }
  21. DockableWidgetLayout::~DockableWidgetLayout()
  22. {
  23. while (!dockedWidgets.empty()) {
  24. DockInfo* info = dockedWidgets.takeFirst();
  25. delete info->item;
  26. delete info->widget;
  27. delete info;
  28. }
  29. }
  30. void DockableWidgetLayout::addItem(QLayoutItem* item)
  31. {
  32. addItem(item, -1);
  33. }
  34. void DockableWidgetLayout::addItem(
  35. QLayoutItem* item, int index, DockSide side, int dist, int w, int h)
  36. {
  37. DockInfo* info = new DockInfo();
  38. info->item = item;
  39. info->widget = qobject_cast<DockableWidget*>(item->widget());
  40. info->dockSide = side;
  41. info->dockDistance = dist;
  42. info->left = 0;
  43. info->top = 0;
  44. info->width = -1;
  45. info->height = -1;
  46. info->useHintWidth = true;
  47. info->useHintHeight = true;
  48. if (info->widget->sizePolicy().horizontalPolicy() != QSizePolicy::Fixed &&
  49. w > 0) {
  50. info->width = w;
  51. info->useHintWidth = false;
  52. }
  53. if (info->widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed &&
  54. h > 0 ) {
  55. info->height = h;
  56. info->useHintHeight = false;
  57. }
  58. // first widget is the resize widget, set initial size
  59. if (dockedWidgets.empty()) {
  60. if (info->width == -1) {
  61. info->width = item->sizeHint().width();
  62. }
  63. if (info->height == -1) {
  64. info->height = item->sizeHint().height();
  65. }
  66. info->useHintWidth = false;
  67. info->useHintHeight = false;
  68. }
  69. for (int i = 0; i < dockedWidgets.size(); ++i) {
  70. Q_ASSERT(dockedWidgets.at(i)->widget != item->widget());
  71. }
  72. if (index > -1 && index < dockedWidgets.size()) {
  73. dockedWidgets.insert(index, info);
  74. } else {
  75. dockedWidgets.append(info);
  76. }
  77. // recalculate size limits
  78. calcSizeLimits();
  79. }
  80. void DockableWidgetLayout::addWidget(DockableWidget* widget, const QRect& rect)
  81. {
  82. int index;
  83. DockSide side;
  84. QRect r(rect);
  85. if (insertLocation(r, index, side, widget->sizePolicy())) {
  86. DockInfo* d = dockedWidgets.first();
  87. int dist;
  88. switch (side) {
  89. case TOP:
  90. case BOTTOM:
  91. dist = r.left() - d->left;
  92. break;
  93. case LEFT:
  94. case RIGHT:
  95. dist = r.top() - d->top;
  96. break;
  97. }
  98. widget->show();
  99. addItem(new QWidgetItem(widget), index, side, dist,
  100. r.width(), r.height());
  101. update();
  102. }
  103. }
  104. void DockableWidgetLayout::addWidget(
  105. DockableWidget* widget, DockSide side, int dist, int w, int h)
  106. {
  107. // append item at the back
  108. addItem(new QWidgetItem(widget), -1, side, dist, w, h);
  109. }
  110. void DockableWidgetLayout::changed()
  111. {
  112. calcSizeLimits();
  113. update();
  114. }
  115. Qt::Orientations DockableWidgetLayout::expandingDirections() const
  116. {
  117. return Qt::Horizontal | Qt::Vertical;
  118. }
  119. bool DockableWidgetLayout::hasHeightForWidth() const
  120. {
  121. return false;
  122. }
  123. int DockableWidgetLayout::count() const
  124. {
  125. return dockedWidgets.size();
  126. }
  127. QLayoutItem* DockableWidgetLayout::takeAt(int index)
  128. {
  129. if (index < 0 || index >= dockedWidgets.size()) return 0;
  130. DockInfo* info = dockedWidgets.takeAt(index);
  131. QLayoutItem* item = info->item;
  132. delete info;
  133. calcSizeLimits();
  134. return item;
  135. }
  136. QLayoutItem* DockableWidgetLayout::itemAt(int index) const
  137. {
  138. if (index < 0 || index >= dockedWidgets.size()) return 0;
  139. return dockedWidgets.at(index)->item;
  140. }
  141. QSize DockableWidgetLayout::minimumSize() const
  142. {
  143. return QSize(minWidth, minHeight);
  144. }
  145. QSize DockableWidgetLayout::maximumSize() const
  146. {
  147. return QSize(maxWidth, maxHeight);
  148. }
  149. QSize DockableWidgetLayout::sizeHint() const
  150. {
  151. return QSize(layoutWidth, layoutHeight);
  152. }
  153. void DockableWidgetLayout::setGeometry(const QRect& rect)
  154. {
  155. QLayout::setGeometry(rect);
  156. // Qt sometimes sets the geometry outside the minimumSize/maximumSize range. :/
  157. int W = std::min(maxWidth, std::max(minWidth, rect.width ()));
  158. int H = std::min(maxHeight, std::max(minHeight, rect.height()));
  159. // set main widget size
  160. int dx = W - layoutWidth;
  161. int dy = H - layoutHeight;
  162. if (dx != 0 || dy != 0) {
  163. sizeMove(dx, dy);
  164. calcSizeLimits();
  165. }
  166. // resize the widgets
  167. for (int i = 0; i < dockedWidgets.size(); ++i) {
  168. DockInfo* d = dockedWidgets[i];
  169. if (!d->widget->isHidden()) {
  170. d->item->setGeometry(d->bounds());
  171. }
  172. }
  173. }
  174. void DockableWidgetLayout::calcSizeLimits()
  175. {
  176. if (dockedWidgets.empty()) return;
  177. // layout with current sizes
  178. doLayout();
  179. DockInfo* d = dockedWidgets.first();
  180. // store current size
  181. int curWidth = d->width;
  182. int curHeight = d->height;
  183. QVector<int> distStore;
  184. for (int i = 0; i < dockedWidgets.size(); ++i) {
  185. distStore.push_back(dockedWidgets.at(i)->dockDistance);
  186. }
  187. // first check minimum width (blunt method)
  188. for (int i = d->widget->minimumWidth(); i <= curWidth; ++i) {
  189. // trial layout
  190. sizeMove(i - d->width, 0);
  191. doLayout(true);
  192. // restore
  193. d->width = curWidth;
  194. for (int j = 1; j < dockedWidgets.size(); ++j) {
  195. dockedWidgets.at(j)->dockDistance = distStore.at(j);
  196. }
  197. // check result
  198. if (layoutHeight == checkHeight &&
  199. layoutWidth - checkWidth == d->width - i) {
  200. break;
  201. }
  202. }
  203. minWidth = checkWidth;
  204. // first check maximum width (blunt method)
  205. for (int i = d->widget->maximumWidth(); i >= curWidth; --i) {
  206. // trial layout
  207. sizeMove(i - d->width, 0);
  208. doLayout(true);
  209. // restore
  210. d->width = curWidth;
  211. for (int j = 1; j < dockedWidgets.size(); ++j) {
  212. dockedWidgets.at(j)->dockDistance = distStore.at(j);
  213. }
  214. // check result
  215. if (layoutHeight == checkHeight &&
  216. layoutWidth - checkWidth == d->width - i) {
  217. break;
  218. }
  219. }
  220. maxWidth = checkWidth;
  221. // first check minimum height (blunt method)
  222. for (int i = d->widget->minimumHeight(); i <= curHeight; ++i) {
  223. // trial layout
  224. sizeMove(0, i - d->height);
  225. doLayout(true);
  226. // restore
  227. d->height = curHeight;
  228. for (int j = 1; j < dockedWidgets.size(); ++j) {
  229. dockedWidgets.at(j)->dockDistance = distStore.at(j);
  230. }
  231. // check result
  232. if (layoutWidth == checkWidth &&
  233. layoutHeight - checkHeight == d->height - i) {
  234. break;
  235. }
  236. }
  237. minHeight = checkHeight;
  238. // first check maximum width (blunt method)
  239. for (int i = d->widget->maximumHeight(); i >= curHeight; --i) {
  240. // trial layout
  241. sizeMove(0, i - d->height);
  242. doLayout(true);
  243. // restore
  244. d->height = curHeight;
  245. for (int j = 1; j < dockedWidgets.size(); ++j) {
  246. dockedWidgets.at(j)->dockDistance = distStore.at(j);
  247. }
  248. // check result
  249. if (layoutWidth == checkWidth &&
  250. layoutHeight - checkHeight == d->height - i) {
  251. break;
  252. }
  253. }
  254. maxHeight = checkHeight;
  255. // restore layout
  256. doLayout();
  257. }
  258. void DockableWidgetLayout::sizeMove(int dx, int dy)
  259. {
  260. DockInfo* d0 = dockedWidgets.first();
  261. for (int i = 1; i < dockedWidgets.size(); ++i) {
  262. DockInfo* d = dockedWidgets.at(i);
  263. if (d->dockSide == TOP || d->dockSide == BOTTOM) {
  264. if (d->dockDistance >= d0->width) {
  265. d->dockDistance += dx;
  266. }
  267. }
  268. if (d->dockSide == LEFT || d->dockSide == RIGHT) {
  269. if (d->dockDistance >= d0->height) {
  270. d->dockDistance += dy;
  271. }
  272. }
  273. }
  274. d0->width += dx;
  275. d0->height += dy;
  276. }
  277. void DockableWidgetLayout::doLayout(bool check)
  278. {
  279. if (dockedWidgets.empty()) return;
  280. DockInfo* d = dockedWidgets.first();
  281. d->left = 0;
  282. d->top = 0;
  283. int W = d->width;
  284. int H = d->height;
  285. int dx = 0, dy = 0;
  286. for (int i = 1; i < dockedWidgets.size(); ++i) {
  287. d = dockedWidgets[i];
  288. // only process visible widgets
  289. if (d->widget->isHidden()) {
  290. d->left = -10000;
  291. d->top = -10000;
  292. continue;
  293. }
  294. // determine size
  295. if (d->useHintWidth) {
  296. d->width = d->item->sizeHint().width();
  297. }
  298. if (d->useHintHeight) {
  299. d->height = d->item->sizeHint().height();
  300. }
  301. // determine location
  302. switch (d->dockSide) {
  303. case TOP:
  304. d->left = d->dockDistance;
  305. if (d->dockDistance >= W || d->dockDistance+d->width <= 0) {
  306. d->top = H - d->height;
  307. } else {
  308. d->top = -d->height;
  309. }
  310. // adjust position until it doesn't overlap other widgets
  311. for (int j = 1; j < i; ++j) {
  312. DockInfo* d2 = dockedWidgets[j];
  313. QRect r(d->left, d->top - QWIDGETSIZE_MAX,
  314. d->width, d->height + QWIDGETSIZE_MAX);
  315. if (r.intersects(d2->bounds())) {
  316. d->top = d2->top - d->height;
  317. }
  318. }
  319. break;
  320. case LEFT:
  321. d->top = d->dockDistance;
  322. if (d->dockDistance >= H || d->dockDistance+d->height <= 0) {
  323. d->left = W - d->width;
  324. } else {
  325. d->left = -d->width;
  326. }
  327. // adjust position until it doesn't overlap other widgets
  328. for (int j = 1; j < i; ++j) {
  329. DockInfo* d2 = dockedWidgets[j];
  330. QRect r(d->left - QWIDGETSIZE_MAX, d->top,
  331. d->width + QWIDGETSIZE_MAX, d->height);
  332. if (r.intersects(d2->bounds())) {
  333. d->left = d2->left - d->width;
  334. }
  335. }
  336. break;
  337. case RIGHT:
  338. d->top = d->dockDistance;
  339. if (d->dockDistance >= H || d->dockDistance+d->height <= 0) {
  340. d->left = 0;
  341. } else {
  342. d->left = W;
  343. }
  344. // adjust position until it doesn't overlap other widgets
  345. for (int j = 1; j < i; ++j) {
  346. DockInfo* d2 = dockedWidgets[j];
  347. QRect r(d->left, d->top,
  348. d->width + QWIDGETSIZE_MAX, d->height);
  349. if (r.intersects(d2->bounds())) {
  350. d->left = d2->left + d2->width;
  351. }
  352. }
  353. break;
  354. case BOTTOM:
  355. d->left = d->dockDistance;
  356. if (d->dockDistance >= W || d->dockDistance+d->width <= 0) {
  357. d->top = 0;
  358. } else {
  359. d->top = H;
  360. }
  361. // adjust position until it doesn't overlap other widgets
  362. for (int j = 1; j < i; ++j) {
  363. DockInfo* d2 = dockedWidgets[j];
  364. QRect r(d->left, d->top,
  365. d->width, d->height + QWIDGETSIZE_MAX);
  366. if (r.intersects(d2->bounds())) {
  367. d->top = d2->top + d2->height;
  368. }
  369. }
  370. break;
  371. }
  372. // check negative coordinates
  373. if (d->left < dx) dx = d->left;
  374. if (d->top < dy) dy = d->top;
  375. }
  376. // translate widgets and calculate size
  377. int& w = check ? checkWidth : layoutWidth;
  378. int& h = check ? checkHeight : layoutHeight;
  379. w = h = 0;
  380. for (int i = 0; i < dockedWidgets.size(); ++i) {
  381. DockInfo* d = dockedWidgets[i];
  382. if (!d->widget->isHidden()) {
  383. d->left -= dx;
  384. d->top -= dy;
  385. w = std::max(w, d->right());
  386. h = std::max(h, d->bottom());
  387. }
  388. }
  389. }
  390. bool DockableWidgetLayout::overlaysWithFirstNWidgets(const QRect& r, int n) const
  391. {
  392. for (int i = 0; i < n; ++i) {
  393. if (r.intersects(dockedWidgets[i]->bounds())) {
  394. return true;
  395. }
  396. }
  397. return false;
  398. }
  399. static bool isClose(int a, int b)
  400. {
  401. return abs(a - b) < SNAP_DISTANCE;
  402. }
  403. bool DockableWidgetLayout::insertLocation(
  404. QRect& rect, int& index, DockSide& side, const QSizePolicy& sizePol)
  405. {
  406. // best insertion data
  407. // Distance is a number that represents the how far
  408. // the insertion rectangle is from the final location.
  409. unsigned bestDistance = 0xFFFFFFFF;
  410. int bestIndex = 0;
  411. DockSide bestSide;
  412. QRect bestRect;
  413. // loop over all widgets and find appropriate matching sides
  414. for (int i = 0; i < dockedWidgets.size(); ++i) {
  415. DockInfo* d = dockedWidgets[i];
  416. /*****************************************************
  417. * Check for placement against the top of the widget *
  418. *****************************************************/
  419. if (i == 0 || d->dockSide != BOTTOM) {
  420. if (!(rect.left() > d->right() - SNAP_DISTANCE ||
  421. rect.right() < d->left + SNAP_DISTANCE) &&
  422. isClose(rect.bottom(), d->top)) {
  423. // rectangle is close to the edge
  424. unsigned dist = 8 * abs(rect.bottom() - d->top);
  425. // now find all points on this side
  426. // (use set as a sorted unique list)
  427. QSet<int> sidePoints;
  428. for (int j = 0; j <= i; ++j) {
  429. DockInfo* d2 = dockedWidgets[j];
  430. if (d->top == d2->top) {
  431. sidePoints.insert(d2->left);
  432. sidePoints.insert(d2->right());
  433. // check if any other widget rest against this side
  434. for (int k = i + 1; k < dockedWidgets.size(); ++k) {
  435. DockInfo* d3 = dockedWidgets[k];
  436. if (d3->bottom() == d2->top) {
  437. sidePoints.insert(d3->left);
  438. sidePoints.insert(d3->right());
  439. }
  440. }
  441. }
  442. }
  443. // widget placement can occur at all points, find the closest
  444. QSet<int>::iterator it = sidePoints.begin();
  445. for (int j = 0; j < sidePoints.size() - 1; ++j) {
  446. // check after point
  447. unsigned newDist1 = dist + abs(*it - rect.left());
  448. if (newDist1 < bestDistance && isClose(*it, rect.left())) {
  449. QRect r(QPoint(*it, d->top - rect.height()), rect.size());
  450. if (!overlaysWithFirstNWidgets(r, i)) {
  451. bestDistance = newDist1;
  452. bestIndex = i + 1;
  453. bestSide = TOP;
  454. bestRect = r;
  455. }
  456. }
  457. ++it;
  458. // check before point
  459. unsigned newDist2 = dist + abs(*it - rect.right());
  460. if (newDist2 < bestDistance && isClose(*it, rect.right())) {
  461. QRect r(QPoint(*it - rect.width(), d->top - rect.height()),
  462. rect.size());
  463. if (!overlaysWithFirstNWidgets(r, i)) {
  464. bestDistance = newDist2;
  465. bestIndex = i + 1;
  466. bestSide = TOP;
  467. bestRect = r;
  468. }
  469. }
  470. }
  471. // check for resized placement options
  472. if (sizePol.horizontalPolicy() != QSizePolicy::Fixed) {
  473. int mid = rect.left() + rect.width() / 2;
  474. for (QSet<int>::iterator ita = sidePoints.begin() + 1;
  475. ita != sidePoints.end(); ++ita) {
  476. for (QSet<int>::iterator itb = sidePoints.begin();
  477. ita != itb; ++itb) {
  478. int sp_mid = (*ita + *itb) / 2;
  479. int sp_diff = *ita - *itb;
  480. if (isClose(sp_mid, mid)) {
  481. QRect r(*itb, d->top - rect.height(),
  482. sp_diff, rect.height());
  483. if (!overlaysWithFirstNWidgets(r, i)) {
  484. bestDistance = dist + abs(sp_mid - mid);
  485. bestIndex = i + 1;
  486. bestSide = TOP;
  487. bestRect = r;
  488. }
  489. }
  490. }
  491. }
  492. }
  493. }
  494. }
  495. /********************************************************
  496. * Check for placement against the bottom of the widget *
  497. ********************************************************/
  498. if (i == 0 || d->dockSide != TOP) {
  499. if (!(rect.left() > d->right() - SNAP_DISTANCE ||
  500. rect.right() < d->left + SNAP_DISTANCE) &&
  501. isClose(rect.top(), d->bottom())) {
  502. // rectangle is close to the edge
  503. unsigned dist = 8 * abs(rect.top() - d->bottom());
  504. // now find all points on this side
  505. // (use set as a sorted unique list)
  506. QSet<int> sidePoints;
  507. for (int j = 0; j <= i; ++j) {
  508. DockInfo* d2 = dockedWidgets[j];
  509. if (d->bottom() == d2->bottom()) {
  510. sidePoints.insert(d2->left);
  511. sidePoints.insert(d2->right());
  512. // check if any other widget rest against this side
  513. for (int k = i + 1; k < dockedWidgets.size(); ++k) {
  514. DockInfo* d3 = dockedWidgets[k];
  515. if (d3->top == d2->bottom()) {
  516. sidePoints.insert(d3->left);
  517. sidePoints.insert(d3->right());
  518. }
  519. }
  520. }
  521. }
  522. // widget placement can occur at all points, find the closest
  523. QSet<int>::iterator it = sidePoints.begin();
  524. for (int j = 0; j < sidePoints.size() - 1; ++j) {
  525. // check after point
  526. unsigned newDist1 = dist + abs(*it - rect.left());
  527. if (newDist1 < bestDistance && isClose(*it, rect.left())) {
  528. QRect r(QPoint(*it, d->bottom()), rect.size());
  529. if (!overlaysWithFirstNWidgets(r, i)) {
  530. bestDistance = newDist1;
  531. bestIndex = i + 1;
  532. bestSide = BOTTOM;
  533. bestRect = r;
  534. }
  535. }
  536. ++it;
  537. // check before point
  538. unsigned newDist2 = dist + abs(*it - rect.right());
  539. if (newDist2 < bestDistance && isClose(*it, rect.right())) {
  540. QRect r(QPoint(*it - rect.width(), d->bottom()), rect.size());
  541. if (!overlaysWithFirstNWidgets(r, i)) {
  542. bestDistance = newDist2;
  543. bestIndex = i + 1;
  544. bestSide = BOTTOM;
  545. bestRect = r;
  546. }
  547. }
  548. }
  549. // check for resized placement options
  550. if (sizePol.horizontalPolicy() != QSizePolicy::Fixed) {
  551. int mid = rect.left() + rect.width() / 2;
  552. for (QSet<int>::iterator ita = sidePoints.begin() + 1;
  553. ita != sidePoints.end(); ++ita) {
  554. for (QSet<int>::iterator itb = sidePoints.begin();
  555. ita != itb; ++itb) {
  556. int sp_mid = (*ita + *itb) / 2;
  557. int sp_diff = *ita - *itb;
  558. if (isClose(sp_mid, mid)) {
  559. QRect r(*itb, d->bottom(),
  560. sp_diff, rect.height());
  561. if (!overlaysWithFirstNWidgets(r, i)) {
  562. bestDistance = dist + abs(sp_mid - mid);
  563. bestIndex = i + 1;
  564. bestSide = BOTTOM;
  565. bestRect = r;
  566. }
  567. }
  568. }
  569. }
  570. }
  571. }
  572. }
  573. /******************************************************
  574. * Check for placement against the left of the widget *
  575. ******************************************************/
  576. if (i == 0 || d->dockSide != RIGHT) {
  577. if (!(rect.top() > d->bottom() - SNAP_DISTANCE ||
  578. rect.bottom() < d->top + SNAP_DISTANCE) &&
  579. isClose(rect.right(), d->left)) {
  580. // rectangle is close to the edge
  581. unsigned dist = 8 * abs(rect.right() - d->left);
  582. // now find all points on this side
  583. // (use set as a sorted unique list)
  584. QSet<int> sidePoints;
  585. for (int j = 0; j <= i; ++j) {
  586. DockInfo* d2 = dockedWidgets[j];
  587. if (d->left == d2->left) {
  588. sidePoints.insert(d2->top);
  589. sidePoints.insert(d2->bottom());
  590. // check if any other widget rest against this side
  591. for (int k = i + 1; k < dockedWidgets.size(); ++k) {
  592. DockInfo* d3 = dockedWidgets[k];
  593. if (d3->right() == d2->left) {
  594. sidePoints.insert(d3->top);
  595. sidePoints.insert(d3->bottom());
  596. }
  597. }
  598. }
  599. }
  600. // widget placement can occur at all points, find the closest
  601. QSet<int>::iterator it = sidePoints.begin();
  602. for (int j = 0; j < sidePoints.size() - 1; ++j) {
  603. // check after point
  604. unsigned newDist1 = dist + abs(*it - rect.top());
  605. if (newDist1 < bestDistance && isClose(*it, rect.top())) {
  606. QRect r(QPoint(d->left - rect.width(), *it), rect.size());
  607. if (!overlaysWithFirstNWidgets(r, i)) {
  608. bestDistance = newDist1;
  609. bestIndex = i + 1;
  610. bestSide = LEFT;
  611. bestRect = r;
  612. }
  613. }
  614. ++it;
  615. // check before point
  616. unsigned newDist2 = dist + abs(*it - rect.bottom());
  617. if (newDist2 < bestDistance && isClose(*it, rect.bottom())) {
  618. QRect r(QPoint(d->left - rect.width(), *it - rect.height()),
  619. rect.size());
  620. if (!overlaysWithFirstNWidgets(r, i)) {
  621. bestDistance = newDist2;
  622. bestIndex = i + 1;
  623. bestSide = LEFT;
  624. bestRect = r;
  625. }
  626. }
  627. }
  628. // check for resized placement options
  629. if (sizePol.verticalPolicy() != QSizePolicy::Fixed) {
  630. int mid = rect.top() + rect.height() / 2;
  631. for (QSet<int>::iterator ita = sidePoints.begin() + 1;
  632. ita != sidePoints.end(); ++ita) {
  633. for (QSet<int>::iterator itb = sidePoints.begin();
  634. ita != itb; ++itb) {
  635. int sp_mid = (*ita + *itb) / 2;
  636. int sp_diff = *ita - *itb;
  637. if (isClose(sp_mid, mid)) {
  638. QRect r(d->left - rect.width(), *itb,
  639. rect.width(), sp_diff);
  640. if (!overlaysWithFirstNWidgets(r, i)) {
  641. bestDistance = dist + abs(sp_mid - mid);
  642. bestIndex = i + 1;
  643. bestSide = LEFT;
  644. bestRect = r;
  645. }
  646. }
  647. }
  648. }
  649. }
  650. }
  651. }
  652. /*******************************************************
  653. * Check for placement against the right of the widget *
  654. *******************************************************/
  655. if (i == 0 || d->dockSide != LEFT) {
  656. if (!(rect.top() > d->bottom() - SNAP_DISTANCE ||
  657. rect.bottom() < d->top + SNAP_DISTANCE) &&
  658. isClose(rect.left(), d->right())) {
  659. // rectangle is close to the edge
  660. unsigned dist = 8 * abs(rect.left() - d->right());
  661. // now find all points on this side
  662. // (use set as a sorted unique list)
  663. QSet<int> sidePoints;
  664. for (int j = 0; j <= i; ++j) {
  665. DockInfo* d2 = dockedWidgets[j];
  666. if (d->right() == d2->right()) {
  667. sidePoints.insert(d2->top);
  668. sidePoints.insert(d2->bottom());
  669. // check if any other widget rest against this side
  670. for (int k = i + 1; k < dockedWidgets.size(); ++k) {
  671. DockInfo* d3 = dockedWidgets[k];
  672. if (d3->left == d2->right()) {
  673. sidePoints.insert(d3->top);
  674. sidePoints.insert(d3->bottom());
  675. }
  676. }
  677. }
  678. }
  679. // widget placement can occur at all points, find the closest
  680. QSet<int>::iterator it = sidePoints.begin();
  681. for (int j = 0; j < sidePoints.size() - 1; ++j) {
  682. // check after point
  683. unsigned newDist1 = dist + abs(*it - rect.top());
  684. if (newDist1 < bestDistance && isClose(*it, rect.top())) {
  685. QRect r(QPoint(d->left + d->width, *it), rect.size());
  686. if (!overlaysWithFirstNWidgets(r, i)) {
  687. bestDistance = newDist1;
  688. bestIndex = i + 1;
  689. bestSide = RIGHT;
  690. bestRect = r;
  691. }
  692. }
  693. ++it;
  694. // check before point
  695. unsigned newDist2 = dist + abs(*it - rect.bottom());
  696. if (newDist2 < bestDistance && isClose(*it, rect.bottom())) {
  697. QRect r(QPoint(d->right(), *it - rect.height()), rect.size());
  698. if (!overlaysWithFirstNWidgets(r, i)) {
  699. bestDistance = newDist2;
  700. bestIndex = i + 1;
  701. bestSide = RIGHT;
  702. bestRect = r;
  703. }
  704. }
  705. }
  706. // check for resized placement options
  707. if (sizePol.verticalPolicy() != QSizePolicy::Fixed) {
  708. int mid = rect.top() + rect.height() / 2;
  709. for (QSet<int>::iterator ita = sidePoints.begin() + 1;
  710. ita != sidePoints.end(); ++ita) {
  711. for (QSet<int>::iterator itb = sidePoints.begin();
  712. ita != itb; ++itb) {
  713. int sp_mid = (*ita + *itb) / 2;
  714. int sp_diff = *ita - *itb;
  715. if (isClose(sp_mid, mid)) {
  716. QRect r(d->right(), *itb, rect.width(), sp_diff);
  717. if (!overlaysWithFirstNWidgets(r, i)) {
  718. bestDistance = dist + abs(sp_mid - mid);
  719. bestIndex = i + 1;
  720. bestSide = RIGHT;
  721. bestRect = r;
  722. }
  723. }
  724. }
  725. }
  726. }
  727. }
  728. }
  729. }
  730. if (bestIndex) {
  731. rect = bestRect;
  732. index = bestIndex;
  733. side = bestSide;
  734. return true;
  735. } else {
  736. return false;
  737. }
  738. }
  739. bool DockableWidgetLayout::insertLocation(QRect& rect, const QSizePolicy& sizePol)
  740. {
  741. int index;
  742. DockSide side;
  743. return insertLocation(rect, index, side, sizePol);
  744. }
  745. void DockableWidgetLayout::getConfig(QStringList& list)
  746. {
  747. for (int i = 0; i < dockedWidgets.size(); ++i) {
  748. DockInfo* d = dockedWidgets.at(i);
  749. // string format D [Hidden/Visible] [Side] [Distance] [Width] [Height]
  750. QString s("%1 D %2 %3 %4 %5 %6");
  751. s = s.arg(d->widget->id());
  752. if (d->widget->isHidden()) {
  753. s = s.arg("H");
  754. } else {
  755. s = s.arg("V");
  756. }
  757. switch (d->dockSide) {
  758. case TOP:
  759. s = s.arg("T");
  760. break;
  761. case LEFT:
  762. s = s.arg("L");
  763. break;
  764. case RIGHT:
  765. s = s.arg("R");
  766. break;
  767. case BOTTOM:
  768. s = s.arg("B");
  769. break;
  770. }
  771. s = s.arg(d->dockDistance);
  772. if (d->useHintWidth) {
  773. s = s.arg(-1);
  774. } else {
  775. s = s.arg(d->width);
  776. }
  777. if (d->useHintHeight) {
  778. s = s.arg(-1);
  779. } else {
  780. s = s.arg(d->height);
  781. }
  782. list.append(s);
  783. }
  784. }