Changeset View
Changeset View
Standalone View
Standalone View
src/ViewSplitter.cpp
Show All 18 Lines | 1 | /* | |||
---|---|---|---|---|---|
19 | 02110-1301 USA. | 19 | 02110-1301 USA. | ||
20 | */ | 20 | */ | ||
21 | 21 | | |||
22 | // Own | 22 | // Own | ||
23 | #include "ViewSplitter.h" | 23 | #include "ViewSplitter.h" | ||
24 | 24 | | |||
25 | // Qt | 25 | // Qt | ||
26 | #include <QDebug> | 26 | #include <QDebug> | ||
27 | #include <QChildEvent> | ||||
28 | #include <QScrollBar> | ||||
27 | 29 | | |||
28 | // Konsole | 30 | // Konsole | ||
29 | #include "ViewContainer.h" | 31 | #include "ViewContainer.h" | ||
32 | #include "TerminalDisplay.h" | ||||
30 | 33 | | |||
31 | using Konsole::ViewSplitter; | 34 | using Konsole::ViewSplitter; | ||
32 | using Konsole::TabbedViewContainer; | 35 | using Konsole::TerminalDisplay; | ||
33 | 36 | | |||
34 | ViewSplitter::ViewSplitter(QWidget *parent) : | 37 | //TODO: Connect the TerminalDisplay destroyed signal here. | ||
35 | QSplitter(parent), | | |||
36 | _containers(QList<TabbedViewContainer *>()), | | |||
37 | _recursiveSplitting(true) | | |||
38 | { | | |||
39 | } | | |||
40 | 38 | | |||
41 | void ViewSplitter::childEmpty(ViewSplitter *splitter) | 39 | ViewSplitter::ViewSplitter(QWidget *parent) : | ||
40 | QSplitter(parent) | ||||
42 | { | 41 | { | ||
43 | delete splitter; | | |||
44 | | ||||
45 | if (count() == 0) { | | |||
46 | emit empty(this); | | |||
47 | } | | |||
48 | } | 42 | } | ||
49 | 43 | | |||
50 | void ViewSplitter::adjustContainerSize(TabbedViewContainer *container, int percentage) | 44 | void ViewSplitter::adjustActiveTerminalDisplaySize(int percentage) | ||
51 | { | 45 | { | ||
52 | int containerIndex = indexOf(container); | 46 | const int containerIndex = indexOf(activeTerminalDisplay()); | ||
53 | | ||||
54 | Q_ASSERT(containerIndex != -1); | 47 | Q_ASSERT(containerIndex != -1); | ||
55 | 48 | | |||
56 | QList<int> containerSizes = sizes(); | 49 | QList<int> containerSizes = sizes(); | ||
57 | 50 | | |||
58 | const int oldSize = containerSizes[containerIndex]; | 51 | const int oldSize = containerSizes[containerIndex]; | ||
59 | const auto newSize = static_cast<int>(oldSize * (1.0 + percentage / 100.0)); | 52 | const int newSize = static_cast<int>(oldSize * (1.0 + percentage / 100.0)); | ||
60 | | ||||
61 | const int perContainerDelta = (count() == 1) ? 0 : ((newSize - oldSize) / (count() - 1)) * (-1); | 53 | const int perContainerDelta = (count() == 1) ? 0 : ((newSize - oldSize) / (count() - 1)) * (-1); | ||
62 | 54 | | |||
63 | for (int i = 0; i < containerSizes.count(); i++) { | 55 | for (int& size : containerSizes) { | ||
64 | if (i == containerIndex) { | 56 | size += perContainerDelta; | ||
65 | containerSizes[i] = newSize; | | |||
66 | } else { | | |||
67 | containerSizes[i] = containerSizes[i] + perContainerDelta; | | |||
68 | } | | |||
69 | } | 57 | } | ||
58 | containerSizes[containerIndex] = newSize; | ||||
70 | 59 | | |||
71 | setSizes(containerSizes); | 60 | setSizes(containerSizes); | ||
72 | } | 61 | } | ||
73 | 62 | | |||
74 | ViewSplitter *ViewSplitter::activeSplitter() | 63 | ViewSplitter *ViewSplitter::activeSplitter() | ||
75 | { | 64 | { | ||
76 | QWidget *widget = focusWidget() != nullptr ? focusWidget() : this; | 65 | QWidget *widget = focusWidget() != nullptr ? focusWidget() : this; | ||
77 | 66 | | |||
78 | ViewSplitter *splitter = nullptr; | 67 | ViewSplitter *splitter = nullptr; | ||
79 | 68 | | |||
80 | while ((splitter == nullptr) && (widget != nullptr)) { | 69 | while ((splitter == nullptr) && (widget != nullptr)) { | ||
81 | splitter = qobject_cast<ViewSplitter *>(widget); | 70 | splitter = qobject_cast<ViewSplitter *>(widget); | ||
82 | widget = widget->parentWidget(); | 71 | widget = widget->parentWidget(); | ||
83 | } | 72 | } | ||
84 | 73 | | |||
85 | Q_ASSERT(splitter); | 74 | Q_ASSERT(splitter); | ||
86 | return splitter; | 75 | return splitter; | ||
87 | } | 76 | } | ||
88 | 77 | | |||
89 | void ViewSplitter::registerContainer(TabbedViewContainer *container) | | |||
90 | { | | |||
91 | _containers << container; | | |||
92 | connect(container, &TabbedViewContainer::empty, this, &ViewSplitter::containerEmpty); | | |||
93 | } | | |||
94 | | ||||
95 | void ViewSplitter::unregisterContainer(TabbedViewContainer *container) | | |||
96 | { | | |||
97 | _containers.removeAll(container); | | |||
98 | disconnect(container, nullptr, this, nullptr); | | |||
99 | } | | |||
100 | | ||||
101 | void ViewSplitter::updateSizes() | 78 | void ViewSplitter::updateSizes() | ||
102 | { | 79 | { | ||
103 | int space; | 80 | const int space = (orientation() == Qt::Horizontal ? width() : height()) / count(); | ||
104 | 81 | setSizes(QVector<int>(count(), space).toList()); | |||
105 | if (orientation() == Qt::Horizontal) { | | |||
106 | space = width() / count(); | | |||
107 | } else { | | |||
108 | space = height() / count(); | | |||
109 | } | | |||
110 | | ||||
111 | QList<int> widgetSizes; | | |||
112 | const int widgetCount = count(); | | |||
113 | widgetSizes.reserve(widgetCount); | | |||
114 | for (int i = 0; i < widgetCount; i++) { | | |||
115 | widgetSizes << space; | | |||
116 | } | | |||
117 | | ||||
118 | setSizes(widgetSizes); | | |||
119 | } | | |||
120 | | ||||
121 | void ViewSplitter::setRecursiveSplitting(bool recursive) | | |||
122 | { | | |||
123 | _recursiveSplitting = recursive; | | |||
124 | } | | |||
125 | | ||||
126 | bool ViewSplitter::recursiveSplitting() const | | |||
127 | { | | |||
128 | return _recursiveSplitting; | | |||
129 | } | 82 | } | ||
130 | 83 | | |||
131 | void ViewSplitter::removeContainer(TabbedViewContainer *container) | 84 | void ViewSplitter::addTerminalDisplay(TerminalDisplay *terminalDisplay, Qt::Orientation containerOrientation) | ||
132 | { | | |||
133 | Q_ASSERT(containers().contains(container)); | | |||
134 | | ||||
135 | unregisterContainer(container); | | |||
136 | } | | |||
137 | | ||||
138 | void ViewSplitter::addContainer(TabbedViewContainer *container, Qt::Orientation containerOrientation) | | |||
139 | { | 85 | { | ||
140 | ViewSplitter *splitter = activeSplitter(); | 86 | ViewSplitter *splitter = activeSplitter(); | ||
141 | 87 | if (splitter->count() < 2) { | |||
142 | if (splitter->count() < 2 | 88 | splitter->addWidget(terminalDisplay); | ||
143 | || containerOrientation == splitter->orientation() | | |||
144 | || !_recursiveSplitting) { | | |||
145 | splitter->registerContainer(container); | | |||
146 | splitter->addWidget(container); | | |||
147 | | ||||
148 | if (splitter->orientation() != containerOrientation) { | | |||
149 | splitter->setOrientation(containerOrientation); | 89 | splitter->setOrientation(containerOrientation); | ||
90 | } else if (containerOrientation == splitter->orientation()) { | ||||
91 | auto activeDisplay = splitter->activeTerminalDisplay(); | ||||
92 | if (!activeDisplay) { | ||||
93 | splitter->addWidget(terminalDisplay); | ||||
94 | } else { | ||||
95 | const int currentIndex = splitter->indexOf(activeDisplay); | ||||
96 | splitter->insertWidget(currentIndex, terminalDisplay); | ||||
150 | } | 97 | } | ||
151 | | ||||
152 | splitter->updateSizes(); | | |||
153 | } else { | 98 | } else { | ||
154 | auto newSplitter = new ViewSplitter(this); | 99 | auto newSplitter = new ViewSplitter(); | ||
155 | connect(newSplitter, &Konsole::ViewSplitter::empty, splitter, | | |||
156 | &Konsole::ViewSplitter::childEmpty); | | |||
157 | 100 | | |||
158 | TabbedViewContainer *oldContainer = splitter->activeContainer(); | 101 | TerminalDisplay *oldTerminalDisplay = splitter->activeTerminalDisplay(); | ||
159 | const int oldContainerIndex = splitter->indexOf(oldContainer); | 102 | const int oldContainerIndex = splitter->indexOf(oldTerminalDisplay); | ||
160 | 103 | newSplitter->addWidget(oldTerminalDisplay); | |||
161 | splitter->unregisterContainer(oldContainer); | 104 | newSplitter->addWidget(terminalDisplay); | ||
162 | | ||||
163 | newSplitter->registerContainer(oldContainer); | | |||
164 | newSplitter->registerContainer(container); | | |||
165 | | ||||
166 | newSplitter->addWidget(oldContainer); | | |||
167 | newSplitter->addWidget(container); | | |||
168 | newSplitter->setOrientation(containerOrientation); | 105 | newSplitter->setOrientation(containerOrientation); | ||
169 | newSplitter->updateSizes(); | 106 | newSplitter->updateSizes(); | ||
170 | newSplitter->show(); | 107 | newSplitter->show(); | ||
171 | 108 | | |||
172 | splitter->insertWidget(oldContainerIndex, newSplitter); | 109 | splitter->insertWidget(oldContainerIndex, newSplitter); | ||
173 | } | 110 | } | ||
111 | splitter->updateSizes(); | ||||
174 | } | 112 | } | ||
175 | 113 | | |||
176 | void ViewSplitter::containerEmpty(TabbedViewContainer * myContainer) | 114 | void ViewSplitter::childEvent(QChildEvent *event) | ||
177 | { | 115 | { | ||
178 | _containers.removeAll(myContainer); | 116 | QSplitter::childEvent(event); | ||
179 | if (count() == 0) { | | |||
180 | emit empty(this); | | |||
181 | } | | |||
182 | | ||||
183 | int children = 0; | | |||
184 | foreach (auto container, _containers) { | | |||
185 | children += container->count(); | | |||
186 | } | | |||
187 | | ||||
188 | if (children == 0) { | | |||
189 | emit allContainersEmpty(); | | |||
190 | } | | |||
191 | 117 | | |||
192 | // This container is no more, try to find another container to focus. | 118 | if (event->removed()) { | ||
193 | ViewSplitter *currentSplitter = activeSplitter(); | 119 | if (count() == 0) { | ||
194 | while(qobject_cast<ViewSplitter*>(currentSplitter->parent())) { | 120 | deleteLater(); | ||
195 | currentSplitter = qobject_cast<ViewSplitter*>(currentSplitter->parent()); | | |||
196 | } | 121 | } | ||
197 | 122 | if (!findChild<TerminalDisplay*>()) { | |||
198 | for(auto tabWidget : currentSplitter->findChildren<TabbedViewContainer*>()) { | 123 | deleteLater(); | ||
199 | if (tabWidget != myContainer && tabWidget->count()) { | | |||
200 | tabWidget->setCurrentIndex(0); | | |||
201 | } | 124 | } | ||
202 | } | 125 | } | ||
203 | } | 126 | } | ||
204 | 127 | | |||
205 | void ViewSplitter::activateNextContainer() | 128 | void ViewSplitter::handleFocusDirection(Qt::Orientation orientation, int direction) | ||
206 | { | 129 | { | ||
207 | TabbedViewContainer *active = activeContainer(); | 130 | auto terminalDisplay = activeTerminalDisplay(); | ||
131 | auto parentSplitter = qobject_cast<ViewSplitter*>(terminalDisplay->parentWidget()); | ||||
132 | auto topSplitter = parentSplitter->getToplevelSplitter(); | ||||
208 | 133 | | |||
209 | int index = _containers.indexOf(active); | 134 | const auto handleWidth = parentSplitter->handleWidth() <= 1 ? 4 : parentSplitter->handleWidth(); | ||
210 | 135 | | |||
211 | if (index == -1) { | 136 | const auto start = QPoint(terminalDisplay->x(), terminalDisplay->y()); | ||
212 | return; | 137 | const auto startMapped = parentSplitter->mapTo(topSplitter, start); | ||
213 | } | | |||
214 | 138 | | |||
215 | if (index == _containers.count() - 1) { | 139 | const int newX = orientation != Qt::Horizontal ? startMapped.x() + handleWidth | ||
216 | index = 0; | 140 | : direction == 1 ? startMapped.x() + terminalDisplay->width() + handleWidth | ||
217 | } else { | 141 | : startMapped.x() - handleWidth; | ||
218 | index++; | | |||
219 | } | | |||
220 | 142 | | |||
221 | setActiveContainer(_containers.at(index)); | 143 | const int newY = orientation != Qt::Vertical ? startMapped.y() + handleWidth | ||
222 | } | 144 | : direction == 1 ? startMapped.y() + terminalDisplay->height() + handleWidth | ||
223 | 145 | : startMapped.y() - handleWidth; | |||
224 | void ViewSplitter::activatePreviousContainer() | | |||
225 | { | | |||
226 | TabbedViewContainer *active = activeContainer(); | | |||
227 | 146 | | |||
228 | int index = _containers.indexOf(active); | 147 | const auto newPoint = QPoint(newX, newY); | ||
148 | auto child = topSplitter->childAt(newPoint); | ||||
229 | 149 | | |||
230 | if (index == 0) { | 150 | qDebug() << "Handling focus"; | ||
231 | index = _containers.count() - 1; | 151 | if (TerminalDisplay* terminal = qobject_cast<TerminalDisplay*>(child)) { | ||
232 | } else { | 152 | terminal->setFocus(Qt::OtherFocusReason); | ||
233 | index--; | 153 | } else if (qobject_cast<QScrollBar*>(child)) { | ||
154 | auto terminal = qobject_cast<TerminalDisplay*>(child->parent()); | ||||
155 | terminal->setFocus(Qt::OtherFocusReason); | ||||
156 | } else if (qobject_cast<QSplitterHandle*>(child)) { | ||||
hindenburg: try not to shadow another variable here | |||||
157 | auto targetSplitter = qobject_cast<QSplitter*>(child->parent()); | ||||
158 | auto terminal = qobject_cast<TerminalDisplay*>(targetSplitter->widget(0)); | ||||
159 | terminal->setFocus(Qt::OtherFocusReason); | ||||
234 | } | 160 | } | ||
235 | | ||||
236 | setActiveContainer(_containers.at(index)); | | |||
237 | } | 161 | } | ||
238 | 162 | | |||
239 | void ViewSplitter::setActiveContainer(TabbedViewContainer *container) | 163 | void ViewSplitter::focusUp() | ||
240 | { | 164 | { | ||
241 | QWidget *activeView = container->currentWidget(); | 165 | handleFocusDirection(Qt::Vertical, -1); | ||
166 | } | ||||
242 | 167 | | |||
243 | if (activeView != nullptr) { | 168 | void ViewSplitter::focusDown() | ||
244 | activeView->setFocus(Qt::OtherFocusReason); | 169 | { | ||
170 | handleFocusDirection(Qt::Vertical, +1); | ||||
245 | } | 171 | } | ||
172 | | ||||
173 | void ViewSplitter::focusLeft() | ||||
174 | { | ||||
175 | handleFocusDirection(Qt::Horizontal, -1); | ||||
246 | } | 176 | } | ||
247 | 177 | | |||
248 | TabbedViewContainer *ViewSplitter::activeContainer() const | 178 | void ViewSplitter::focusRight() | ||
249 | { | 179 | { | ||
250 | if (QWidget *focusW = focusWidget()) { | 180 | handleFocusDirection(Qt::Horizontal, +1); | ||
251 | TabbedViewContainer *focusContainer = nullptr; | 181 | } | ||
252 | 182 | | |||
253 | while (focusW != nullptr) { | 183 | TerminalDisplay *ViewSplitter::activeTerminalDisplay() const | ||
254 | foreach (TabbedViewContainer *container, _containers) { | 184 | { | ||
255 | if (container == focusW) { | 185 | auto focusedWidget = qobject_cast<TerminalDisplay*>(focusWidget()); | ||
256 | focusContainer = container; | 186 | return focusedWidget ? focusedWidget : findChild<TerminalDisplay*>(); | ||
257 | break; | | |||
258 | } | 187 | } | ||
188 | | ||||
189 | void ViewSplitter::maximizeCurrentTerminal() | ||||
190 | { | ||||
191 | handleMinimizeMaximize(true); | ||||
259 | } | 192 | } | ||
260 | focusW = focusW->parentWidget(); | 193 | | ||
194 | void ViewSplitter::restoreOtherTerminals() | ||||
195 | { | ||||
196 | handleMinimizeMaximize(false); | ||||
261 | } | 197 | } | ||
262 | 198 | | |||
263 | if (focusContainer != nullptr) { | 199 | void ViewSplitter::handleMinimizeMaximize(bool maximize) | ||
264 | return focusContainer; | 200 | { | ||
201 | auto viewSplitter = getToplevelSplitter(); | ||||
202 | auto terminalDisplays = viewSplitter->findChildren<TerminalDisplay*>(); | ||||
203 | auto currentActiveTerminal = viewSplitter->activeTerminalDisplay(); | ||||
204 | auto method = maximize ? &QWidget::hide : &QWidget::show; | ||||
205 | for(auto terminal : terminalDisplays) { | ||||
206 | if (Q_LIKELY(currentActiveTerminal != terminal)) { | ||||
207 | (terminal->*method)(); | ||||
265 | } | 208 | } | ||
266 | } | 209 | } | ||
267 | | ||||
268 | QList<ViewSplitter *> splitters = findChildren<ViewSplitter *>(); | | |||
269 | | ||||
270 | if (!splitters.isEmpty()) { | | |||
271 | return splitters.last()->activeContainer(); | | |||
272 | } else { | | |||
273 | if (!_containers.isEmpty()) { | | |||
274 | return _containers.last(); | | |||
275 | } else { | | |||
276 | return nullptr; | | |||
277 | } | 210 | } | ||
211 | | ||||
212 | ViewSplitter *ViewSplitter::getToplevelSplitter() | ||||
213 | { | ||||
214 | ViewSplitter *current = this; | ||||
215 | while(qobject_cast<ViewSplitter*>(current->parentWidget())) { | ||||
216 | current = qobject_cast<ViewSplitter*>(current->parentWidget()); | ||||
278 | } | 217 | } | ||
218 | return current; | ||||
279 | } | 219 | } |
try not to shadow another variable here