File indexing completed on 2024-04-28 05:50:56
0001 /* 0002 SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // Own 0008 #include "widgets/ViewSplitter.h" 0009 #include "KonsoleSettings.h" 0010 0011 // Qt 0012 #include <QApplication> 0013 #include <QChildEvent> 0014 #include <QDragEnterEvent> 0015 #include <QMimeData> 0016 0017 // C++ 0018 #include <memory> 0019 0020 // Konsole 0021 #include "terminalDisplay/TerminalDisplay.h" 0022 #include "terminalDisplay/TerminalFonts.h" 0023 #include "terminalDisplay/TerminalScrollBar.h" 0024 #include "widgets/ViewContainer.h" 0025 0026 using Konsole::TerminalDisplay; 0027 using Konsole::ViewSplitter; 0028 0029 bool ViewSplitter::m_drawTopLevelHandler; 0030 Qt::Orientation ViewSplitter::m_topLevelHandlerDrawnOrientation; 0031 int ViewSplitter::lastSplitterId = -1; 0032 0033 // TODO: Connect the TerminalDisplay destroyed signal here. 0034 0035 namespace 0036 { 0037 int calculateHandleWidth(int settingsEnum) 0038 { 0039 switch (settingsEnum) { 0040 case Konsole::KonsoleSettings::SplitDragHandleLarge: 0041 return 10; 0042 case Konsole::KonsoleSettings::SplitDragHandleMedium: 0043 return 5; 0044 case Konsole::KonsoleSettings::SplitDragHandleSmall: 0045 return 1; 0046 default: 0047 return 1; 0048 } 0049 } 0050 } 0051 0052 ViewSplitter::ViewSplitter(QWidget *parent) 0053 : QSplitter(parent) 0054 , _id(++lastSplitterId) 0055 { 0056 setAcceptDrops(true); 0057 connect(KonsoleSettings::self(), &KonsoleSettings::configChanged, this, [this] { 0058 setHandleWidth(calculateHandleWidth(KonsoleSettings::self()->splitDragHandleSize())); 0059 }); 0060 } 0061 0062 /* This function is called on the toplevel splitter, we need to look at the actual ViewSplitter inside it */ 0063 void ViewSplitter::adjustActiveTerminalDisplaySize(int percentage) 0064 { 0065 auto focusedTerminalDisplay = activeTerminalDisplay(); 0066 Q_ASSERT(focusedTerminalDisplay); 0067 0068 auto parentSplitter = qobject_cast<ViewSplitter *>(focusedTerminalDisplay->parent()); 0069 const int containerIndex = parentSplitter->indexOf(activeTerminalDisplay()); 0070 Q_ASSERT(containerIndex != -1); 0071 0072 QList<int> containerSizes = parentSplitter->sizes(); 0073 0074 const int oldSize = containerSizes[containerIndex]; 0075 const auto newSize = static_cast<int>(oldSize * (1.0 + percentage / 100.0)); 0076 const int perContainerDelta = (count() == 1) ? 0 : ((newSize - oldSize) / (count() - 1)) * (-1); 0077 0078 for (int &size : containerSizes) { 0079 size += perContainerDelta; 0080 } 0081 containerSizes[containerIndex] = newSize; 0082 0083 parentSplitter->setSizes(containerSizes); 0084 } 0085 0086 // Get the first splitter that's a parent of the current focused widget. 0087 ViewSplitter *ViewSplitter::activeSplitter() 0088 { 0089 QWidget *widget = focusWidget() != nullptr ? focusWidget() : this; 0090 0091 ViewSplitter *splitter = nullptr; 0092 0093 while ((splitter == nullptr) && (widget != nullptr)) { 0094 splitter = qobject_cast<ViewSplitter *>(widget); 0095 widget = widget->parentWidget(); 0096 } 0097 0098 Q_ASSERT(splitter); 0099 return splitter; 0100 } 0101 0102 void ViewSplitter::updateSizes() 0103 { 0104 const int space = (orientation() == Qt::Horizontal ? width() : height()) / count(); 0105 setSizes(QVector<int>(count(), space).toList()); 0106 } 0107 0108 void ViewSplitter::addTerminalDisplay(TerminalDisplay *terminalDisplay, Qt::Orientation containerOrientation, AddBehavior behavior) 0109 { 0110 ViewSplitter *splitter = activeSplitter(); 0111 const int currentIndex = splitter->activeTerminalDisplay() == nullptr ? splitter->count() : splitter->indexOf(splitter->activeTerminalDisplay()); 0112 0113 if (splitter->count() < 2) { 0114 splitter->insertWidget(behavior == AddBehavior::AddBefore ? currentIndex : currentIndex + 1, terminalDisplay); 0115 splitter->setOrientation(containerOrientation); 0116 splitter->updateSizes(); 0117 } else if (containerOrientation == splitter->orientation()) { 0118 splitter->insertWidget(currentIndex, terminalDisplay); 0119 splitter->updateSizes(); 0120 } else { 0121 QList<int> sizes = splitter->sizes(); 0122 auto newSplitter = new ViewSplitter(); 0123 TerminalDisplay *oldTerminalDisplay = splitter->activeTerminalDisplay(); 0124 const int oldContainerIndex = splitter->indexOf(oldTerminalDisplay); 0125 splitter->m_blockPropagatedDeletion = true; 0126 newSplitter->addWidget(behavior == AddBehavior::AddBefore ? terminalDisplay : oldTerminalDisplay); 0127 newSplitter->addWidget(behavior == AddBehavior::AddBefore ? oldTerminalDisplay : terminalDisplay); 0128 newSplitter->setOrientation(containerOrientation); 0129 newSplitter->show(); 0130 splitter->insertWidget(oldContainerIndex, newSplitter); 0131 splitter->m_blockPropagatedDeletion = false; 0132 splitter->setSizes(sizes); 0133 newSplitter->updateSizes(); 0134 } 0135 } 0136 0137 void ViewSplitter::addTerminalDisplay(TerminalDisplay *terminalDisplay, int index) 0138 { 0139 auto toplevelSplitter = getToplevelSplitter(); 0140 0141 if (index == -1) 0142 index = count(); 0143 0144 if (toplevelSplitter->count() == 2 && toplevelSplitter == qobject_cast<ViewSplitter *>(parent()) && toplevelSplitter->indexOf(terminalDisplay) != -1) { 0145 QVector<QWidget *> childWidgets; 0146 0147 for (int i = 0; i < count(); ++i) { 0148 childWidgets.append(widget(i)); 0149 } 0150 0151 childWidgets.insert(index, terminalDisplay); 0152 0153 for (auto child : childWidgets) { 0154 toplevelSplitter->addWidget(child); 0155 } 0156 } else { 0157 insertWidget(index, terminalDisplay); 0158 } 0159 0160 updateSizes(); 0161 } 0162 0163 void ViewSplitter::addSplitter(ViewSplitter *splitter, int index) 0164 { 0165 if (index == -1) 0166 index = count(); 0167 0168 m_blockPropagatedDeletion = true; 0169 0170 if (splitter->orientation() == orientation()) { 0171 QVector<QWidget *> children; 0172 0173 for (int i = 0; i < splitter->count(); ++i) { 0174 children.append(splitter->widget(i)); 0175 } 0176 0177 for (int i = 0; i < children.count(); ++i) { 0178 insertWidget(index + i, children[i]); 0179 } 0180 } else { 0181 insertWidget(index, splitter); 0182 } 0183 0184 m_blockPropagatedDeletion = false; 0185 updateSizes(); 0186 } 0187 0188 void ViewSplitter::childEvent(QChildEvent *event) 0189 { 0190 QSplitter::childEvent(event); 0191 0192 if (event->removed()) { 0193 if (count() == 0) { 0194 auto *parent_splitter = qobject_cast<ViewSplitter *>(parent()); 0195 if (parent_splitter != nullptr) { 0196 setParent(nullptr); 0197 } 0198 deleteLater(); 0199 } else if (count() == 1) { 0200 if (!m_blockPropagatedDeletion) { 0201 auto *parent_splitter = qobject_cast<ViewSplitter *>(parent()); 0202 if (parent_splitter) { 0203 parent_splitter->m_blockPropagatedDeletion = true; 0204 0205 // Force recalculation of sizes to take into account recent 0206 // setVisible(true) calls due to a clearMaximize() upon 0207 // sessionFinished. Without the refresh() call, the size of 0208 // a previously hidden splitter could still be 0. The 0209 // documentation of refresh() says "You should not need to 0210 // call this function", but it seems we do. 0211 parent_splitter->refresh(); 0212 0213 const auto sizes = parent_splitter->sizes(); 0214 auto *wdg = widget(0); 0215 const int oldContainerIndex = parent_splitter->indexOf(this); 0216 parent_splitter->replaceWidget(oldContainerIndex, wdg); 0217 parent_splitter->m_blockPropagatedDeletion = false; 0218 parent_splitter->setSizes(sizes); 0219 wdg->setFocus(); 0220 deleteLater(); 0221 } 0222 } 0223 } 0224 } 0225 0226 auto terminals = getToplevelSplitter()->findChildren<TerminalDisplay *>(); 0227 for (auto terminal : terminals) { 0228 terminal->headerBar()->applyVisibilitySettings(); 0229 } 0230 } 0231 0232 void ViewSplitter::handleFocusDirection(Qt::Orientation orientation, int direction) 0233 { 0234 auto terminalDisplay = activeTerminalDisplay(); 0235 auto parentSplitter = qobject_cast<ViewSplitter *>(terminalDisplay->parentWidget()); 0236 auto topSplitter = parentSplitter->getToplevelSplitter(); 0237 0238 // Find the theme's splitter width + extra space to find valid terminal 0239 // See https://bugs.kde.org/show_bug.cgi?id=411387 for more info 0240 const auto handleWidth = parentSplitter->handleWidth() + 3; 0241 0242 const auto start = QPoint(terminalDisplay->x(), terminalDisplay->y()); 0243 const auto startMapped = parentSplitter->mapTo(topSplitter, start); 0244 0245 const int newX = orientation != Qt::Horizontal ? startMapped.x() + handleWidth 0246 : direction == 1 ? startMapped.x() + terminalDisplay->width() + handleWidth 0247 : startMapped.x() - handleWidth; 0248 0249 const int newY = orientation != Qt::Vertical ? startMapped.y() + handleWidth 0250 : direction == 1 ? startMapped.y() + terminalDisplay->height() + handleWidth 0251 : startMapped.y() - handleWidth; 0252 0253 const auto newPoint = QPoint(newX, newY); 0254 auto child = topSplitter->childAt(newPoint); 0255 0256 TerminalDisplay *focusTerminal = nullptr; 0257 if (auto *terminal = qobject_cast<TerminalDisplay *>(child)) { 0258 focusTerminal = terminal; 0259 } else if (qobject_cast<QSplitterHandle *>(child) != nullptr) { 0260 auto targetSplitter = qobject_cast<QSplitter *>(child->parent()); 0261 focusTerminal = qobject_cast<TerminalDisplay *>(targetSplitter->widget(0)); 0262 } else if (qobject_cast<QWidget *>(child) != nullptr) { 0263 while (child != nullptr && focusTerminal == nullptr) { 0264 focusTerminal = qobject_cast<TerminalDisplay *>(child->parentWidget()); 0265 child = child->parentWidget(); 0266 } 0267 } 0268 if (focusTerminal != nullptr) { 0269 focusTerminal->setFocus(Qt::OtherFocusReason); 0270 } 0271 } 0272 0273 void ViewSplitter::focusUp() 0274 { 0275 handleFocusDirection(Qt::Vertical, -1); 0276 } 0277 0278 void ViewSplitter::focusDown() 0279 { 0280 handleFocusDirection(Qt::Vertical, +1); 0281 } 0282 0283 void ViewSplitter::focusLeft() 0284 { 0285 handleFocusDirection(Qt::Horizontal, -1); 0286 } 0287 0288 void ViewSplitter::focusRight() 0289 { 0290 handleFocusDirection(Qt::Horizontal, +1); 0291 } 0292 0293 TerminalDisplay *ViewSplitter::activeTerminalDisplay() const 0294 { 0295 auto focusedWidget = focusWidget(); 0296 auto focusedTerminalDisplay = qobject_cast<TerminalDisplay *>(focusedWidget); 0297 0298 // TD's child can be focused - try to find parent. 0299 while (focusedTerminalDisplay == nullptr && focusedWidget != nullptr && focusedWidget != this) { 0300 focusedWidget = focusedWidget->parentWidget(); 0301 focusedTerminalDisplay = qobject_cast<TerminalDisplay *>(focusedWidget); 0302 } 0303 0304 return focusedTerminalDisplay != nullptr ? focusedTerminalDisplay : findChild<TerminalDisplay *>(); 0305 } 0306 0307 void ViewSplitter::toggleMaximizeCurrentTerminal() 0308 { 0309 m_terminalMaximized = !m_terminalMaximized; 0310 handleMinimizeMaximize(m_terminalMaximized, false); 0311 } 0312 0313 void ViewSplitter::toggleZoomMaximizeCurrentTerminal() 0314 { 0315 m_terminalMaximized = !m_terminalMaximized; 0316 handleMinimizeMaximize(m_terminalMaximized, true); 0317 } 0318 0319 namespace 0320 { 0321 void restoreAll(QList<TerminalDisplay *> &&terminalDisplays, QList<ViewSplitter *> &&splitters) 0322 { 0323 for (auto splitter : splitters) { 0324 splitter->setVisible(true); 0325 } 0326 for (auto terminalDisplay : terminalDisplays) { 0327 terminalDisplay->setVisible(true); 0328 } 0329 } 0330 } 0331 0332 bool ViewSplitter::hideRecurse(TerminalDisplay *currentTerminalDisplay) 0333 { 0334 bool allHidden = true; 0335 0336 for (int i = 0, end = count(); i < end; i++) { 0337 if (auto *maybeSplitter = qobject_cast<ViewSplitter *>(widget(i))) { 0338 allHidden = maybeSplitter->hideRecurse(currentTerminalDisplay) && allHidden; 0339 continue; 0340 } 0341 if (auto maybeTerminalDisplay = qobject_cast<TerminalDisplay *>(widget(i))) { 0342 if (maybeTerminalDisplay == currentTerminalDisplay) { 0343 allHidden = false; 0344 } else { 0345 maybeTerminalDisplay->setVisible(false); 0346 } 0347 } 0348 } 0349 0350 if (allHidden) { 0351 setVisible(false); 0352 } 0353 return allHidden; 0354 } 0355 0356 void ViewSplitter::handleMinimizeMaximize(bool maximize, bool zoom) 0357 { 0358 auto topLevelSplitter = getToplevelSplitter(); 0359 auto currentTerminalDisplay = topLevelSplitter->activeTerminalDisplay(); 0360 currentTerminalDisplay->setExpandedMode(maximize); 0361 0362 auto currentTerminalFont = currentTerminalDisplay->terminalFont()->getVTFont(); 0363 0364 if (maximize) { 0365 if (zoom) { 0366 fontSizeBeforeMaximization = currentTerminalFont.pointSizeF(); 0367 auto headerBar = currentTerminalDisplay->headerBar(); 0368 auto headerBarHeight = headerBar->isVisible() ? headerBar->height() : 0; 0369 auto scrollBar = currentTerminalDisplay->scrollBar(); 0370 auto scrollBarWidth = scrollBar->scrollBarPosition() != Enum::ScrollBarHidden ? scrollBar->width() : 0; 0371 0372 // This is very inexact, which is fine, because the aspect ratio of 0373 // both the scaled font and the terminal can differ anyway. 0374 auto scaleFactor = std::min( 0375 (topLevelSplitter->width() - scrollBarWidth) 0376 / (currentTerminalDisplay->width() - scrollBarWidth), 0377 0378 (topLevelSplitter->height() - headerBarHeight) 0379 / (currentTerminalDisplay->height() - headerBarHeight) 0380 ); 0381 auto newSize = int(fontSizeBeforeMaximization * scaleFactor * 0.97); 0382 0383 if (newSize > fontSizeBeforeMaximization) { 0384 currentTerminalFont.setPointSizeF(newSize); 0385 currentTerminalDisplay->terminalFont()->setVTFont(currentTerminalFont); 0386 } 0387 } else { 0388 fontSizeBeforeMaximization = 0; 0389 } 0390 0391 for (int i = 0, end = topLevelSplitter->count(); i < end; i++) { 0392 auto widgetAt = topLevelSplitter->widget(i); 0393 if (auto *maybeSplitter = qobject_cast<ViewSplitter *>(widgetAt)) { 0394 maybeSplitter->hideRecurse(currentTerminalDisplay); 0395 } 0396 if (auto maybeTerminalDisplay = qobject_cast<TerminalDisplay *>(widgetAt)) { 0397 if (maybeTerminalDisplay != currentTerminalDisplay) { 0398 maybeTerminalDisplay->setVisible(false); 0399 } 0400 } 0401 } 0402 } else { 0403 // When restoring to un-maximzed state, it doesn't matter if this is done through 0404 // toggleMaximizeCurrentTerminal or toggleZoomMaximizeCurrentTerminal. 0405 // Only the method which was used to maximize, determines if the font size needs 0406 // to be restored. 0407 if (fontSizeBeforeMaximization) { 0408 currentTerminalFont.setPointSizeF(fontSizeBeforeMaximization); 0409 currentTerminalDisplay->terminalFont()->setVTFont(currentTerminalFont); 0410 } 0411 0412 restoreAll(topLevelSplitter->findChildren<TerminalDisplay *>(), topLevelSplitter->findChildren<ViewSplitter *>()); 0413 } 0414 } 0415 0416 void ViewSplitter::clearMaximized() 0417 { 0418 ViewSplitter *top = getToplevelSplitter(); 0419 Q_ASSERT(top); 0420 if (top->terminalMaximized()) { 0421 top->toggleMaximizeCurrentTerminal(); 0422 } 0423 } 0424 0425 ViewSplitter *ViewSplitter::getToplevelSplitter() 0426 { 0427 ViewSplitter *current = this; 0428 while (qobject_cast<ViewSplitter *>(current->parentWidget()) != nullptr) { 0429 current = qobject_cast<ViewSplitter *>(current->parentWidget()); 0430 } 0431 return current; 0432 } 0433 0434 ViewSplitter *ViewSplitter::getChildSplitter(int id) 0435 { 0436 for (auto childSplitter : findChildren<ViewSplitter *>()) { 0437 if (childSplitter->id() == id) 0438 return childSplitter; 0439 } 0440 0441 return nullptr; 0442 } 0443 0444 QString ViewSplitter::getChildWidgetsLayout() 0445 { 0446 QString layoutString; 0447 0448 for (int i = 0; i < count(); ++i) { 0449 if (auto v = qobject_cast<TerminalDisplay *>(widget(i))) 0450 layoutString += QString::number(v->id()); 0451 else if (auto s = qobject_cast<ViewSplitter *>(widget(i))) 0452 layoutString += s->getChildWidgetsLayout(); 0453 0454 layoutString += QLatin1Char('|'); 0455 } 0456 0457 layoutString.removeLast(); 0458 0459 if (orientation() == Qt::Orientation::Horizontal) 0460 layoutString = QLatin1Char('[') + layoutString + QLatin1Char(']'); 0461 else 0462 layoutString = QLatin1Char('{') + layoutString + QLatin1Char('}'); 0463 0464 return QStringLiteral("(%1)").arg(id()) + layoutString; 0465 } 0466 0467 namespace 0468 { 0469 TerminalDisplay *currentDragTarget = nullptr; 0470 } 0471 0472 void Konsole::ViewSplitter::dragEnterEvent(QDragEnterEvent *ev) 0473 { 0474 const auto dragId = QStringLiteral("konsole/terminal_display"); 0475 if (ev->mimeData()->hasFormat(dragId)) { 0476 auto other_pid = ev->mimeData()->data(dragId).toInt(); 0477 // don't accept the drop if it's another instance of konsole 0478 if (qApp->applicationPid() != other_pid) { 0479 return; 0480 } 0481 if (getToplevelSplitter()->terminalMaximized()) { 0482 return; 0483 } 0484 ev->accept(); 0485 } 0486 } 0487 0488 void Konsole::ViewSplitter::dragMoveEvent(QDragMoveEvent *ev) 0489 { 0490 auto currentWidget = childAt(ev->position().toPoint()); 0491 if (auto terminal = qobject_cast<TerminalDisplay *>(currentWidget)) { 0492 if ((currentDragTarget != nullptr) && currentDragTarget != terminal) { 0493 currentDragTarget->hideDragTarget(); 0494 } 0495 if (terminal == ev->source()) { 0496 return; 0497 } 0498 currentDragTarget = terminal; 0499 auto localPos = currentDragTarget->mapFromParent(ev->position().toPoint()); 0500 currentDragTarget->showDragTarget(localPos); 0501 } 0502 } 0503 0504 void Konsole::ViewSplitter::dragLeaveEvent(QDragLeaveEvent *event) 0505 { 0506 Q_UNUSED(event) 0507 if (currentDragTarget != nullptr) { 0508 currentDragTarget->hideDragTarget(); 0509 currentDragTarget = nullptr; 0510 } 0511 } 0512 0513 void Konsole::ViewSplitter::dropEvent(QDropEvent *ev) 0514 { 0515 if (ev->mimeData()->hasFormat(QStringLiteral("konsole/terminal_display"))) { 0516 if (getToplevelSplitter()->terminalMaximized()) { 0517 return; 0518 } 0519 if (currentDragTarget != nullptr) { 0520 m_blockPropagatedDeletion = true; 0521 0522 currentDragTarget->hideDragTarget(); 0523 auto source = qobject_cast<TerminalDisplay *>(ev->source()); 0524 source->setVisible(false); 0525 source->setParent(nullptr); 0526 0527 currentDragTarget->setFocus(Qt::OtherFocusReason); 0528 const auto droppedEdge = currentDragTarget->droppedEdge(); 0529 0530 AddBehavior behavior = droppedEdge == Qt::LeftEdge || droppedEdge == Qt::TopEdge ? AddBehavior::AddBefore : AddBehavior::AddAfter; 0531 0532 Qt::Orientation orientation = droppedEdge == Qt::LeftEdge || droppedEdge == Qt::RightEdge ? Qt::Horizontal : Qt::Vertical; 0533 0534 // Add the display so it can be counted correctly by ViewManager 0535 addTerminalDisplay(source, orientation, behavior); 0536 0537 // topLevel is the splitter that's connected with the ViewManager 0538 // that in turn can call the SessionController. 0539 Q_EMIT getToplevelSplitter()->terminalDisplayDropped(source); 0540 source->setVisible(true); 0541 currentDragTarget = nullptr; 0542 0543 m_blockPropagatedDeletion = false; 0544 } 0545 } 0546 } 0547 0548 void Konsole::ViewSplitter::showEvent(QShowEvent *) 0549 { 0550 // Fixes lost focus in background mode. 0551 setFocusProxy(activeSplitter()->activeTerminalDisplay()); 0552 } 0553 0554 QPoint Konsole::ViewSplitter::mapToTopLevel(const QPoint &p) 0555 { 0556 auto parentSplitter = qobject_cast<ViewSplitter *>(parent()); 0557 if (parentSplitter) { 0558 auto next_pos = mapToParent(p); 0559 return parentSplitter->mapToTopLevel(next_pos); 0560 } 0561 return p; 0562 } 0563 0564 QPoint Konsole::ViewSplitter::mapFromTopLevel(const QPoint &p) 0565 { 0566 auto parentSplitter = qobject_cast<ViewSplitter *>(parent()); 0567 if (parentSplitter) { 0568 return mapFromParent(parentSplitter->mapFromTopLevel(p)); 0569 } 0570 return p; 0571 } 0572 0573 Konsole::ViewSplitterHandle::ViewSplitterHandle(Qt::Orientation orientation, QSplitter *parent) 0574 : QSplitterHandle(orientation, parent) 0575 , mouseReleaseEventCounter(0) 0576 { 0577 } 0578 0579 QSplitterHandle *ViewSplitter::createHandle() 0580 { 0581 return new ViewSplitterHandle(orientation(), this); 0582 } 0583 0584 namespace 0585 { 0586 QList<int> allSplitterSizes; 0587 0588 int search_closest(const QList<int> &sorted_array, int x) 0589 { 0590 if (sorted_array.isEmpty()) { 0591 return -1; 0592 } 0593 0594 auto iter_geq = std::lower_bound(sorted_array.begin(), sorted_array.end(), x); 0595 0596 if (iter_geq == sorted_array.begin()) { 0597 return sorted_array[0]; 0598 } 0599 0600 int a = *(iter_geq - 1); 0601 int b = *(iter_geq); 0602 0603 if (abs(x - a) < abs(x - b)) { 0604 return a; 0605 } 0606 0607 return b; 0608 } 0609 } 0610 0611 void Konsole::ViewSplitterHandle::mousePressEvent(QMouseEvent *ev) 0612 { 0613 auto parentSplitter = qobject_cast<ViewSplitter *>(parentWidget()); 0614 auto topLevelSplitter = parentSplitter->getToplevelSplitter(); 0615 0616 QList<ViewSplitter *> splitters = topLevelSplitter->findChildren<ViewSplitter *>(); 0617 splitters.append(topLevelSplitter); 0618 0619 for (auto splitter : splitters) { 0620 if (splitter->orientation() != orientation()) { 0621 continue; 0622 } 0623 0624 int delta = 0; 0625 for (auto point : splitter->sizes()) { 0626 delta += point; 0627 QPoint thisPoint = orientation() == Qt::Horizontal ? QPoint(delta, 0) : QPoint(0, delta); 0628 0629 QPoint splitterPos = splitter->mapToTopLevel(thisPoint); 0630 0631 const int ourPos = orientation() == Qt::Horizontal ? splitterPos.x() : splitterPos.y(); 0632 0633 allSplitterSizes.push_back(ourPos); 0634 } 0635 } 0636 0637 std::sort(std::begin(allSplitterSizes), std::end(allSplitterSizes)); 0638 0639 QPoint thisPoint = parentSplitter->mapToTopLevel(mapToParent(ev->pos())); 0640 const int thisValue = search_closest(allSplitterSizes, orientation() == Qt::Horizontal ? thisPoint.x() : thisPoint.y()); 0641 allSplitterSizes.removeOne(thisValue); 0642 { // context for the splitterSet temporary. 0643 auto splitterSet = QSet<int>(std::begin(allSplitterSizes), std::end(allSplitterSizes)); 0644 allSplitterSizes = QList<int>(std::begin(splitterSet), std::end(splitterSet)); 0645 } 0646 std::sort(std::begin(allSplitterSizes), std::end(allSplitterSizes)); 0647 0648 mouseReleaseEventCounter = 0; 0649 0650 QSplitterHandle::mousePressEvent(ev); 0651 } 0652 0653 void Konsole::ViewSplitterHandle::mouseReleaseEvent(QMouseEvent *ev) 0654 { 0655 allSplitterSizes.clear(); 0656 if (++mouseReleaseEventCounter > 1) { 0657 mouseDoubleClickEvent(ev); 0658 } 0659 QSplitterHandle::mouseReleaseEvent(ev); 0660 } 0661 0662 void Konsole::ViewSplitterHandle::mouseMoveEvent(QMouseEvent *ev) 0663 { 0664 ViewSplitter *parentSplitter = qobject_cast<ViewSplitter *>(parentWidget()); 0665 0666 QPoint thisPoint = parentSplitter->mapToTopLevel(mapToParent(ev->pos())); 0667 0668 const int thisValue = orientation() == Qt::Horizontal ? thisPoint.x() : thisPoint.y(); 0669 const int nearest = search_closest(allSplitterSizes, thisValue); 0670 const int threshould = qAbs(nearest - thisValue); 0671 if (threshould <= 20) { 0672 auto *thisSplitter = qobject_cast<ViewSplitter *>(splitter()); 0673 QPoint localPoint = thisSplitter->mapFromTopLevel(orientation() == Qt::Horizontal ? QPoint(nearest, 0) : QPoint(0, nearest)); 0674 moveSplitter(orientation() == Qt::Horizontal ? localPoint.x() : localPoint.y()); 0675 return; 0676 } 0677 0678 mouseReleaseEventCounter = 0; 0679 0680 QSplitterHandle::mouseMoveEvent(ev); 0681 } 0682 0683 void Konsole::ViewSplitterHandle::mouseDoubleClickEvent(QMouseEvent *ev) 0684 { 0685 auto parentSplitter = qobject_cast<ViewSplitter *>(parentWidget()); 0686 0687 if (parentSplitter->count() > 1) { 0688 for (int i = 1; i < parentSplitter->count(); i++) { 0689 if (parentSplitter->handle(i) != this) { 0690 continue; 0691 } 0692 if (orientation() == Qt::Horizontal) { 0693 moveSplitter(parentSplitter->widget(i - 1)->pos().x() 0694 + ((parentSplitter->widget(i)->pos().x() + parentSplitter->widget(i)->width() - (parentSplitter->widget(i - 1)->pos().x())) / 2)); 0695 } else { 0696 moveSplitter(parentSplitter->widget(i - 1)->pos().y() 0697 + ((parentSplitter->widget(i)->pos().y() + parentSplitter->widget(i)->height() - (parentSplitter->widget(i - 1)->pos().y())) / 2)); 0698 } 0699 break; 0700 } 0701 } 0702 0703 mouseReleaseEventCounter = 0; 0704 QSplitterHandle::mouseDoubleClickEvent(ev); 0705 } 0706 0707 #include "moc_ViewSplitter.cpp"