1 /*
2     SPDX-FileCopyrightText: 2007 Alexander Dymo <adymo@kdevelop.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "test_viewactivation.h"
8 
9 #include <QTest>
10 #include <QSignalSpy>
11 
12 #include <QListView>
13 #include <QTextEdit>
14 #include <QDockWidget>
15 #include <QFocusEvent>
16 #include <QStandardPaths>
17 
18 #include <sublime/view.h>
19 #include <sublime/area.h>
20 #include <sublime/controller.h>
21 #include <sublime/mainwindow.h>
22 #include <sublime/container.h>
23 #include <sublime/tooldocument.h>
24 
25 using namespace Sublime;
26 
27 template <class Widget>
28 class SpecialWidgetFactory: public SimpleToolWidgetFactory<Widget> {
29 public:
SpecialWidgetFactory(const QString & id)30     explicit SpecialWidgetFactory(const QString &id): SimpleToolWidgetFactory<Widget>(id) {}
create(ToolDocument * doc,QWidget * parent=nullptr)31     QWidget* create(ToolDocument *doc, QWidget *parent = nullptr) override
32     {
33         auto* w = new QWidget(parent);
34         auto *inner = new Widget(w);
35         inner->setObjectName(doc->title()+"_inner");
36         w->setObjectName(doc->title()+"_outer");
37         return w;
38     }
39 };
40 
initTestCase()41 void TestViewActivation::initTestCase()
42 {
43     QStandardPaths::setTestModeEnabled(true);
44     qRegisterMetaType<View*>("View*");
45 }
46 
init()47 void TestViewActivation::init()
48 {
49     controller = new Controller(this);
50     doc1 = new ToolDocument(QStringLiteral("doc1"), controller, new SimpleToolWidgetFactory<QListView>(QStringLiteral("doc1")));
51     //this document will create special widgets - QListView nested in QWidget
52     doc2 = new ToolDocument(QStringLiteral("doc2"), controller, new SpecialWidgetFactory<QListView>(QStringLiteral("doc2")));
53     doc3 = new ToolDocument(QStringLiteral("doc3"), controller, new SimpleToolWidgetFactory<QListView>(QStringLiteral("doc3")));
54     doc4 = new ToolDocument(QStringLiteral("doc4"), controller, new SimpleToolWidgetFactory<QListView>(QStringLiteral("doc4")));
55 
56     tool1 = new ToolDocument(QStringLiteral("tool1"), controller, new SimpleToolWidgetFactory<QListView>(QStringLiteral("tool1")));
57     tool2 = new ToolDocument(QStringLiteral("tool2"), controller, new SimpleToolWidgetFactory<QTextEdit>(QStringLiteral("tool2")));
58     tool3 = new ToolDocument(QStringLiteral("tool3"), controller, new SimpleToolWidgetFactory<QTextEdit>(QStringLiteral("tool3")));
59 
60     area = new Area(controller, QStringLiteral("Area"));
61 
62     view211 = doc1->createView();
63     view211->setObjectName(QStringLiteral("view211"));
64     area->addView(view211);
65     view212 = doc1->createView();
66     view212->setObjectName(QStringLiteral("view212"));
67     area->addView(view212);
68     view221 = doc2->createView();
69     area->addView(view221, view211, Qt::Vertical);
70     view231 = doc3->createView();
71     area->addView(view231, view221, Qt::Horizontal);
72     view241 = doc4->createView();
73     area->addView(view241, view212, Qt::Vertical);
74     viewT11 = tool1->createView();
75     area->addToolView(viewT11, Sublime::Bottom);
76     viewT21 = tool2->createView();
77     area->addToolView(viewT21, Sublime::Right);
78     viewT31 = tool3->createView();
79     area->addToolView(viewT31, Sublime::Top);
80     viewT32 = tool3->createView();
81     area->addToolView(viewT32, Sublime::Top);
82 }
83 
cleanup()84 void TestViewActivation::cleanup()
85 {
86     delete controller;
87 }
88 
signalsOnViewCreationAndDeletion()89 void TestViewActivation::signalsOnViewCreationAndDeletion()
90 {
91     auto *controller = new Controller(this);
92     auto* doc1 = new ToolDocument(QStringLiteral("doc1"), controller, new SimpleToolWidgetFactory<QListView>(QStringLiteral("doc1")));
93     Area *area = new Area(controller, QStringLiteral("Area"));
94 
95     QSignalSpy spy(controller, SIGNAL(viewAdded(Sublime::View*)));
96     View *v = doc1->createView();
97     area->addView(v);
98     QCOMPARE(spy.count(), 1);
99 
100     QSignalSpy spy2(controller, SIGNAL(aboutToRemoveView(Sublime::View*)));
101     area->removeView(v);
102     QCOMPARE(spy2.count(), 1);
103 
104     QSignalSpy spy3(controller, SIGNAL(toolViewAdded(Sublime::View*)));
105     v = doc1->createView();
106     area->addToolView(v, Sublime::Bottom);
107     QCOMPARE(spy3.count(), 1);
108 
109     QSignalSpy spy4(controller, SIGNAL(aboutToRemoveToolView(Sublime::View*)));
110     area->removeToolView(v);
111     QCOMPARE(spy4.count(), 1);
112 
113     delete controller;
114 }
115 
viewActivation()116 void TestViewActivation::viewActivation()
117 {
118     auto* mw = new MainWindow(controller);
119     controller->addDefaultArea(area); // Q_ASSERT without this.
120     controller->addMainWindow(mw);
121 
122     controller->showArea(area, mw);
123     //we should have an active view immediatelly after the area is shown
124     QCOMPARE(mw->activeView(), view211);
125 
126     //add some widgets that are not in layout
127     auto *breaker = new QTextEdit(mw);
128     breaker->setObjectName(QStringLiteral("breaker"));
129     auto *toolBreaker = new QTextEdit(mw);
130     toolBreaker->setObjectName(QStringLiteral("toolBreaker"));
131 
132     auto* dock = new QDockWidget(mw);
133     dock->setWidget(toolBreaker);
134     mw->addDockWidget(Qt::LeftDockWidgetArea, dock);
135 
136     QFocusEvent focusEvent(QEvent::FocusIn);
137     //now post events to the widgets and see if mainwindow has the right active views
138     //activate view
139     qApp->sendEvent(view212->widget(), &focusEvent);
140     QString failMsg = QStringLiteral("\nWas expecting %1 to be active but got %2").
141                       arg(view212->objectName(), mw->activeView()->objectName());
142     QVERIFY2(mw->activeView() == view212, failMsg.toLatin1().data());
143 
144     //activate tool view and check that both view and tool view are active
145     qApp->sendEvent(viewT31->widget(), &focusEvent);
146     QCOMPARE(mw->activeView(), view212);
147     QCOMPARE(mw->activeToolView(), viewT31);
148 
149     //active another view
150     qApp->sendEvent(view241->widget(), &focusEvent);
151     QCOMPARE(mw->activeView(), view241);
152     QCOMPARE(mw->activeToolView(), viewT31);
153 
154     //focus a widget not in the area
155     qApp->sendEvent(breaker, &focusEvent);
156     QCOMPARE(mw->activeView(), view241);
157     QCOMPARE(mw->activeToolView(), viewT31);
158 
159     //focus a dock not in the area
160     qApp->sendEvent(toolBreaker, &focusEvent);
161     QCOMPARE(mw->activeView(), view241);
162     QCOMPARE(mw->activeToolView(), viewT31);
163 
164     //focus inner widget for view221
165     auto *inner = mw->findChild<QListView*>(QStringLiteral("doc2_inner"));
166     QVERIFY(inner);
167     qApp->sendEvent(inner, &focusEvent);
168     QCOMPARE(mw->activeView(), view221);
169     QCOMPARE(mw->activeToolView(), viewT31);
170 }
171 
activationInMultipleMainWindows()172 void TestViewActivation::activationInMultipleMainWindows()
173 {
174     MainWindow mw(controller);
175     controller->showArea(area, &mw);
176     QCOMPARE(mw.activeView(), view211);
177 
178     //check that new mainwindow always have active view right after displaying area
179     MainWindow mw2(controller);
180     controller->showArea(area, &mw2);
181     QVERIFY(mw2.activeView());
182     QCOMPARE(mw2.activeView()->document(), doc1);
183 }
184 
activationAfterViewRemoval()185 void TestViewActivation::activationAfterViewRemoval()
186 {
187     MainWindow mw(controller);
188     controller->showArea(area, &mw);
189     QCOMPARE(mw.activeView(), view211);
190 
191     //check what happens if we remove a view which is not the only one in container
192     delete area->removeView(view211);
193     QCOMPARE(mw.activeView(), view212);
194 
195     //check what happens if we remove a view which is alone in container
196     mw.activateView(view231);
197     QCOMPARE(mw.activeView(), view231);
198     delete area->removeView(view231);
199     QCOMPARE(mw.activeView(), view221);
200 }
201 
activationAfterRemovalSimplestCase()202 void TestViewActivation::activationAfterRemovalSimplestCase()
203 {
204     //we don't have split views - just two views in one area index
205     MainWindow mw(controller);
206     Area *area = new Area(controller, QStringLiteral("Area"));
207     View *v1 = doc1->createView();
208     View *v2 = doc2->createView();
209     area->addView(v1);
210     area->addView(v2, v1);
211     controller->showArea(area, &mw);
212     mw.activateView(v2);
213 
214     //delete active view and check that previous is activated
215     delete area->removeView(v2);
216     QCOMPARE(mw.activeView(), v1);
217 }
218 
219 QTEST_MAIN(TestViewActivation)
220