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; | ||
36 | | ||||
37 | //TODO: Connect the TerminalDisplay destroyed signal here. | ||||
33 | 38 | | |||
34 | ViewSplitter::ViewSplitter(QWidget *parent) : | 39 | ViewSplitter::ViewSplitter(QWidget *parent) : | ||
35 | QSplitter(parent), | 40 | QSplitter(parent) | ||
36 | _containers(QList<TabbedViewContainer *>()), | | |||
37 | _recursiveSplitting(true) | | |||
38 | { | 41 | { | ||
39 | } | 42 | } | ||
40 | 43 | | |||
41 | void ViewSplitter::childEmpty(ViewSplitter *splitter) | 44 | void ViewSplitter::adjustActiveTerminalDisplaySize(int percentage) | ||
42 | { | 45 | { | ||
43 | delete splitter; | 46 | const int containerIndex = indexOf(activeTerminalDisplay()); | ||
44 | | ||||
45 | if (count() == 0) { | | |||
46 | emit empty(this); | | |||
47 | } | | |||
48 | } | | |||
49 | | ||||
50 | void ViewSplitter::adjustContainerSize(TabbedViewContainer *container, int percentage) | | |||
51 | { | | |||
52 | int containerIndex = indexOf(container); | | |||
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 | } | 82 | } | ||
125 | 83 | | |||
126 | bool ViewSplitter::recursiveSplitting() const | 84 | void ViewSplitter::addTerminalDisplay(TerminalDisplay *terminalDisplay, Qt::Orientation containerOrientation) | ||
127 | { | | |||
128 | return _recursiveSplitting; | | |||
129 | } | | |||
130 | | ||||
131 | void ViewSplitter::removeContainer(TabbedViewContainer *container) | | |||
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 | | |||
142 | if (splitter->count() < 2 | 88 | if (splitter->count() < 2 | ||
143 | || containerOrientation == splitter->orientation() | 89 | || containerOrientation == splitter->orientation()) { | ||
144 | || !_recursiveSplitting) { | 90 | splitter->addWidget(terminalDisplay); | ||
145 | splitter->registerContainer(container); | | |||
146 | splitter->addWidget(container); | | |||
147 | 91 | | |||
148 | if (splitter->orientation() != containerOrientation) { | 92 | if (splitter->orientation() != containerOrientation) { | ||
149 | splitter->setOrientation(containerOrientation); | 93 | splitter->setOrientation(containerOrientation); | ||
150 | } | 94 | } | ||
151 | | ||||
152 | splitter->updateSizes(); | 95 | splitter->updateSizes(); | ||
153 | } else { | 96 | } else { | ||
154 | auto newSplitter = new ViewSplitter(this); | 97 | auto newSplitter = new ViewSplitter(); | ||
155 | connect(newSplitter, &Konsole::ViewSplitter::empty, splitter, | | |||
156 | &Konsole::ViewSplitter::childEmpty); | | |||
157 | 98 | | |||
158 | TabbedViewContainer *oldContainer = splitter->activeContainer(); | 99 | TerminalDisplay *oldTerminalDisplay = splitter->activeTerminalDisplay(); | ||
159 | const int oldContainerIndex = splitter->indexOf(oldContainer); | 100 | const int oldContainerIndex = splitter->indexOf(oldTerminalDisplay); | ||
160 | 101 | newSplitter->addWidget(oldTerminalDisplay); | |||
161 | splitter->unregisterContainer(oldContainer); | 102 | 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); | 103 | newSplitter->setOrientation(containerOrientation); | ||
169 | newSplitter->updateSizes(); | 104 | newSplitter->updateSizes(); | ||
170 | newSplitter->show(); | 105 | newSplitter->show(); | ||
171 | 106 | | |||
172 | splitter->insertWidget(oldContainerIndex, newSplitter); | 107 | splitter->insertWidget(oldContainerIndex, newSplitter); | ||
173 | } | 108 | } | ||
174 | } | 109 | } | ||
175 | 110 | | |||
176 | void ViewSplitter::containerEmpty(TabbedViewContainer * myContainer) | 111 | void ViewSplitter::childEvent(QChildEvent *event) | ||
177 | { | 112 | { | ||
178 | _containers.removeAll(myContainer); | 113 | 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 | 114 | | |||
192 | // This container is no more, try to find another container to focus. | 115 | if (event->removed()) { | ||
193 | ViewSplitter *currentSplitter = activeSplitter(); | 116 | if (count() == 0) { | ||
194 | while(qobject_cast<ViewSplitter*>(currentSplitter->parent())) { | 117 | deleteLater(); | ||
195 | currentSplitter = qobject_cast<ViewSplitter*>(currentSplitter->parent()); | | |||
196 | } | 118 | } | ||
197 | 119 | if (!findChild<TerminalDisplay*>()) { | |||
198 | for(auto tabWidget : currentSplitter->findChildren<TabbedViewContainer*>()) { | 120 | deleteLater(); | ||
199 | if (tabWidget != myContainer && tabWidget->count()) { | | |||
200 | tabWidget->setCurrentIndex(0); | | |||
201 | } | 121 | } | ||
202 | } | 122 | } | ||
203 | } | 123 | } | ||
204 | 124 | | |||
205 | void ViewSplitter::activateNextContainer() | 125 | void ViewSplitter::handleFocusDirection(Qt::Orientation orientation, int direction) | ||
206 | { | 126 | { | ||
207 | TabbedViewContainer *active = activeContainer(); | 127 | auto terminalDisplay = activeTerminalDisplay(); | ||
128 | auto parentSplitter = qobject_cast<ViewSplitter*>(terminalDisplay->parentWidget()); | ||||
129 | auto topSplitter = parentSplitter->getToplevelSplitter(); | ||||
208 | 130 | | |||
209 | int index = _containers.indexOf(active); | 131 | const auto handleWidth = parentSplitter->handleWidth() <= 1 ? 4 : parentSplitter->handleWidth(); | ||
210 | 132 | | |||
211 | if (index == -1) { | 133 | const auto start = QPoint(terminalDisplay->x(), terminalDisplay->y()); | ||
212 | return; | 134 | const auto startMapped = parentSplitter->mapTo(topSplitter, start); | ||
213 | } | | |||
214 | 135 | | |||
215 | if (index == _containers.count() - 1) { | 136 | const int newX = orientation != Qt::Horizontal ? startMapped.x() + handleWidth | ||
216 | index = 0; | 137 | : direction == 1 ? startMapped.x() + terminalDisplay->width() + handleWidth | ||
217 | } else { | 138 | : startMapped.x() - handleWidth; | ||
218 | index++; | 139 | | ||
219 | } | 140 | const int newY = orientation != Qt::Vertical ? startMapped.y() + handleWidth | ||
141 | : direction == 1 ? startMapped.y() + terminalDisplay->height() + handleWidth | ||||
142 | : startMapped.y() - handleWidth; | ||||
220 | 143 | | |||
221 | setActiveContainer(_containers.at(index)); | 144 | const auto newPoint = QPoint(newX, newY); | ||
145 | auto child = topSplitter->childAt(newPoint); | ||||
146 | | ||||
147 | if (TerminalDisplay* terminal = qobject_cast<TerminalDisplay*>(child)) { | ||||
148 | terminal->setFocus(Qt::OtherFocusReason); | ||||
149 | } else if (qobject_cast<QScrollBar*>(child)) { | ||||
150 | auto terminal = qobject_cast<TerminalDisplay*>(child->parent()); | ||||
151 | terminal->setFocus(Qt::OtherFocusReason); | ||||
152 | } else if (qobject_cast<QSplitterHandle*>(child)) { | ||||
153 | auto targetSplitter = qobject_cast<QSplitter*>(child->parent()); | ||||
154 | auto terminal = qobject_cast<TerminalDisplay*>(targetSplitter->widget(0)); | ||||
hindenburg: try not to shadow another variable here | |||||
155 | terminal->setFocus(Qt::OtherFocusReason); | ||||
156 | } | ||||
222 | } | 157 | } | ||
223 | 158 | | |||
224 | void ViewSplitter::activatePreviousContainer() | 159 | void ViewSplitter::focusUp() | ||
225 | { | 160 | { | ||
226 | TabbedViewContainer *active = activeContainer(); | 161 | handleFocusDirection(Qt::Vertical, -1); | ||
227 | 162 | } | |||
228 | int index = _containers.indexOf(active); | | |||
229 | 163 | | |||
230 | if (index == 0) { | 164 | void ViewSplitter::focusDown() | ||
231 | index = _containers.count() - 1; | 165 | { | ||
232 | } else { | 166 | handleFocusDirection(Qt::Vertical, +1); | ||
233 | index--; | | |||
234 | } | 167 | } | ||
235 | 168 | | |||
236 | setActiveContainer(_containers.at(index)); | 169 | void ViewSplitter::focusLeft() | ||
170 | { | ||||
171 | handleFocusDirection(Qt::Horizontal, -1); | ||||
237 | } | 172 | } | ||
238 | 173 | | |||
239 | void ViewSplitter::setActiveContainer(TabbedViewContainer *container) | 174 | void ViewSplitter::focusRight() | ||
240 | { | 175 | { | ||
241 | QWidget *activeView = container->currentWidget(); | 176 | handleFocusDirection(Qt::Horizontal, +1); | ||
177 | } | ||||
242 | 178 | | |||
243 | if (activeView != nullptr) { | 179 | TerminalDisplay *ViewSplitter::activeTerminalDisplay() const | ||
244 | activeView->setFocus(Qt::OtherFocusReason); | 180 | { | ||
181 | auto focusedWidget = qobject_cast<TerminalDisplay*>(focusWidget()); | ||||
182 | return focusedWidget ? focusedWidget : findChild<TerminalDisplay*>(); | ||||
245 | } | 183 | } | ||
184 | | ||||
185 | void ViewSplitter::maximizeCurrentTerminal() | ||||
186 | { | ||||
187 | handleMinimizeMaximize(true); | ||||
246 | } | 188 | } | ||
247 | 189 | | |||
248 | TabbedViewContainer *ViewSplitter::activeContainer() const | 190 | void ViewSplitter::restoreOtherTerminals() | ||
249 | { | 191 | { | ||
250 | if (QWidget *focusW = focusWidget()) { | 192 | handleMinimizeMaximize(false); | ||
251 | TabbedViewContainer *focusContainer = nullptr; | 193 | } | ||
252 | 194 | | |||
253 | while (focusW != nullptr) { | 195 | void ViewSplitter::handleMinimizeMaximize(bool maximize) | ||
254 | foreach (TabbedViewContainer *container, _containers) { | 196 | { | ||
255 | if (container == focusW) { | 197 | auto viewSplitter = getToplevelSplitter(); | ||
256 | focusContainer = container; | 198 | auto terminalDisplays = viewSplitter->findChildren<TerminalDisplay*>(); | ||
257 | break; | 199 | auto currentActiveTerminal = viewSplitter->activeTerminalDisplay(); | ||
200 | auto method = maximize ? &QWidget::hide : &QWidget::show; | ||||
201 | for(auto terminal : terminalDisplays) { | ||||
202 | if (Q_LIKELY(currentActiveTerminal != terminal)) { | ||||
203 | (terminal->*method)(); | ||||
258 | } | 204 | } | ||
259 | } | 205 | } | ||
260 | focusW = focusW->parentWidget(); | | |||
261 | } | 206 | } | ||
262 | 207 | | |||
263 | if (focusContainer != nullptr) { | 208 | ViewSplitter *ViewSplitter::getToplevelSplitter() | ||
264 | return focusContainer; | 209 | { | ||
210 | ViewSplitter *current = this; | ||||
211 | while(qobject_cast<ViewSplitter*>(current->parentWidget())) { | ||||
212 | current = qobject_cast<ViewSplitter*>(current->parentWidget()); | ||||
265 | } | 213 | } | ||
214 | return current; | ||||
266 | } | 215 | } | ||
267 | 216 | | |||
268 | QList<ViewSplitter *> splitters = findChildren<ViewSplitter *>(); | 217 | QList<TerminalDisplay*> ViewSplitter::getTerminalDisplays() { | ||
218 | auto terminals = QList<TerminalDisplay*>(); | ||||
219 | auto count = this->count(); | ||||
220 | for(int i=0; i < count; ++i) { | ||||
221 | auto widget = this->widget(i); | ||||
269 | 222 | | |||
270 | if (!splitters.isEmpty()) { | 223 | auto terminal = qobject_cast<TerminalDisplay*>(widget); | ||
271 | return splitters.last()->activeContainer(); | 224 | if (terminal != nullptr) { | ||
272 | } else { | 225 | terminals.append(terminal); | ||
273 | if (!_containers.isEmpty()) { | | |||
274 | return _containers.last(); | | |||
275 | } else { | | |||
276 | return nullptr; | | |||
277 | } | 226 | } | ||
227 | auto splitter = qobject_cast<ViewSplitter*>(widget); | ||||
228 | if (splitter != nullptr) { | ||||
229 | terminals.append(splitter->getTerminalDisplays()); | ||||
278 | } | 230 | } | ||
279 | } | 231 | } | ||
232 | | ||||
233 | return terminals; | ||||
234 | } |
try not to shadow another variable here