Status: | Superseded |
---|---|
Proposed branch: | lp:~dandrader/unity8/miral |
Merge into: | lp:unity8 |
Diff against target: |
5243 lines (+2201/-1371) 41 files modified
CMakeLists.txt (+1/-1) debian/control (+3/-3) plugins/WindowManager/CMakeLists.txt (+3/-1) plugins/WindowManager/TopLevelSurfaceList.cpp (+0/-481) plugins/WindowManager/TopLevelSurfaceList.h (+0/-223) plugins/WindowManager/TopLevelWindowModel.cpp (+644/-0) plugins/WindowManager/TopLevelWindowModel.h (+258/-0) plugins/WindowManager/Window.cpp (+234/-0) plugins/WindowManager/Window.h (+161/-0) plugins/WindowManager/WindowManagerPlugin.cpp (+5/-2) qml/Components/InputMethod.qml (+5/-4) qml/Components/KeymapSwitcher.qml (+4/-1) qml/Shell.qml (+14/-6) qml/Stage/DecoratedWindow.qml (+1/-1) qml/Stage/FakeMaximizeDelegate.qml (+7/-7) qml/Stage/Stage.qml (+136/-119) qml/Stage/StagedFullscreenPolicy.qml (+4/-4) qml/Stage/TopLevelSurfaceRepeater.qml (+0/-67) qml/Stage/WindowDecoration.qml (+4/-5) qml/Stage/WindowedFullscreenPolicy.qml (+1/-1) tests/mocks/Unity/Application/ApplicationInfo.cpp (+45/-24) tests/mocks/Unity/Application/ApplicationInfo.h (+2/-2) tests/mocks/Unity/Application/ApplicationManager.cpp (+92/-61) tests/mocks/Unity/Application/ApplicationManager.h (+31/-4) tests/mocks/Unity/Application/CMakeLists.txt (+1/-1) tests/mocks/Unity/Application/MirSurface.cpp (+85/-98) tests/mocks/Unity/Application/MirSurface.h (+25/-27) tests/mocks/Unity/Application/MirSurfaceItem.cpp (+4/-4) tests/mocks/Unity/Application/MirSurfaceItem.h (+1/-2) tests/mocks/Unity/Application/MirSurfaceListModel.cpp (+12/-34) tests/mocks/Unity/Application/MirSurfaceListModel.h (+2/-7) tests/mocks/Unity/Application/SurfaceManager.cpp (+169/-26) tests/mocks/Unity/Application/SurfaceManager.h (+28/-10) tests/mocks/Unity/Application/plugin.cpp (+2/-27) tests/plugins/Unity/Launcher/launchermodeltest.cpp (+4/-0) tests/qmltests/Stage/ApplicationCheckBox.qml (+1/-1) tests/qmltests/Stage/tst_DesktopStage.qml (+50/-40) tests/qmltests/Stage/tst_PhoneStage.qml (+18/-12) tests/qmltests/Stage/tst_TabletStage.qml (+16/-10) tests/qmltests/tst_Shell.qml (+124/-50) tests/utils/modules/Unity/Test/UnityTestCase.qml (+4/-5) |
To merge this branch: | bzr merge lp:~dandrader/unity8/miral |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Michael Zanetti (community) | Needs Fixing | ||
Review via email: mp+309790@code.launchpad.net |
This proposal has been superseded by a proposal from 2016-11-23.
Commit message
Let the model deal with some window management decisions
eg: which window to focus, whether to change surface state
unity8 requests and reacts to changes in the model instead of applying them
Description of the change
Prereq-archive: ppa:ci-
For the upcoming, miral-powered, qtmir.
One notable introduction is the Window object, which is a sligthly higher level abstraction of a MirSurface (and a drop-in replacement for it) to be used by Stage.qml. Window always exists in a TopLevelWindowModel entry so that QML code no longer has to if(){}else{} for the existence of a MirSurface (because of the splash screen case)
* Are there any related MPs required for this MP to build/function as expected? Please list.
https:/
https:/
It's all in this silo: https:/
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable
* If you changed the UI, has there been a design review?
Not applicable
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2675
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2697
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2697
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michael Zanetti (mzanetti) wrote : | # |
* See a bunch of inline comments
* I think we should not give up on the differntiation between claimFocus() and requestFocus(). One of them says the stage wants to actually focus something *now* while the other indicates that the app requested focus on its own (or perhaps u-a-l did). IMO claimFocus() should not be dropped and still do the raisId() call on the model. requestfocus() instead should end up in the shell, and if the shell is ok with doing so, it should call claimFocus() itself.
* IMO you're exaggerating with the debug messages. unity8.
This has been a code review for the code up until the mocks. Still needs review for tests/mocks and especially in depth testing.
Michael Zanetti (mzanetti) wrote : | # |
Not yet sure if just an issue in the mocks or not, but this breaks:
- open an app
- click the minimize button in the window decoration
- click the app again in the launcher to restore it
- now click the minimize button again
=> button doesn't work any more
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
> * IMO you're exaggerating with the debug messages. unity8.
Reduced the logging output. Should be the way you prefer now.
Michael Zanetti (mzanetti) wrote : | # |
- have 3 apps (a, b and c), make them overlap a bit for better visibility
- focus a by clicking it
- focus b by clicking it
- focus c by clicking it
- minimize c
=> a will be focused
expected: after minimizing c, b should be focused
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
> * I think we should not give up on the differntiation between claimFocus() and requestFocus(). One of them says the stage wants to actually focus something*now* while the other indicates that the app requested focus on its own (or perhaps u-a-l did). IMO claimFocus() should not be dropped and still do the raisId() call on the model. requestfocus() instead should end up in the shell, and if the shell is ok with doing so, it should call claimFocus() itself.
What you are explaining effectively is that: leave things as they are
currently with regards to focus. Which means focus lives in unity8 and
miral has no say over it.
The whole point of this miral effort is exactly to move such window
management decisions down to Mir, as dictated by our high-profile
stakeholder.
On the bright side, focus requests made by unity8 should naturally
always be sensible (since it knows the situation pretty well) and
therefore miral should always swiftly comply. So in terms of end
results, those requests will work just like imperative assignments.
Focus is a central piece of window management and if miral is to do
anything useful it has to have a say in it. And we cannot have two
entities making decisions on focus (unity8 and miral) as it will
inevitably be racy and conflictive.
As for the origin of the focus change request (shell vs. application),
it doesn't matter from miral's standpoint. It will comply to either as
long the change is valid and makes sense. Eg.: it should never allow a
window blocked by a child modal dialog to be focused. So it's mostly
sanity checking. I don't see miral ignoring requests on a whim. :)
Besides, having two separate code paths would make unity8 code more
complex. Eg: Having both code that sets surface state and code that
responds to surface state changes. "If surface state change was done in
response my your own set(), do nothing, else, ponder and respond."
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2698
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> +int TopLevelWindowM
>> >+{
>> >+ if (candidateId > m_maxId) {
>> >+ return nextFreeId(1);
>> >+ } else {
>> >+ if (indexForId(
>> >+ // it's indeed free
>> >+ return candidateId;
>> >+ } else {
>> >+ return nextFreeId(
>> >+ }
>> >+ }
>> >+}
> In the very unlikely event that we'd run out of free ids, this would crash with a stack overflow. Think it makes sense to write this in a non-recursive manner and perhaps add some way to actually handle failures?
>
If we run out of free ids it's because there's already a bug somewhere.
There's no way there's a valid situation where we would have literally a
million windows lying around.
But a non-recursive version is indeed lighter-weight on resources if it
ever has to traverse a long stretch of taken ids. Done.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2699
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> +QString TopLevelWindowM
>> >+{
>> >+ QString str;
>> >+ for (int i = 0; i < m_windowModel.
>> >+ auto item = m_windowModel.
>> >+
>> >+ QString itemStr = QString(
>> >+ .arg(i)
>> >+ .arg(item.
>> >+ .arg((qintptr)
>> >+ .arg(item.
> it would be slightly more performant if you'd use QString:
>
Done.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> +public Q_SLOTS:
>> >+ /**
>> >+ * @brief Returns the surface at the given index
>> >+ *
>> >+ * It will be a nullptr if the application is still starting up and thus hasn't yet created
>> >+ * and drawn into a surface.
>> >+ *
>> >+ * Same as windowAt(
>> >+ */
>> >+ unity::
>> >+
>> >+ /**
>> >+ * @brief Returns the window at the given index
>> >+ *
>> >+ * Will always be valid
>> >+ */
>> >+ Window *windowAt(int index) const;
>> >+
>> >+ /**
>> >+ * @brief Returns the application at the given index
>> >+ */
>> >+ unity::
>> >+
>> >+ /**
>> >+ * @brief Returns the unique id of the element at the given index
>> >+ */
>> >+ int idAt(int index) const;
>> >+
>> >+ /**
>> >+ * @brief Returns the index where the row with the given id is located
>> >+ *
>> >+ * Returns -1 if there's no row with the given id.
>> >+ */
>> >+ int indexForId(int id) const;
> slots with return value are weird. All the above should be public Q_INVOKABLE instead IMO.
Which uglifies the header file. Not great either.
Done.
>
>> >+
>> >+ /**
>> >+ * @brief Raises the row with the given id to the top of the window stack (index == count-1)
>> >+ */
>> >+ void raiseId(int id);
> this makes sense as slot indeed
>
But it's only a slot so it can be called from QML. Made it a Q_INVOKABLE
as well to match the others.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> +Window::Window(int id)
>> >+ : QObject(nullptr)
>> >+ , m_id(id)
>> >+{
>> >+ DEBUG_MSG << "()";
>> >+ QQmlEngine:
> is there a reason why you can't use QObject's parent instead?
Laziness. Done.
>
> Would make sense in any case to parent the Window objects properly IMO
>
Yes, that's a nice safety net.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> +QString Window::toString() const
>> >+{
>> >+ if (surface()) {
>> >+ return QString(
>> >+ .arg((quintptr)
>> >+ .arg(id())
>> >+ .arg((quintptr)
>> >+ .arg(surface(
> same here... if this is only ever executed in certain debug modes, ok with me, but if this is executed in production code too, please use the single .arg(..., ..., ...) call for better performance.
>
Done.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> + Q_PROPERTY(
> gah I despise this overly excessive namespacing...
Me too! Nested namespaces are an eyesore.
> IMO you could just do "using namespace unity::
>
Not in a header file though. I think that's bad form. A .cpp file
#including this header would inadvertently get the
unity::
I'm also scared of doing namespace manipulations in Qt macros. That
might go wrong.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> + qRegisterMetaTy
> think it would make sense to qmlRegisterUncr
>
Not sure. qmlRegisterUncr
useful where the type is only intended for providing attached properties
or enum values."
Not the case here.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> - onMinimizeClicked: root.minimizeCl
>> >+ onMinimizeClicked: {
>> >+ if (applicationWin
>> >+ applicationWind
>> >+ }
>> >+ }
> this seems odd... why would you refactor DecoratedWindow to not directly operate on the data any more but instead pass everything outside with signals in a earlier branch, but now start introducing this somewhat messy behavior here again? IMO this should also be passed to outside to the Stage, and only the stage being in charge to actually execute things on the surfaces.
>
That's some old change that pre-dates that branch you mentioned.
Reverted.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> - function focusNext() {
> same as I mentioned earlier. I'm still a bit scared of dropping control over this from the qml part, but lets see how it works... At least now that the model is part of unity8 again, it seems easier to change that back or modify in yet another way if we need to.
>
This is a perfect case where it makes sense to delegate to the C++
model. Choosing the next window do be focused when the current one is
minimized is a bunch of imperative code (and WM policy) that fits better
in C++ and not mixed with animation code in QML.
Particularly now that the model knows everything (who's focused and the
position and state of all surfaces).
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2700
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> if (!shown && priv.mainStageD
>> >- priv.mainStageD
>> >+ priv.mainStageD
> ok... given that requestFocus() effectively does claimFocus() now, this change totally makes sense... but we're losing the distinction between "the stage wants to focus something" and "an app requests focus by itsel (or something else than the stage)".
>
> Can you think of a way to keep that separation perhaps?
>
Heh, I actually think it is a good thing to handle them the same way (as
I said in some other comment).
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> + function updateQmlFocusF
>> >+ if (model.
>> > claimFocus();
>> >+ priv.focusedApp
>> >+ //} else if (priv.focusedAp
>> >+ // priv.focusedApp
> leftover?
>
Yes, removed.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 12:29, Michael Zanetti wrote:
>> + onMaximizeHoriz
>> >+ if (appDelegate.
>> >+ appDelegate.
>> >+ else
>> >+ appDelegate.
>> >+ }
> {} missing
>
>> >+ onMaximizeVerti
>> >+ if (appDelegate.
>> >+ appDelegate.
>> >+ else
>> >+ appDelegate.
>> >+ }
> {} missing
>
Not sure if that's in our coding style but ok, added.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 14:16, Michael Zanetti wrote:
> Review: Needs Fixing
>
> Not yet sure if just an issue in the mocks or not, but this breaks:
>
> - open an app
> - click the minimize button in the window decoration
> - click the app again in the launcher to restore it
> - now click the minimize button again
> => button doesn't work any more
Bug in the mock. Works with qtmir.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2705
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2706
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michał Sawicz (saviq) wrote : | # |
>>>> - priv.mainStageD
>>>> >> >+ priv.mainStageD
>> > ok... given that requestFocus() effectively does claimFocus() now, this change totally makes sense... but we're losing the distinction between "the stage wants to focus something" and "an app requests focus by itsel (or something else than the stage)".
>> >
>> > Can you think of a way to keep that separation perhaps?
>> >
> Heh, I actually think it is a good thing to handle them the same way (as
> I said in some other comment).
How does focus stealing prevention come into play here? I believe the
plan was to only grant focus to apps that send along a ~MirCookie based
on a timestamp. I believe it's described here:
Michael Zanetti (mzanetti) wrote : | # |
On 18.11.2016 21:09, Daniel d'Andrada wrote:
> On 18/11/2016 12:29, Michael Zanetti wrote:
>>> + qRegisterMetaTy
>> think it would make sense to qmlRegisterUncr
>>
>
> Not sure. qmlRegisterUncr
> useful where the type is only intended for providing attached properties
> or enum values."
> Not the case here.
I'm more thinking about being able to access the Window type in QML
altogether. Not really about providing attached properties etc, but it
could make sense to do pass the Window object to somewhere in QML and
access its property there without having access to the model.
Michael Zanetti (mzanetti) wrote : | # |
> On 18/11/2016 12:29, Michael Zanetti wrote:
> > * I think we should not give up on the differntiation between claimFocus()
> and requestFocus(). One of them says the stage wants to actually focus
> something*now* while the other indicates that the app requested focus on its
> own (or perhaps u-a-l did). IMO claimFocus() should not be dropped and still
> do the raisId() call on the model. requestfocus() instead should end up in the
> shell, and if the shell is ok with doing so, it should call claimFocus()
> itself.
>
> What you are explaining effectively is that: leave things as they are
> currently with regards to focus. Which means focus lives in unity8 and
> miral has no say over it.
>
> The whole point of this miral effort is exactly to move such window
> management decisions down to Mir, as dictated by our high-profile
> stakeholder.
There's a bunch of other stakeholders though that want to have it pixel perfect when it comes to animations and transitions. I am not saying miral should not have any say, but miral should send a requestFocus() to the shell and the shell will do it.
What I'm saying is that miral can tell unity to change the focus, and unity will follow that (unless it's in the middle of a transition or something). What you're proposing is that miral has all the say on when to change focus, and unity8 cannot do any course correction if the moment is bad.
> Focus is a central piece of window management and if miral is to do
> anything useful it has to have a say in it. And we cannot have two
> entities making decisions on focus (unity8 and miral) as it will
> inevitably be racy and conflictive.
which is exactly what we have now... unity8 thinks it is in charge, but miral can just jump in and trash it. I'm really just trying to avoid that.
>
> As for the origin of the focus change request (shell vs. application),
> it doesn't matter from miral's standpoint. It will comply to either as
> long the change is valid and makes sense. Eg.: it should never allow a
> window blocked by a child modal dialog to be focused. So it's mostly
> sanity checking. I don't see miral ignoring requests on a whim. :)
>
> Besides, having two separate code paths would make unity8 code more
> complex. Eg: Having both code that sets surface state and code that
> responds to surface state changes. "If surface state change was done in
> response my your own set(), do nothing, else, ponder and respond."
And I'm also not talking about 2 separate code paths. I'm saying the one code path should go through unity8 and not just manipulate the model at random.
Therefore I think only unity8 should manipulate the model, taking miral as consultant on how to manipulate it. This current approach is really the one that creates multiple entities fighting over the state of the model.
Daniel d'Andrada (dandrader) wrote : | # |
On 18/11/2016 14:21, Michael Zanetti wrote:
> Review: Needs Fixing
>
> - have 3 apps (a, b and c), make them overlap a bit for better visibility
> - focus a by clicking it
> - focus b by clicking it
> - focus c by clicking it
> - minimize c
> => a will be focused
>
> expected: after minimizing c, b should be focused
Can't reproduce in "make tryDesktopStage". Need more specific steps
(like exact test and apps launched).
Daniel d'Andrada (dandrader) wrote : | # |
On 21/11/2016 07:27, Michael Zanetti wrote:
>
> On 18.11.2016 21:09, Daniel d'Andrada wrote:
>> On 18/11/2016 12:29, Michael Zanetti wrote:
>>>> + qRegisterMetaTy
>>> think it would make sense to qmlRegisterUncr
>>>
>> Not sure. qmlRegisterUncr
>> useful where the type is only intended for providing attached properties
>> or enum values."
>> Not the case here.
> I'm more thinking about being able to access the Window type in QML
> altogether. Not really about providing attached properties etc, but it
> could make sense to do pass the Window object to somewhere in QML and
> access its property there without having access to the model.
>
>
This already happens in Stage.qml:
"""
x: model.window.
y: model.window.
"""
Looks like qmlRegisterUncr
you only need it for Window.foo type of constructs.
Daniel d'Andrada (dandrader) wrote : | # |
On 21/11/2016 06:48, Michał Sawicz wrote:
>>>>> - priv.mainStageD
>>>>>>>> + priv.mainStageD
>>>> ok... given that requestFocus() effectively does claimFocus() now, this change totally makes sense... but we're losing the distinction between "the stage wants to focus something" and "an app requests focus by itsel (or something else than the stage)".
>>>>
>>>> Can you think of a way to keep that separation perhaps?
>>>>
>> Heh, I actually think it is a good thing to handle them the same way (as
>> I said in some other comment).
> How does focus stealing prevention come into play here? I believe the
> plan was to only grant focus to apps that send along a ~MirCookie based
> on a timestamp. I believe it's described here:
>
> https:/
>
>
This happens at a level lower than unity8. All unity8 knows and cares
about is that some surface in the model got focus.
Michael Zanetti (mzanetti) wrote : | # |
On 21.11.2016 12:09, Daniel d'Andrada wrote:
> On 21/11/2016 07:27, Michael Zanetti wrote:
>>
>> On 18.11.2016 21:09, Daniel d'Andrada wrote:
>>> On 18/11/2016 12:29, Michael Zanetti wrote:
>>>>> + qRegisterMetaTy
>>>> think it would make sense to qmlRegisterUncr
>>>>
>>> Not sure. qmlRegisterUncr
>>> useful where the type is only intended for providing attached properties
>>> or enum values."
>>> Not the case here.
>> I'm more thinking about being able to access the Window type in QML
>> altogether. Not really about providing attached properties etc, but it
>> could make sense to do pass the Window object to somewhere in QML and
>> access its property there without having access to the model.
>>
>>
> This already happens in Stage.qml:
>
> """
> x: model.window.
> y: model.window.
> """
>
> Looks like qmlRegisterUncr
> you only need it for Window.foo type of constructs.
Hmm, ok... fine then.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2707
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michael Zanetti (mzanetti) wrote : | # |
> On 18/11/2016 14:21, Michael Zanetti wrote:
> > Review: Needs Fixing
> >
> > - have 3 apps (a, b and c), make them overlap a bit for better visibility
> > - focus a by clicking it
> > - focus b by clicking it
> > - focus c by clicking it
> > - minimize c
> > => a will be focused
> >
> > expected: after minimizing c, b should be focused
>
>
> Can't reproduce in "make tryDesktopStage". Need more specific steps
> (like exact test and apps launched).
Hmm... I can't reproduce it any more either after your latest changes.
Michael Zanetti (mzanetti) wrote : | # |
> On 18/11/2016 14:16, Michael Zanetti wrote:
> > Review: Needs Fixing
> >
> > Not yet sure if just an issue in the mocks or not, but this breaks:
> >
> > - open an app
> > - click the minimize button in the window decoration
> > - click the app again in the launcher to restore it
> > - now click the minimize button again
> > => button doesn't work any more
>
> Bug in the mock. Works with qtmir.
This is really why I think we need to move all those models to unity8, put unity8 in control of them and mock things at a lower level. All our tests regarding window management are just useless right now... they can perfectly pass while the real thing can be broken really badly.
Daniel d'Andrada (dandrader) wrote : | # |
On 21/11/2016 10:08, Michael Zanetti wrote:
>> On 18/11/2016 14:16, Michael Zanetti wrote:
>>> Review: Needs Fixing
>>>
>>> Not yet sure if just an issue in the mocks or not, but this breaks:
>>>
>>> - open an app
>>> - click the minimize button in the window decoration
>>> - click the app again in the launcher to restore it
>>> - now click the minimize button again
>>> => button doesn't work any more
>> Bug in the mock. Works with qtmir.
> This is really why I think we need to move all those models to unity8, put unity8 in control of them and mock things at a lower level. All our tests regarding window management are just useless right now... they can perfectly pass while the real thing can be broken really badly.
Window management policy things like "if currently focused window gets
minimized, focus the most recently focused window that is not hidden or
minimized" will now belong to a lower level (miral) and as such they get
mock implementations in our tests.
Thus qml tests that strictly check window management policy will indeed
get redundant. Test of this nature now belong to miral's own test suite.
Michael Zanetti (mzanetti) wrote : | # |
> On 21/11/2016 10:08, Michael Zanetti wrote:
> >> On 18/11/2016 14:16, Michael Zanetti wrote:
> >>> Review: Needs Fixing
> >>>
> >>> Not yet sure if just an issue in the mocks or not, but this breaks:
> >>>
> >>> - open an app
> >>> - click the minimize button in the window decoration
> >>> - click the app again in the launcher to restore it
> >>> - now click the minimize button again
> >>> => button doesn't work any more
> >> Bug in the mock. Works with qtmir.
> > This is really why I think we need to move all those models to unity8, put
> unity8 in control of them and mock things at a lower level. All our tests
> regarding window management are just useless right now... they can perfectly
> pass while the real thing can be broken really badly.
>
>
> Window management policy things like "if currently focused window gets
> minimized, focus the most recently focused window that is not hidden or
> minimized" will now belong to a lower level (miral) and as such they get
> mock implementations in our tests.
>
> Thus qml tests that strictly check window management policy will indeed
> get redundant. Test of this nature now belong to miral's own test suite.
Right, but we would still need tests that make sure that the QML part works together with the model as we expect it. And right now we don't have such tests... Anyhow, this is a different discussion than this particular merge proposal. It just once again popped up here. Also this isn't a new problem, we already had this since forever, but as long as we only had the one ApplicationManager model which would not even do much except moving things when unity8 said so, it was still easy enough to keep the mock close enough to the real thing. Now this seems to grow out of a manageable size.
Gerry Boland (gerboland) wrote : | # |
> > On 18/11/2016 12:29, Michael Zanetti wrote:
> > > * I think we should not give up on the differntiation between claimFocus()
> > and requestFocus(). One of them says the stage wants to actually focus
> > something*now* while the other indicates that the app requested focus on
> its
> > own (or perhaps u-a-l did). IMO claimFocus() should not be dropped and still
> > do the raisId() call on the model. requestfocus() instead should end up in
> the
> > shell, and if the shell is ok with doing so, it should call claimFocus()
> > itself.
> >
> > What you are explaining effectively is that: leave things as they are
> > currently with regards to focus. Which means focus lives in unity8 and
> > miral has no say over it.
> >
> > The whole point of this miral effort is exactly to move such window
> > management decisions down to Mir, as dictated by our high-profile
> > stakeholder.
>
> There's a bunch of other stakeholders though that want to have it pixel
> perfect when it comes to animations and transitions. I am not saying miral
> should not have any say, but miral should send a requestFocus() to the shell
> and the shell will do it.
> What I'm saying is that miral can tell unity to change the focus, and unity
> will follow that (unless it's in the middle of a transition or something).
> What you're proposing is that miral has all the say on when to change focus,
> and unity8 cannot do any course correction if the moment is bad.
>
> > Focus is a central piece of window management and if miral is to do
> > anything useful it has to have a say in it. And we cannot have two
> > entities making decisions on focus (unity8 and miral) as it will
> > inevitably be racy and conflictive.
>
> which is exactly what we have now... unity8 thinks it is in charge, but miral
> can just jump in and trash it. I'm really just trying to avoid that.
>
> >
> > As for the origin of the focus change request (shell vs. application),
> > it doesn't matter from miral's standpoint. It will comply to either as
> > long the change is valid and makes sense. Eg.: it should never allow a
> > window blocked by a child modal dialog to be focused. So it's mostly
> > sanity checking. I don't see miral ignoring requests on a whim. :)
> >
> > Besides, having two separate code paths would make unity8 code more
> > complex. Eg: Having both code that sets surface state and code that
> > responds to surface state changes. "If surface state change was done in
> > response my your own set(), do nothing, else, ponder and respond."
>
> And I'm also not talking about 2 separate code paths. I'm saying the one code
> path should go through unity8 and not just manipulate the model at random.
>
> Therefore I think only unity8 should manipulate the model, taking miral as
> consultant on how to manipulate it. This current approach is really the one
> that creates multiple entities fighting over the state of the model.
This is a valid concern. The model must only have one owner. I think it safest that Unity8 has total ownership of the model and uses MirAL's recommendations only when it can.
MirAL has no concept of intermediary-states (like transitions/
- 2703. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2708
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2709
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2711
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2704. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
Michael Zanetti (mzanetti) wrote : | # |
Thanks a lot for the activate() thing. Makes me feel much more comfy with the whole thing.
One minor thing (see inline comments) which I missed last time.
I'll give it another test with the latest changes, but it looks like we're getting close to an approval now.
Daniel d'Andrada (dandrader) wrote : | # |
On 23/11/2016 09:24, Michael Zanetti wrote:
>> readonly property Item clientAreaItem: applicationWindow
>> >
>> >+ readonly property real contentX: applicationWindow.x
>> >+ readonly property real contentY: applicationWindow.y
> afaict, you're only using that for knowing how much to add/remove for the decoration height. There is already a property "decorationHeight" you can use so this would be not needed, esp as applicationWindow.y will always be 0 at this point too.
>
Actually I can just use the existing clientAreaItem property right above
them. Fixed.
Daniel d'Andrada (dandrader) wrote : | # |
On 23/11/2016 09:24, Michael Zanetti wrote:
> Qt.point(
>
> should be enough
I would rather not assume that decorations only affect the Y coordinate.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2712
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2712
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2705. By Andrea Cimitan
-
Fix white text (LP: #1644468)
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2706. By Andrea Cimitan
-
Make MascotLoader in CardCreator flat
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2707. By Andrea Cimitan
-
Make the remaining UbuntuShape flat
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2708. By Andrea Cimitan
-
added dropshadow from panel indicators to dash page header extra panel, so there is right shadow when the extra panel is narrow
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2709. By Andrea Cimitan
-
Look up for expandable template dash category flag
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2710. By Albert Astals Cid
-
Fix PreviewProgress implicitHeight
We actually want this to be the height of the progressbar that we fix so use that and not the implicitHeight (LP: #1641943)
Approved by: Andrea Cimitan, Unity8 CI Bot
- 2711. By Andrea Azzarone
-
Set Mir.cursorName to "grabbing" on first mouse press on a window decoration. Don't wait for press+motion. (LP: #1618078)
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2712. By Andrea Azzarone
-
Do not show a divider between each quicklist entry but just between each section, similar to how unity7 does. (LP: #1637478)
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2713. By Daniel d'Andrada
-
There's no need for WindowDecoration to access the appDelegate
It damages modularity.
Approved by: Michael Zanetti, Unity8 CI Bot
- 2714. By Brian Douglass
-
Added a setting to enable/disable the indicator dropdown menu.
Approved by: Michael Zanetti, Unity8 CI Bot
- 2715. By Michael Zanetti
-
disable spread interaction while locked (LP: #1641578)
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2716. By Olivier Tilloy
-
Remove dependency on transitional package. (LP: #1583079)
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2717. By Lukáš Tinkl
-
Enable brightness (laptop backlight) handling on desktop/laptop PCs (LP: #1595947)
Approved by: Michael Zanetti, Unity8 CI Bot
- 2718. By Andrea Azzarone
-
Implement launcher tooltips.
Approved by: Michał Sawicz
- 2719. By Albert Astals Cid
-
Adapt to dummy notification being gone
and warning fix as bonus
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2720. By Albert Astals Cid
-
Fix autopilot test_lock_screen tests
Approved by: Andrea Cimitan, Unity8 CI Bot
- 2721. By Albert Astals Cid
-
Autopilot: Add more applications to the list for wider screens
Otherwise the test was failing because available_
applications was too short Approved by: Andrea Cimitan, Unity8 CI Bot
- 2722. By Albert Astals Cid
-
Fix autopilot DashHelperTestC
ase.test_ search Approved by: Andrea Cimitan, Unity8 CI Bot
- 2723. By Albert Astals Cid
-
Also install the Screens mock
This way we can run some more autopilot tests in X11
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2724. By Albert Astals Cid
-
Give default value to gu-px size
Fixes some of the autopilot greeter tests when run under X11
Approved by: Andrea Cimitan, Unity8 CI Bot
- 2725. By CI Train Bot Account
-
Releasing 8.15+17.
04.20161129- 0ubuntu1 - 2726. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
- 2727. By Lukáš Tinkl
-
Fix the Super key not invoking the dash scope home (LP: #1607427)
Approved by: Daniel d'Andrada, Unity8 CI Bot
- 2728. By Albert Astals Cid
-
Add the Wsuggest-override flag to gcc
While at it mark system includes as such so we don't get warnings we can not fix
Approved by: Michael Zanetti, Unity8 CI Bot
- 2729. By Albert Astals Cid
-
Add support for compiler sanitizers via ECM
- 2730. By Albert Astals Cid
-
Use timeStep as delay time
Passing iterations / speed didn't make much sense since that parameter is a delay in milliseconds and the default parameters would give a value of 5 / units.gu(10) that is smaller than 1 millisecond.
Qt 5.7 calculation for velocity was very unhappy if we moved things so fast in less than 1ms and ignored the movements, so this also makes tests pass on Qt 5.7 (LP: #1642919)
Approved by: Josh Arenson, Unity8 CI Bot
- 2731. By Michael Zanetti
-
Add the ApplicationDrawer
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2732. By Michael Zanetti
-
tune right edge push
make it less intrusive when accidentally hitting the edge with the mouse
tweak visuals for the mouse case (LP: #1646094)Approved by: Unity8 CI Bot
- 2733. By Michael Zanetti
-
improve close button visiblity when hovering with the mouse
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2734. By Albert Astals Cid
-
Bring back fix for 1517830
Now with autotest \o/ (LP: #1517830)
Approved by: Andrea Cimitan, Unity8 CI Bot
- 2735. By Daniel d'Andrada
-
Fix "make tryApplicationW
indow" No surface was showing up on the screen
Also remove outdated button (feature is no longer there)Approved by: Albert Astals Cid, Unity8 CI Bot
- 2736. By Albert Astals Cid
-
Fix compile warnings in mocks
Approved by: Daniel d'Andrada, Unity8 CI Bot
- 2737. By Josh Arenson
-
Enable the greeter to remember which session the user last logged into
This also fixes a small issue with how the default session was handled. (LP: #1631365)
Approved by: Albert Astals Cid, Unity8 CI Bot
- 2738. By Daniel d'Andrada
-
Take save/restore functions out of WindowResizeArea
They've no relationship with resizing whatsoever.
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2739. By Michael Zanetti
-
Update virtual touchpad visuals and add a tutorial. (LP: #1585220)
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2740. By Albert Astals Cid
-
Do not hide panel when launching an application if the mouse is on the panel
Need Functions.
itemUnderMouse because MouseArea. containsMouse returns true when tapping (i.e. no mouse used) on it. Unfortunately the QML testlib do not set the proper value when issueing a mouseMove so i can't add a test that proofs it works, i'll try to propose something upstream and then add the test at a later MR (LP: #1591311)
Approved by: Lukáš Tinkl, Unity8 CI Bot
- 2741. By Pete Woods
-
MenuItemFactory: Add subtitle support to SwitchItem widget
Approved by: Marco Trevisan (Treviño), Michał Sawicz, Unity8 CI Bot
- 2742. By CI Train Bot Account
-
Releasing 8.15+17.
04.20161207. 1-0ubuntu1 - 2743. By Daniel d'Andrada
-
Let the model deal with some window management decisions
eg: which window to focus, whether to change surface state
unit8 requests and reacts to changes in the model instead of applying them
Unmerged revisions
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-10-24 11:34:08 +0000 |
3 | +++ CMakeLists.txt 2016-11-23 15:15:21 +0000 |
4 | @@ -57,7 +57,7 @@ |
5 | find_package(Qt5Concurrent 5.4 REQUIRED) |
6 | find_package(Qt5Sql 5.4 REQUIRED) |
7 | |
8 | -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=22) |
9 | +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=23) |
10 | pkg_check_modules(GEONAMES REQUIRED geonames>=0.2) |
11 | pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) |
12 | pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) |
13 | |
14 | === modified file 'debian/control' |
15 | --- debian/control 2016-11-16 10:44:00 +0000 |
16 | +++ debian/control 2016-11-23 15:15:21 +0000 |
17 | @@ -38,7 +38,7 @@ |
18 | libubuntugestures5-private-dev (>= 1.3.2030), |
19 | libudev-dev, |
20 | libudm-common-dev, |
21 | - libunity-api-dev (>= 7.119), |
22 | + libunity-api-dev (>= 7.120), |
23 | libusermetricsoutput1-dev, |
24 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
25 | libx11-dev[!arm64 !armhf], |
26 | @@ -159,7 +159,7 @@ |
27 | qttranslations5-l10n, |
28 | ubuntu-thumbnailer-impl-0, |
29 | ubuntu-wallpapers, |
30 | - unity-application-impl-22, |
31 | + unity-application-impl-23, |
32 | unity-notifications-impl-3, |
33 | unity-plugin-scopes | unity-scopes-impl, |
34 | unity-scopes-impl-12, |
35 | @@ -205,7 +205,7 @@ |
36 | Depends: ${misc:Depends}, |
37 | ${shlibs:Depends}, |
38 | Provides: unity-application-impl, |
39 | - unity-application-impl-22, |
40 | + unity-application-impl-23, |
41 | Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1) |
42 | Description: Fake environment for running Unity 8 shell |
43 | Provides fake implementations of some QML modules used by Unity 8 shell |
44 | |
45 | === modified file 'plugins/WindowManager/CMakeLists.txt' |
46 | --- plugins/WindowManager/CMakeLists.txt 2016-06-20 09:43:38 +0000 |
47 | +++ plugins/WindowManager/CMakeLists.txt 2016-11-23 15:15:21 +0000 |
48 | @@ -1,10 +1,12 @@ |
49 | set(WINDOWMANAGER_SRC |
50 | - TopLevelSurfaceList.cpp |
51 | + TopLevelWindowModel.cpp |
52 | + Window.cpp |
53 | WindowManagerPlugin.cpp |
54 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
55 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h |
56 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h |
57 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h |
58 | + ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h |
59 | ) |
60 | |
61 | add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC}) |
62 | |
63 | === removed file 'plugins/WindowManager/TopLevelSurfaceList.cpp' |
64 | --- plugins/WindowManager/TopLevelSurfaceList.cpp 2016-09-14 15:02:01 +0000 |
65 | +++ plugins/WindowManager/TopLevelSurfaceList.cpp 1970-01-01 00:00:00 +0000 |
66 | @@ -1,481 +0,0 @@ |
67 | -/* |
68 | - * Copyright (C) 2016 Canonical, Ltd. |
69 | - * |
70 | - * This program is free software; you can redistribute it and/or modify |
71 | - * it under the terms of the GNU General Public License as published by |
72 | - * the Free Software Foundation; version 3. |
73 | - * |
74 | - * This program is distributed in the hope that it will be useful, |
75 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
76 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
77 | - * GNU General Public License for more details. |
78 | - * |
79 | - * You should have received a copy of the GNU General Public License |
80 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
81 | - */ |
82 | - |
83 | -#include "TopLevelSurfaceList.h" |
84 | - |
85 | -// unity-api |
86 | -#include <unity/shell/application/ApplicationInfoInterface.h> |
87 | -#include <unity/shell/application/MirSurfaceInterface.h> |
88 | -#include <unity/shell/application/MirSurfaceListInterface.h> |
89 | - |
90 | -#include <QMetaObject> |
91 | - |
92 | -Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist", QtDebugMsg) |
93 | - |
94 | -#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__ |
95 | - |
96 | -using namespace unity::shell::application; |
97 | - |
98 | -TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) : |
99 | - QAbstractListModel(parent) |
100 | -{ |
101 | - DEBUG_MSG << "()"; |
102 | -} |
103 | - |
104 | -TopLevelSurfaceList::~TopLevelSurfaceList() |
105 | -{ |
106 | - DEBUG_MSG << "()"; |
107 | -} |
108 | - |
109 | -int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const |
110 | -{ |
111 | - return !parent.isValid() ? m_surfaceList.size() : 0; |
112 | -} |
113 | - |
114 | -QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const |
115 | -{ |
116 | - if (index.row() < 0 || index.row() >= m_surfaceList.size()) |
117 | - return QVariant(); |
118 | - |
119 | - if (role == SurfaceRole) { |
120 | - MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface; |
121 | - return QVariant::fromValue(surface); |
122 | - } else if (role == ApplicationRole) { |
123 | - return QVariant::fromValue(m_surfaceList.at(index.row()).application); |
124 | - } else if (role == IdRole) { |
125 | - return QVariant::fromValue(m_surfaceList.at(index.row()).id); |
126 | - } else { |
127 | - return QVariant(); |
128 | - } |
129 | -} |
130 | - |
131 | -void TopLevelSurfaceList::raise(MirSurfaceInterface *surface) |
132 | -{ |
133 | - if (!surface) |
134 | - return; |
135 | - |
136 | - DEBUG_MSG << "(MirSurface[" << (void*)surface << "])"; |
137 | - |
138 | - int i = indexOf(surface); |
139 | - if (i != -1) { |
140 | - raiseId(m_surfaceList.at(i).id); |
141 | - } |
142 | -} |
143 | - |
144 | -void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application) |
145 | -{ |
146 | - DEBUG_MSG << "(" << application->appId() << ")"; |
147 | - |
148 | - appendSurfaceHelper(nullptr, application); |
149 | -} |
150 | - |
151 | -void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application) |
152 | -{ |
153 | - Q_ASSERT(surface != nullptr); |
154 | - |
155 | - bool filledPlaceholder = false; |
156 | - for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) { |
157 | - ModelEntry &entry = m_surfaceList[i]; |
158 | - if (entry.application == application && entry.surface == nullptr) { |
159 | - entry.surface = surface; |
160 | - connectSurface(surface); |
161 | - DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface |
162 | - << ", filling out placeholder. after: " << toString(); |
163 | - Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole); |
164 | - filledPlaceholder = true; |
165 | - } |
166 | - } |
167 | - |
168 | - if (!filledPlaceholder) { |
169 | - DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row"; |
170 | - appendSurfaceHelper(surface, application); |
171 | - } |
172 | -} |
173 | - |
174 | -void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application) |
175 | -{ |
176 | - if (m_modelState == IdleState) { |
177 | - m_modelState = InsertingState; |
178 | - beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/); |
179 | - } else { |
180 | - Q_ASSERT(m_modelState == ResettingState); |
181 | - // No point in signaling anything if we're resetting the whole model |
182 | - } |
183 | - |
184 | - int id = generateId(); |
185 | - m_surfaceList.append(ModelEntry(surface, application, id)); |
186 | - if (surface) { |
187 | - connectSurface(surface); |
188 | - } |
189 | - |
190 | - if (m_modelState == InsertingState) { |
191 | - endInsertRows(); |
192 | - Q_EMIT countChanged(); |
193 | - Q_EMIT listChanged(); |
194 | - m_modelState = IdleState; |
195 | - } |
196 | - |
197 | - DEBUG_MSG << " after " << toString(); |
198 | -} |
199 | - |
200 | -void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface) |
201 | -{ |
202 | - connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){ |
203 | - if (!live) { |
204 | - onSurfaceDied(surface); |
205 | - } |
206 | - }); |
207 | - connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); }); |
208 | -} |
209 | - |
210 | -void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface) |
211 | -{ |
212 | - int i = indexOf(surface); |
213 | - if (i == -1) { |
214 | - return; |
215 | - } |
216 | - |
217 | - auto application = m_surfaceList[i].application; |
218 | - |
219 | - // can't be starting if it already has a surface |
220 | - Q_ASSERT(application->state() != ApplicationInfoInterface::Starting); |
221 | - |
222 | - if (application->state() == ApplicationInfoInterface::Running) { |
223 | - m_surfaceList[i].removeOnceSurfaceDestroyed = true; |
224 | - } else { |
225 | - // assume it got killed by the out-of-memory daemon. |
226 | - // |
227 | - // So leave entry in the model and only remove its surface, so shell can display a screenshot |
228 | - // in its place. |
229 | - m_surfaceList[i].removeOnceSurfaceDestroyed = false; |
230 | - } |
231 | -} |
232 | - |
233 | -void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface) |
234 | -{ |
235 | - int i = indexOf(surface); |
236 | - if (i == -1) { |
237 | - return; |
238 | - } |
239 | - |
240 | - if (m_surfaceList[i].removeOnceSurfaceDestroyed) { |
241 | - removeAt(i); |
242 | - } else { |
243 | - m_surfaceList[i].surface = nullptr; |
244 | - Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole); |
245 | - DEBUG_MSG << " Removed surface from entry. After: " << toString(); |
246 | - } |
247 | -} |
248 | - |
249 | -void TopLevelSurfaceList::removeAt(int index) |
250 | -{ |
251 | - if (m_modelState == IdleState) { |
252 | - beginRemoveRows(QModelIndex(), index, index); |
253 | - m_modelState = RemovingState; |
254 | - } else { |
255 | - Q_ASSERT(m_modelState == ResettingState); |
256 | - // No point in signaling anything if we're resetting the whole model |
257 | - } |
258 | - |
259 | - m_surfaceList.removeAt(index); |
260 | - |
261 | - if (m_modelState == RemovingState) { |
262 | - endRemoveRows(); |
263 | - Q_EMIT countChanged(); |
264 | - Q_EMIT listChanged(); |
265 | - m_modelState = IdleState; |
266 | - } |
267 | - |
268 | - DEBUG_MSG << " after " << toString(); |
269 | -} |
270 | - |
271 | -int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface) |
272 | -{ |
273 | - for (int i = 0; i < m_surfaceList.count(); ++i) { |
274 | - if (m_surfaceList.at(i).surface == surface) { |
275 | - return i; |
276 | - } |
277 | - } |
278 | - return -1; |
279 | -} |
280 | - |
281 | -void TopLevelSurfaceList::move(int from, int to) |
282 | -{ |
283 | - if (from == to) return; |
284 | - DEBUG_MSG << " from=" << from << " to=" << to; |
285 | - |
286 | - if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) { |
287 | - QModelIndex parent; |
288 | - /* When moving an item down, the destination index needs to be incremented |
289 | - by one, as explained in the documentation: |
290 | - http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ |
291 | - |
292 | - Q_ASSERT(m_modelState == IdleState); |
293 | - m_modelState = MovingState; |
294 | - |
295 | - beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); |
296 | - m_surfaceList.move(from, to); |
297 | - endMoveRows(); |
298 | - Q_EMIT listChanged(); |
299 | - |
300 | - m_modelState = IdleState; |
301 | - |
302 | - DEBUG_MSG << " after " << toString(); |
303 | - } |
304 | -} |
305 | - |
306 | -MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const |
307 | -{ |
308 | - if (index >=0 && index < m_surfaceList.count()) { |
309 | - return m_surfaceList[index].surface; |
310 | - } else { |
311 | - return nullptr; |
312 | - } |
313 | -} |
314 | - |
315 | -ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const |
316 | -{ |
317 | - if (index >=0 && index < m_surfaceList.count()) { |
318 | - return m_surfaceList[index].application; |
319 | - } else { |
320 | - return nullptr; |
321 | - } |
322 | -} |
323 | - |
324 | -int TopLevelSurfaceList::idAt(int index) const |
325 | -{ |
326 | - if (index >=0 && index < m_surfaceList.count()) { |
327 | - return m_surfaceList[index].id; |
328 | - } else { |
329 | - return 0; |
330 | - } |
331 | -} |
332 | - |
333 | -int TopLevelSurfaceList::indexForId(int id) const |
334 | -{ |
335 | - for (int i = 0; i < m_surfaceList.count(); ++i) { |
336 | - if (m_surfaceList[i].id == id) { |
337 | - return i; |
338 | - } |
339 | - } |
340 | - return -1; |
341 | -} |
342 | - |
343 | -void TopLevelSurfaceList::doRaiseId(int id) |
344 | -{ |
345 | - int fromIndex = indexForId(id); |
346 | - if (fromIndex != -1) { |
347 | - move(fromIndex, 0 /* toIndex */); |
348 | - } |
349 | -} |
350 | - |
351 | -void TopLevelSurfaceList::raiseId(int id) |
352 | -{ |
353 | - if (m_modelState == IdleState) { |
354 | - DEBUG_MSG << "(id=" << id << ") - do it now."; |
355 | - doRaiseId(id); |
356 | - } else { |
357 | - DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop."; |
358 | - // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts |
359 | - // if we perform yet another model change straight away. |
360 | - // |
361 | - // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says |
362 | - // the index is definitely within bounds. |
363 | - QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id)); |
364 | - } |
365 | -} |
366 | - |
367 | -int TopLevelSurfaceList::generateId() |
368 | -{ |
369 | - int id = m_nextId; |
370 | - m_nextId = nextFreeId(m_nextId + 1); |
371 | - Q_EMIT nextIdChanged(); |
372 | - return id; |
373 | -} |
374 | - |
375 | -int TopLevelSurfaceList::nextFreeId(int candidateId) |
376 | -{ |
377 | - if (candidateId > m_maxId) { |
378 | - return nextFreeId(1); |
379 | - } else { |
380 | - if (indexForId(candidateId) == -1) { |
381 | - // it's indeed free |
382 | - return candidateId; |
383 | - } else { |
384 | - return nextFreeId(candidateId + 1); |
385 | - } |
386 | - } |
387 | -} |
388 | - |
389 | -QString TopLevelSurfaceList::toString() |
390 | -{ |
391 | - QString str; |
392 | - for (int i = 0; i < m_surfaceList.count(); ++i) { |
393 | - auto item = m_surfaceList.at(i); |
394 | - |
395 | - QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)") |
396 | - .arg(i) |
397 | - .arg(item.application->appId()) |
398 | - .arg((qintptr)item.surface, 0, 16) |
399 | - .arg(item.id); |
400 | - |
401 | - if (i > 0) { |
402 | - str.append(","); |
403 | - } |
404 | - str.append(itemStr); |
405 | - } |
406 | - return str; |
407 | -} |
408 | - |
409 | -void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application) |
410 | -{ |
411 | - DEBUG_MSG << "(" << application->appId() << ")"; |
412 | - Q_ASSERT(!m_applications.contains(application)); |
413 | - m_applications.append(application); |
414 | - |
415 | - MirSurfaceListInterface *surfaceList = application->surfaceList(); |
416 | - |
417 | - if (application->state() != ApplicationInfoInterface::Stopped) { |
418 | - if (surfaceList->count() == 0) { |
419 | - appendPlaceholder(application); |
420 | - } else { |
421 | - for (int i = 0; i < surfaceList->count(); ++i) { |
422 | - appendSurface(surfaceList->get(i), application); |
423 | - } |
424 | - } |
425 | - } |
426 | - |
427 | - connect(surfaceList, &QAbstractItemModel::rowsInserted, this, |
428 | - [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last) |
429 | - { |
430 | - for (int i = last; i >= first; --i) { |
431 | - this->appendSurface(surfaceList->get(i), application); |
432 | - } |
433 | - }); |
434 | -} |
435 | - |
436 | -void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application) |
437 | -{ |
438 | - DEBUG_MSG << "(" << application->appId() << ")"; |
439 | - Q_ASSERT(m_applications.contains(application)); |
440 | - |
441 | - MirSurfaceListInterface *surfaceList = application->surfaceList(); |
442 | - |
443 | - disconnect(surfaceList, 0, this, 0); |
444 | - |
445 | - Q_ASSERT(m_modelState == IdleState); |
446 | - m_modelState = RemovingState; |
447 | - |
448 | - int i = 0; |
449 | - while (i < m_surfaceList.count()) { |
450 | - if (m_surfaceList.at(i).application == application) { |
451 | - beginRemoveRows(QModelIndex(), i, i); |
452 | - m_surfaceList.removeAt(i); |
453 | - endRemoveRows(); |
454 | - Q_EMIT countChanged(); |
455 | - Q_EMIT listChanged(); |
456 | - } else { |
457 | - ++i; |
458 | - } |
459 | - } |
460 | - |
461 | - m_modelState = IdleState; |
462 | - |
463 | - DEBUG_MSG << " after " << toString(); |
464 | - |
465 | - m_applications.removeAll(application); |
466 | -} |
467 | - |
468 | -QAbstractListModel *TopLevelSurfaceList::applicationsModel() const |
469 | -{ |
470 | - return m_applicationsModel; |
471 | -} |
472 | - |
473 | -void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value) |
474 | -{ |
475 | - if (m_applicationsModel == value) { |
476 | - return; |
477 | - } |
478 | - |
479 | - DEBUG_MSG << "(" << value << ")"; |
480 | - |
481 | - Q_ASSERT(m_modelState == IdleState); |
482 | - m_modelState = ResettingState; |
483 | - |
484 | - beginResetModel(); |
485 | - |
486 | - if (m_applicationsModel) { |
487 | - m_surfaceList.clear(); |
488 | - m_applications.clear(); |
489 | - disconnect(m_applicationsModel, 0, this, 0); |
490 | - } |
491 | - |
492 | - m_applicationsModel = value; |
493 | - |
494 | - if (m_applicationsModel) { |
495 | - findApplicationRole(); |
496 | - |
497 | - connect(m_applicationsModel, &QAbstractItemModel::rowsInserted, |
498 | - this, [this](const QModelIndex &/*parent*/, int first, int last) { |
499 | - for (int i = first; i <= last; ++i) { |
500 | - auto application = getApplicationFromModelAt(i); |
501 | - addApplication(application); |
502 | - } |
503 | - }); |
504 | - |
505 | - connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved, |
506 | - this, [this](const QModelIndex &/*parent*/, int first, int last) { |
507 | - for (int i = first; i <= last; ++i) { |
508 | - auto application = getApplicationFromModelAt(i); |
509 | - removeApplication(application); |
510 | - } |
511 | - }); |
512 | - |
513 | - for (int i = 0; i < m_applicationsModel->rowCount(); ++i) { |
514 | - auto application = getApplicationFromModelAt(i); |
515 | - addApplication(application); |
516 | - } |
517 | - } |
518 | - |
519 | - endResetModel(); |
520 | - m_modelState = IdleState; |
521 | -} |
522 | - |
523 | -ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index) |
524 | -{ |
525 | - QModelIndex modelIndex = m_applicationsModel->index(index); |
526 | - |
527 | - QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole); |
528 | - |
529 | - // variant.value<ApplicationInfoInterface*>() returns null for some reason. |
530 | - return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>()); |
531 | -} |
532 | - |
533 | -void TopLevelSurfaceList::findApplicationRole() |
534 | -{ |
535 | - QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames(); |
536 | - |
537 | - m_applicationRole = -1; |
538 | - for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) { |
539 | - if (i.value() == "application") { |
540 | - m_applicationRole = i.key(); |
541 | - } |
542 | - } |
543 | - |
544 | - if (m_applicationRole == -1) { |
545 | - qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role."); |
546 | - } |
547 | -} |
548 | |
549 | === removed file 'plugins/WindowManager/TopLevelSurfaceList.h' |
550 | --- plugins/WindowManager/TopLevelSurfaceList.h 2016-04-04 13:39:44 +0000 |
551 | +++ plugins/WindowManager/TopLevelSurfaceList.h 1970-01-01 00:00:00 +0000 |
552 | @@ -1,223 +0,0 @@ |
553 | -/* |
554 | - * Copyright (C) 2016 Canonical, Ltd. |
555 | - * |
556 | - * This program is free software; you can redistribute it and/or modify |
557 | - * it under the terms of the GNU General Public License as published by |
558 | - * the Free Software Foundation; version 3. |
559 | - * |
560 | - * This program is distributed in the hope that it will be useful, |
561 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
562 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
563 | - * GNU General Public License for more details. |
564 | - * |
565 | - * You should have received a copy of the GNU General Public License |
566 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
567 | - */ |
568 | - |
569 | -#ifndef TOPLEVELSURFACELIST_H |
570 | -#define TOPLEVELSURFACELIST_H |
571 | - |
572 | -#include <QAbstractListModel> |
573 | -#include <QList> |
574 | -#include <QLoggingCategory> |
575 | - |
576 | -Q_DECLARE_LOGGING_CATEGORY(UNITY_TOPSURFACELIST) |
577 | - |
578 | -namespace unity { |
579 | - namespace shell { |
580 | - namespace application { |
581 | - class ApplicationInfoInterface; |
582 | - class MirSurfaceInterface; |
583 | - } |
584 | - } |
585 | -} |
586 | - |
587 | -/** |
588 | - * @brief A model of top-level surfaces |
589 | - * |
590 | - * It's an abstraction of top-level application windows. |
591 | - * |
592 | - * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is |
593 | - * still starting up. A shell should then display a splash screen or saved screenshot of the application |
594 | - * until its surface comes up. |
595 | - * |
596 | - * As applications can have multiple surfaces and you can also have entries without surfaces at all, |
597 | - * the only way to unambiguously refer to an entry in this model is through its id. |
598 | - */ |
599 | -class TopLevelSurfaceList : public QAbstractListModel |
600 | -{ |
601 | - |
602 | - Q_OBJECT |
603 | - |
604 | - /** |
605 | - * @brief A list model of applications. |
606 | - * |
607 | - * It's expected to have a role called "application" which returns a ApplicationInfoInterface |
608 | - */ |
609 | - Q_PROPERTY(QAbstractListModel* applicationsModel READ applicationsModel |
610 | - WRITE setApplicationsModel |
611 | - NOTIFY applicationsModelChanged) |
612 | - |
613 | - /** |
614 | - * @brief Number of top-level surfaces in this model |
615 | - * |
616 | - * This is the same as rowCount, added in order to keep compatibility with QML ListModels. |
617 | - */ |
618 | - Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
619 | - |
620 | - /** |
621 | - The id to be used on the next entry created |
622 | - Useful for tests |
623 | - */ |
624 | - Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged) |
625 | -public: |
626 | - |
627 | - /** |
628 | - * @brief The Roles supported by the model |
629 | - * |
630 | - * SurfaceRole - A MirSurfaceInterface. It will be null if the application is still starting up |
631 | - * ApplicationRole - An ApplicationInfoInterface |
632 | - * IdRole - A unique identifier for this entry. Useful to unambiguosly track elements as they move around in the list |
633 | - */ |
634 | - enum Roles { |
635 | - SurfaceRole = Qt::UserRole, |
636 | - ApplicationRole = Qt::UserRole + 1, |
637 | - IdRole = Qt::UserRole + 2, |
638 | - }; |
639 | - |
640 | - explicit TopLevelSurfaceList(QObject *parent = nullptr); |
641 | - virtual ~TopLevelSurfaceList(); |
642 | - |
643 | - // QAbstractItemModel methods |
644 | - int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
645 | - QVariant data(const QModelIndex& index, int role) const override; |
646 | - QHash<int, QByteArray> roleNames() const override { |
647 | - QHash<int, QByteArray> roleNames { {SurfaceRole, "surface"}, |
648 | - {ApplicationRole, "application"}, |
649 | - {IdRole, "id"} }; |
650 | - return roleNames; |
651 | - } |
652 | - |
653 | - int nextId() const { return m_nextId; } |
654 | - |
655 | - QAbstractListModel *applicationsModel() const; |
656 | - void setApplicationsModel(QAbstractListModel*); |
657 | - |
658 | -public Q_SLOTS: |
659 | - /** |
660 | - * @brief Returns the surface at the given index |
661 | - * |
662 | - * It will be a nullptr if the application is still starting up and thus hasn't yet created |
663 | - * and drawn into a surface. |
664 | - */ |
665 | - unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const; |
666 | - |
667 | - /** |
668 | - * @brief Returns the application at the given index |
669 | - */ |
670 | - unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const; |
671 | - |
672 | - /** |
673 | - * @brief Returns the unique id of the element at the given index |
674 | - */ |
675 | - int idAt(int index) const; |
676 | - |
677 | - /** |
678 | - * @brief Returns the index where the row with the given id is located |
679 | - * |
680 | - * Returns -1 if there's no row with the given id. |
681 | - */ |
682 | - int indexForId(int id) const; |
683 | - |
684 | - /** |
685 | - * @brief Raises the row with the given id to index 0 |
686 | - */ |
687 | - void raiseId(int id); |
688 | - |
689 | - void doRaiseId(int id); |
690 | - |
691 | -Q_SIGNALS: |
692 | - void countChanged(); |
693 | - |
694 | - /** |
695 | - * @brief Emitted when the list changes |
696 | - * |
697 | - * Emitted when model gains an element, loses an element or when elements exchange positions. |
698 | - */ |
699 | - void listChanged(); |
700 | - |
701 | - void nextIdChanged(); |
702 | - |
703 | - void applicationsModelChanged(); |
704 | - |
705 | -private: |
706 | - void addApplication(unity::shell::application::ApplicationInfoInterface *application); |
707 | - void removeApplication(unity::shell::application::ApplicationInfoInterface *application); |
708 | - |
709 | - int indexOf(unity::shell::application::MirSurfaceInterface *surface); |
710 | - void raise(unity::shell::application::MirSurfaceInterface *surface); |
711 | - void move(int from, int to); |
712 | - void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface, |
713 | - unity::shell::application::ApplicationInfoInterface *application); |
714 | - void connectSurface(unity::shell::application::MirSurfaceInterface *surface); |
715 | - int generateId(); |
716 | - int nextFreeId(int candidateId); |
717 | - QString toString(); |
718 | - void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface); |
719 | - void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface); |
720 | - void removeAt(int index); |
721 | - void findApplicationRole(); |
722 | - |
723 | - unity::shell::application::ApplicationInfoInterface *getApplicationFromModelAt(int index); |
724 | - |
725 | - /* |
726 | - Placeholder for a future surface from a starting or running application. |
727 | - Enables shell to give immediate feedback to the user by showing, eg, |
728 | - a splash screen. |
729 | - |
730 | - It's a model row containing a null surface and the given application. |
731 | - */ |
732 | - void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application); |
733 | - |
734 | - /* |
735 | - Adds a model row with the given surface and application |
736 | - |
737 | - Alternatively, if a placeholder exists for the given application it's |
738 | - filled with the given surface instead. |
739 | - */ |
740 | - void appendSurface(unity::shell::application::MirSurfaceInterface *surface, |
741 | - unity::shell::application::ApplicationInfoInterface *application); |
742 | - |
743 | - struct ModelEntry { |
744 | - ModelEntry(unity::shell::application::MirSurfaceInterface *surface, unity::shell::application::ApplicationInfoInterface *application, int id) |
745 | - : surface(surface), application(application), id(id) {} |
746 | - unity::shell::application::MirSurfaceInterface *surface; |
747 | - unity::shell::application::ApplicationInfoInterface *application; |
748 | - int id; |
749 | - bool removeOnceSurfaceDestroyed{false}; |
750 | - }; |
751 | - |
752 | - QList<ModelEntry> m_surfaceList; |
753 | - int m_nextId{1}; |
754 | - static const int m_maxId{1000000}; |
755 | - |
756 | - // applications that are being monitored |
757 | - QList<unity::shell::application::ApplicationInfoInterface *> m_applications; |
758 | - |
759 | - QAbstractListModel* m_applicationsModel{nullptr}; |
760 | - int m_applicationRole{-1}; |
761 | - |
762 | - enum ModelState { |
763 | - IdleState, |
764 | - InsertingState, |
765 | - RemovingState, |
766 | - MovingState, |
767 | - ResettingState |
768 | - }; |
769 | - ModelState m_modelState{IdleState}; |
770 | -}; |
771 | - |
772 | -Q_DECLARE_METATYPE(TopLevelSurfaceList*) |
773 | -Q_DECLARE_METATYPE(QAbstractListModel*) |
774 | - |
775 | -#endif // TOPLEVELSURFACELIST_H |
776 | |
777 | === added file 'plugins/WindowManager/TopLevelWindowModel.cpp' |
778 | --- plugins/WindowManager/TopLevelWindowModel.cpp 1970-01-01 00:00:00 +0000 |
779 | +++ plugins/WindowManager/TopLevelWindowModel.cpp 2016-11-23 15:15:21 +0000 |
780 | @@ -0,0 +1,644 @@ |
781 | +/* |
782 | + * Copyright (C) 2016 Canonical, Ltd. |
783 | + * |
784 | + * This program is free software: you can redistribute it and/or modify it under |
785 | + * the terms of the GNU Lesser General Public License version 3, as published by |
786 | + * the Free Software Foundation. |
787 | + * |
788 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
789 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
790 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
791 | + * Lesser General Public License for more details. |
792 | + * |
793 | + * You should have received a copy of the GNU Lesser General Public License |
794 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
795 | + */ |
796 | + |
797 | +#include "TopLevelWindowModel.h" |
798 | + |
799 | +// unity-api |
800 | +#include <unity/shell/application/ApplicationInfoInterface.h> |
801 | +#include <unity/shell/application/ApplicationManagerInterface.h> |
802 | +#include <unity/shell/application/MirSurfaceInterface.h> |
803 | +#include <unity/shell/application/MirSurfaceListInterface.h> |
804 | +#include <unity/shell/application/SurfaceManagerInterface.h> |
805 | + |
806 | +// Qt |
807 | +#include <QGuiApplication> |
808 | +#include <QDebug> |
809 | + |
810 | +// local |
811 | +#include "Window.h" |
812 | + |
813 | +Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg) |
814 | + |
815 | +#define DEBUG_MSG qCDebug(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__ |
816 | +#define INFO_MSG qCInfo(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__ |
817 | + |
818 | +namespace unityapi = unity::shell::application; |
819 | + |
820 | +TopLevelWindowModel::TopLevelWindowModel() |
821 | +{ |
822 | +} |
823 | + |
824 | +void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInterface* value) |
825 | +{ |
826 | + if (m_applicationManager == value) { |
827 | + return; |
828 | + } |
829 | + |
830 | + DEBUG_MSG << "(" << value << ")"; |
831 | + |
832 | + Q_ASSERT(m_modelState == IdleState); |
833 | + m_modelState = ResettingState; |
834 | + |
835 | + beginResetModel(); |
836 | + |
837 | + if (m_applicationManager) { |
838 | + m_windowModel.clear(); |
839 | + disconnect(m_applicationManager, 0, this, 0); |
840 | + } |
841 | + |
842 | + m_applicationManager = value; |
843 | + |
844 | + if (m_applicationManager) { |
845 | + connect(m_applicationManager, &QAbstractItemModel::rowsInserted, |
846 | + this, [this](const QModelIndex &/*parent*/, int first, int last) { |
847 | + for (int i = first; i <= last; ++i) { |
848 | + auto application = m_applicationManager->get(i); |
849 | + addApplication(application); |
850 | + } |
851 | + }); |
852 | + |
853 | + connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved, |
854 | + this, [this](const QModelIndex &/*parent*/, int first, int last) { |
855 | + for (int i = first; i <= last; ++i) { |
856 | + auto application = m_applicationManager->get(i); |
857 | + removeApplication(application); |
858 | + } |
859 | + }); |
860 | + |
861 | + for (int i = 0; i < m_applicationManager->rowCount(); ++i) { |
862 | + auto application = m_applicationManager->get(i); |
863 | + addApplication(application); |
864 | + } |
865 | + } |
866 | + |
867 | + endResetModel(); |
868 | + m_modelState = IdleState; |
869 | +} |
870 | + |
871 | +void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *surfaceManager) |
872 | +{ |
873 | + if (surfaceManager == m_surfaceManager) { |
874 | + return; |
875 | + } |
876 | + |
877 | + DEBUG_MSG << "(" << surfaceManager << ")"; |
878 | + |
879 | + if (m_surfaceManager) { |
880 | + disconnect(m_surfaceManager, 0, this, 0); |
881 | + } |
882 | + |
883 | + m_surfaceManager = surfaceManager; |
884 | + |
885 | + if (m_surfaceManager) { |
886 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfaceCreated, this, &TopLevelWindowModel::onSurfaceCreated); |
887 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised); |
888 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted); |
889 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded); |
890 | + } |
891 | + |
892 | + Q_EMIT surfaceManagerChanged(m_surfaceManager); |
893 | +} |
894 | + |
895 | +void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application) |
896 | +{ |
897 | + DEBUG_MSG << "(" << application->appId() << ")"; |
898 | + |
899 | + if (application->state() != unityapi::ApplicationInfoInterface::Stopped && application->surfaceList()->count() == 0) { |
900 | + prependPlaceholder(application); |
901 | + } |
902 | +} |
903 | + |
904 | +void TopLevelWindowModel::removeApplication(unityapi::ApplicationInfoInterface *application) |
905 | +{ |
906 | + DEBUG_MSG << "(" << application->appId() << ")"; |
907 | + |
908 | + Q_ASSERT(m_modelState == IdleState); |
909 | + |
910 | + int i = 0; |
911 | + while (i < m_windowModel.count()) { |
912 | + if (m_windowModel.at(i).application == application) { |
913 | + removeAt(i); |
914 | + } else { |
915 | + ++i; |
916 | + } |
917 | + } |
918 | +} |
919 | + |
920 | +void TopLevelWindowModel::prependPlaceholder(unityapi::ApplicationInfoInterface *application) |
921 | +{ |
922 | + INFO_MSG << "(" << application->appId() << ")"; |
923 | + |
924 | + prependSurfaceHelper(nullptr, application); |
925 | +} |
926 | + |
927 | +void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application) |
928 | +{ |
929 | + Q_ASSERT(surface != nullptr); |
930 | + |
931 | + bool filledPlaceholder = false; |
932 | + for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) { |
933 | + ModelEntry &entry = m_windowModel[i]; |
934 | + if (entry.application == application && entry.window->surface() == nullptr) { |
935 | + entry.window->setSurface(surface); |
936 | + connectSurface(surface); |
937 | + INFO_MSG << " appId=" << application->appId() << " surface=" << surface |
938 | + << ", filling out placeholder. after: " << toString(); |
939 | + filledPlaceholder = true; |
940 | + } |
941 | + } |
942 | + |
943 | + if (!filledPlaceholder) { |
944 | + INFO_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row"; |
945 | + prependSurfaceHelper(surface, application); |
946 | + } |
947 | +} |
948 | + |
949 | +void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application) |
950 | +{ |
951 | + if (m_modelState == IdleState) { |
952 | + m_modelState = InsertingState; |
953 | + beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/); |
954 | + } else { |
955 | + Q_ASSERT(m_modelState == ResettingState); |
956 | + // No point in signaling anything if we're resetting the whole model |
957 | + } |
958 | + |
959 | + int id = generateId(); |
960 | + Window *window = new Window(id, this); |
961 | + if (surface) { |
962 | + window->setSurface(surface); |
963 | + } |
964 | + m_windowModel.prepend(ModelEntry(window, application)); |
965 | + if (surface) { |
966 | + connectSurface(surface); |
967 | + } |
968 | + |
969 | + connectWindow(window); |
970 | + |
971 | + if (m_modelState == InsertingState) { |
972 | + endInsertRows(); |
973 | + Q_EMIT countChanged(); |
974 | + Q_EMIT listChanged(); |
975 | + m_modelState = IdleState; |
976 | + } |
977 | + |
978 | + if (!surface) { |
979 | + // focus the newly added window. miral can't help with that as it doesn't know about it. |
980 | + window->setFocused(true); |
981 | + if (m_focusedWindow && m_focusedWindow->surface()) { |
982 | + m_surfaceManager->activate(nullptr); |
983 | + } |
984 | + } |
985 | + |
986 | + INFO_MSG << " after " << toString(); |
987 | +} |
988 | + |
989 | +void TopLevelWindowModel::connectWindow(Window *window) |
990 | +{ |
991 | + connect(window, &Window::focusRequested, this, [this, window]() { |
992 | + if (!window->surface()) { |
993 | + activateEmptyWindow(window); |
994 | + } |
995 | + }); |
996 | + |
997 | + connect(window, &Window::focusedChanged, this, [this, window](bool focused) { |
998 | + if (window->surface()) { |
999 | + // Condense changes to the focused window |
1000 | + // eg: Do focusedWindow=A to focusedWindow=B instead of |
1001 | + // focusedWindow=A to focusedWindow=null to focusedWindow=B |
1002 | + m_focusedWindowChanged = true; |
1003 | + if (focused) { |
1004 | + Q_ASSERT(m_newlyFocusedWindow == nullptr); |
1005 | + m_newlyFocusedWindow = window; |
1006 | + } |
1007 | + } |
1008 | + }); |
1009 | + |
1010 | + connect(window, &Window::closeRequested, this, [this, window]() { |
1011 | + if (!window->surface()) { |
1012 | + int index = indexForId(window->id()); |
1013 | + Q_ASSERT(index >= 0); |
1014 | + m_windowModel[index].application->close(); |
1015 | + } |
1016 | + }); |
1017 | + |
1018 | + connect(window, &Window::emptyWindowActivated, this, [this, window]() { |
1019 | + activateEmptyWindow(window); |
1020 | + }); |
1021 | +} |
1022 | + |
1023 | +void TopLevelWindowModel::activateEmptyWindow(Window *window) |
1024 | +{ |
1025 | + Q_ASSERT(!window->surface()); |
1026 | + |
1027 | + // miral doesn't know about empty windows (ie, windows that are not backed up by MirSurfaces) |
1028 | + // So we have to activate them ourselves (instead of asking SurfaceManager to do it for us). |
1029 | + |
1030 | + window->setFocused(true); |
1031 | + raiseId(window->id()); |
1032 | + Window *previousWindow = m_focusedWindow; |
1033 | + setFocusedWindow(window); |
1034 | + if (previousWindow && previousWindow->surface() && previousWindow->surface()->focused()) { |
1035 | + m_surfaceManager->activate(nullptr); |
1036 | + } |
1037 | +} |
1038 | + |
1039 | +void TopLevelWindowModel::connectSurface(unityapi::MirSurfaceInterface *surface) |
1040 | +{ |
1041 | + connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, [this, surface](bool live){ |
1042 | + if (!live) { |
1043 | + onSurfaceDied(surface); |
1044 | + } |
1045 | + }); |
1046 | + connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); }); |
1047 | +} |
1048 | + |
1049 | +void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface) |
1050 | +{ |
1051 | + if (surface->type() == Mir::InputMethodType) { |
1052 | + removeInputMethodWindow(); |
1053 | + return; |
1054 | + } |
1055 | + |
1056 | + int i = indexOf(surface); |
1057 | + if (i == -1) { |
1058 | + return; |
1059 | + } |
1060 | + |
1061 | + auto application = m_windowModel[i].application; |
1062 | + |
1063 | + // can't be starting if it already has a surface |
1064 | + Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting); |
1065 | + |
1066 | + if (application->state() == unityapi::ApplicationInfoInterface::Running) { |
1067 | + m_windowModel[i].removeOnceSurfaceDestroyed = true; |
1068 | + } else { |
1069 | + // assume it got killed by the out-of-memory daemon. |
1070 | + // |
1071 | + // So leave entry in the model and only remove its surface, so shell can display a screenshot |
1072 | + // in its place. |
1073 | + m_windowModel[i].removeOnceSurfaceDestroyed = false; |
1074 | + } |
1075 | +} |
1076 | + |
1077 | +void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surface) |
1078 | +{ |
1079 | + int i = indexOf(surface); |
1080 | + if (i == -1) { |
1081 | + return; |
1082 | + } |
1083 | + |
1084 | + if (m_windowModel[i].removeOnceSurfaceDestroyed) { |
1085 | + removeAt(i); |
1086 | + } else { |
1087 | + auto window = m_windowModel[i].window; |
1088 | + window->setSurface(nullptr); |
1089 | + window->setFocused(false); |
1090 | + INFO_MSG << " Removed surface from entry. After: " << toString(); |
1091 | + } |
1092 | +} |
1093 | + |
1094 | +void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface) |
1095 | +{ |
1096 | + DEBUG_MSG << "(" << surface << ")"; |
1097 | + if (surface->type() == Mir::InputMethodType) { |
1098 | + int id = generateId(); |
1099 | + Window *qmlWindow = new Window(id, this); |
1100 | + connectWindow(qmlWindow); |
1101 | + qmlWindow->setSurface(surface); |
1102 | + setInputMethodWindow(qmlWindow); |
1103 | + } else { |
1104 | + auto application = m_applicationManager->findApplicationWithSurface(surface); |
1105 | + if (application) { |
1106 | + prependSurface(surface, application); |
1107 | + } else { |
1108 | + // Must be a prompt session. No need to do add it as a prompt surface is not top-level. |
1109 | + // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application. |
1110 | + // Still wrap it in a Window though, so that we keep focusedWindow() up to date. |
1111 | + int id = generateId(); |
1112 | + Window *promptWindow = new Window(id, this); |
1113 | + connectWindow(promptWindow); |
1114 | + promptWindow->setSurface(surface); |
1115 | + connect(surface, &QObject::destroyed, promptWindow, [=](){ |
1116 | + promptWindow->setSurface(nullptr); |
1117 | + promptWindow->deleteLater(); |
1118 | + }); |
1119 | + } |
1120 | + } |
1121 | + // TODO: handle surfaces that are neither top-level windows nor input method. eg: child dialogs, popups, menus |
1122 | +} |
1123 | + |
1124 | +void TopLevelWindowModel::removeAt(int index) |
1125 | +{ |
1126 | + if (m_modelState == IdleState) { |
1127 | + beginRemoveRows(QModelIndex(), index, index); |
1128 | + m_modelState = RemovingState; |
1129 | + } else { |
1130 | + Q_ASSERT(m_modelState == ResettingState); |
1131 | + // No point in signaling anything if we're resetting the whole model |
1132 | + } |
1133 | + |
1134 | + auto window = m_windowModel[index].window; |
1135 | + |
1136 | + window->setSurface(nullptr); |
1137 | + window->setFocused(false); |
1138 | + |
1139 | + m_windowModel.removeAt(index); |
1140 | + |
1141 | + if (m_modelState == RemovingState) { |
1142 | + endRemoveRows(); |
1143 | + Q_EMIT countChanged(); |
1144 | + Q_EMIT listChanged(); |
1145 | + m_modelState = IdleState; |
1146 | + } |
1147 | + |
1148 | + disconnect(window, 0, this, 0); |
1149 | + if (m_focusedWindow == window) { |
1150 | + setFocusedWindow(nullptr); |
1151 | + } |
1152 | + delete window; |
1153 | + |
1154 | + INFO_MSG << " after " << toString(); |
1155 | +} |
1156 | + |
1157 | +void TopLevelWindowModel::setInputMethodWindow(Window *window) |
1158 | +{ |
1159 | + if (m_inputMethodWindow) { |
1160 | + qWarning("Multiple Input Method Surfaces created, removing the old one!"); |
1161 | + delete m_inputMethodWindow; |
1162 | + } |
1163 | + m_inputMethodWindow = window; |
1164 | + Q_EMIT inputMethodSurfaceChanged(m_inputMethodWindow->surface()); |
1165 | +} |
1166 | + |
1167 | +void TopLevelWindowModel::removeInputMethodWindow() |
1168 | +{ |
1169 | + if (m_inputMethodWindow) { |
1170 | + delete m_inputMethodWindow; |
1171 | + m_inputMethodWindow = nullptr; |
1172 | + Q_EMIT inputMethodSurfaceChanged(nullptr); |
1173 | + } |
1174 | +} |
1175 | + |
1176 | +void TopLevelWindowModel::onSurfacesRaised(const QVector<unityapi::MirSurfaceInterface*> &surfaces) |
1177 | +{ |
1178 | + DEBUG_MSG << "(" << surfaces << ")"; |
1179 | + const int raiseCount = surfaces.size(); |
1180 | + for (int i = 0; i < raiseCount; i++) { |
1181 | + int fromIndex = findIndexOf(surfaces[i]); |
1182 | + if (fromIndex != -1) { |
1183 | + move(fromIndex, 0); |
1184 | + } |
1185 | + } |
1186 | +} |
1187 | + |
1188 | +int TopLevelWindowModel::rowCount(const QModelIndex &/*parent*/) const |
1189 | +{ |
1190 | + return m_windowModel.count(); |
1191 | +} |
1192 | + |
1193 | +QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const |
1194 | +{ |
1195 | + if (index.row() < 0 || index.row() >= m_windowModel.size()) |
1196 | + return QVariant(); |
1197 | + |
1198 | + if (role == WindowRole) { |
1199 | + Window *window = m_windowModel.at(index.row()).window; |
1200 | + return QVariant::fromValue(window); |
1201 | + } else if (role == ApplicationRole) { |
1202 | + return QVariant::fromValue(m_windowModel.at(index.row()).application); |
1203 | + } else { |
1204 | + return QVariant(); |
1205 | + } |
1206 | +} |
1207 | + |
1208 | +int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const |
1209 | +{ |
1210 | + for (int i=0; i<m_windowModel.count(); i++) { |
1211 | + if (m_windowModel[i].window->surface() == surface) { |
1212 | + return i; |
1213 | + } |
1214 | + } |
1215 | + return -1; |
1216 | +} |
1217 | + |
1218 | +int TopLevelWindowModel::generateId() |
1219 | +{ |
1220 | + int id = m_nextId; |
1221 | + m_nextId = nextFreeId(nextId(id), id); |
1222 | + return id; |
1223 | +} |
1224 | + |
1225 | +int TopLevelWindowModel::nextId(int id) const |
1226 | +{ |
1227 | + if (id == m_maxId) { |
1228 | + return id = 1; |
1229 | + } else { |
1230 | + return id + 1; |
1231 | + } |
1232 | +} |
1233 | + |
1234 | +int TopLevelWindowModel::nextFreeId(int candidateId, const int latestId) |
1235 | +{ |
1236 | + int firstCandidateId = candidateId; |
1237 | + |
1238 | + while (indexForId(candidateId) != -1 || candidateId == latestId) { |
1239 | + candidateId = nextId(candidateId); |
1240 | + |
1241 | + if (candidateId == firstCandidateId) { |
1242 | + qFatal("TopLevelWindowModel: run out of window ids."); |
1243 | + } |
1244 | + } |
1245 | + |
1246 | + return candidateId; |
1247 | +} |
1248 | + |
1249 | +QString TopLevelWindowModel::toString() |
1250 | +{ |
1251 | + QString str; |
1252 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
1253 | + auto item = m_windowModel.at(i); |
1254 | + |
1255 | + QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)") |
1256 | + .arg(QString::number(i), |
1257 | + item.application->appId(), |
1258 | + QString::number((qintptr)item.window->surface(), 16), |
1259 | + QString::number(item.window->id())); |
1260 | + |
1261 | + if (i > 0) { |
1262 | + str.append(","); |
1263 | + } |
1264 | + str.append(itemStr); |
1265 | + } |
1266 | + return str; |
1267 | +} |
1268 | + |
1269 | +int TopLevelWindowModel::indexOf(unityapi::MirSurfaceInterface *surface) |
1270 | +{ |
1271 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
1272 | + if (m_windowModel.at(i).window->surface() == surface) { |
1273 | + return i; |
1274 | + } |
1275 | + } |
1276 | + return -1; |
1277 | +} |
1278 | + |
1279 | +int TopLevelWindowModel::indexForId(int id) const |
1280 | +{ |
1281 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
1282 | + if (m_windowModel[i].window->id() == id) { |
1283 | + return i; |
1284 | + } |
1285 | + } |
1286 | + return -1; |
1287 | +} |
1288 | + |
1289 | +Window *TopLevelWindowModel::windowAt(int index) const |
1290 | +{ |
1291 | + if (index >=0 && index < m_windowModel.count()) { |
1292 | + return m_windowModel[index].window; |
1293 | + } else { |
1294 | + return nullptr; |
1295 | + } |
1296 | +} |
1297 | + |
1298 | +unityapi::MirSurfaceInterface *TopLevelWindowModel::surfaceAt(int index) const |
1299 | +{ |
1300 | + if (index >=0 && index < m_windowModel.count()) { |
1301 | + return m_windowModel[index].window->surface(); |
1302 | + } else { |
1303 | + return nullptr; |
1304 | + } |
1305 | +} |
1306 | + |
1307 | +unityapi::ApplicationInfoInterface *TopLevelWindowModel::applicationAt(int index) const |
1308 | +{ |
1309 | + if (index >=0 && index < m_windowModel.count()) { |
1310 | + return m_windowModel[index].application; |
1311 | + } else { |
1312 | + return nullptr; |
1313 | + } |
1314 | +} |
1315 | + |
1316 | +int TopLevelWindowModel::idAt(int index) const |
1317 | +{ |
1318 | + if (index >=0 && index < m_windowModel.count()) { |
1319 | + return m_windowModel[index].window->id(); |
1320 | + } else { |
1321 | + return 0; |
1322 | + } |
1323 | +} |
1324 | + |
1325 | +void TopLevelWindowModel::raiseId(int id) |
1326 | +{ |
1327 | + if (m_modelState == IdleState) { |
1328 | + DEBUG_MSG << "(id=" << id << ") - do it now."; |
1329 | + doRaiseId(id); |
1330 | + } else { |
1331 | + DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop."; |
1332 | + // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts |
1333 | + // if we perform yet another model change straight away. |
1334 | + // |
1335 | + // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says |
1336 | + // the index is definitely within bounds. |
1337 | + QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id)); |
1338 | + } |
1339 | +} |
1340 | + |
1341 | +void TopLevelWindowModel::doRaiseId(int id) |
1342 | +{ |
1343 | + int fromIndex = indexForId(id); |
1344 | + if (fromIndex != -1) { |
1345 | + auto surface = m_windowModel[fromIndex].window->surface(); |
1346 | + if (surface) { |
1347 | + m_surfaceManager->raise(surface); |
1348 | + } else { |
1349 | + // move it ourselves. Since there's no mir::scene::Surface/miral::Window, there's nothing |
1350 | + // miral can do about it. |
1351 | + move(fromIndex, 0); |
1352 | + } |
1353 | + } |
1354 | +} |
1355 | + |
1356 | +void TopLevelWindowModel::setFocusedWindow(Window *window) |
1357 | +{ |
1358 | + if (window != m_focusedWindow) { |
1359 | + INFO_MSG << "(" << window << ")"; |
1360 | + |
1361 | + Window* previousWindow = m_focusedWindow; |
1362 | + |
1363 | + m_focusedWindow = window; |
1364 | + Q_EMIT focusedWindowChanged(m_focusedWindow); |
1365 | + |
1366 | + if (previousWindow && previousWindow->focused() && !previousWindow->surface()) { |
1367 | + // do it ourselves. miral doesn't know about this window |
1368 | + previousWindow->setFocused(false); |
1369 | + } |
1370 | + } |
1371 | +} |
1372 | + |
1373 | +unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const |
1374 | +{ |
1375 | + return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr; |
1376 | +} |
1377 | + |
1378 | +Window* TopLevelWindowModel::focusedWindow() const |
1379 | +{ |
1380 | + return m_focusedWindow; |
1381 | +} |
1382 | + |
1383 | +void TopLevelWindowModel::move(int from, int to) |
1384 | +{ |
1385 | + if (from == to) return; |
1386 | + DEBUG_MSG << " from=" << from << " to=" << to; |
1387 | + |
1388 | + if (from >= 0 && from < m_windowModel.size() && to >= 0 && to < m_windowModel.size()) { |
1389 | + QModelIndex parent; |
1390 | + /* When moving an item down, the destination index needs to be incremented |
1391 | + by one, as explained in the documentation: |
1392 | + http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ |
1393 | + |
1394 | + Q_ASSERT(m_modelState == IdleState); |
1395 | + m_modelState = MovingState; |
1396 | + |
1397 | + beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); |
1398 | +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) |
1399 | + const auto &window = m_windowModel.takeAt(from); |
1400 | + m_windowModel.insert(to, window); |
1401 | +#else |
1402 | + m_windowModel.move(from, to); |
1403 | +#endif |
1404 | + endMoveRows(); |
1405 | + |
1406 | + Q_EMIT listChanged(); |
1407 | + m_modelState = IdleState; |
1408 | + |
1409 | + INFO_MSG << " after " << toString(); |
1410 | + } |
1411 | +} |
1412 | +void TopLevelWindowModel::onModificationsStarted() |
1413 | +{ |
1414 | +} |
1415 | + |
1416 | +void TopLevelWindowModel::onModificationsEnded() |
1417 | +{ |
1418 | + if (m_focusedWindowChanged) { |
1419 | + setFocusedWindow(m_newlyFocusedWindow); |
1420 | + } |
1421 | + // reset |
1422 | + m_focusedWindowChanged = false; |
1423 | + m_newlyFocusedWindow = nullptr; |
1424 | +} |
1425 | |
1426 | === added file 'plugins/WindowManager/TopLevelWindowModel.h' |
1427 | --- plugins/WindowManager/TopLevelWindowModel.h 1970-01-01 00:00:00 +0000 |
1428 | +++ plugins/WindowManager/TopLevelWindowModel.h 2016-11-23 15:15:21 +0000 |
1429 | @@ -0,0 +1,258 @@ |
1430 | +/* |
1431 | + * Copyright (C) 2016 Canonical, Ltd. |
1432 | + * |
1433 | + * This program is free software: you can redistribute it and/or modify it under |
1434 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1435 | + * the Free Software Foundation. |
1436 | + * |
1437 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1438 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1439 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1440 | + * Lesser General Public License for more details. |
1441 | + * |
1442 | + * You should have received a copy of the GNU Lesser General Public License |
1443 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1444 | + */ |
1445 | + |
1446 | +#ifndef TOPLEVELWINDOWMODEL_H |
1447 | +#define TOPLEVELWINDOWMODEL_H |
1448 | + |
1449 | +#include <QAbstractListModel> |
1450 | +#include <QLoggingCategory> |
1451 | + |
1452 | +Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL) |
1453 | + |
1454 | +class Window; |
1455 | + |
1456 | +namespace unity { |
1457 | + namespace shell { |
1458 | + namespace application { |
1459 | + class ApplicationInfoInterface; |
1460 | + class ApplicationManagerInterface; |
1461 | + class MirSurfaceInterface; |
1462 | + class SurfaceManagerInterface; |
1463 | + } |
1464 | + } |
1465 | +} |
1466 | + |
1467 | +/** |
1468 | + * @brief A model of top-level surfaces |
1469 | + * |
1470 | + * It's an abstraction of top-level application windows. |
1471 | + * |
1472 | + * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is |
1473 | + * still starting up. A shell should then display a splash screen or saved screenshot of the application |
1474 | + * until its surface comes up. |
1475 | + * |
1476 | + * As applications can have multiple surfaces and you can also have entries without surfaces at all, |
1477 | + * the only way to unambiguously refer to an entry in this model is through its id. |
1478 | + */ |
1479 | +class TopLevelWindowModel : public QAbstractListModel |
1480 | +{ |
1481 | + Q_OBJECT |
1482 | + |
1483 | + /** |
1484 | + * @brief Number of top-level surfaces in this model |
1485 | + * |
1486 | + * This is the same as rowCount, added in order to keep compatibility with QML ListModels. |
1487 | + */ |
1488 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
1489 | + |
1490 | + /** |
1491 | + * @brief The input method surface, if any |
1492 | + * |
1493 | + * The surface of a onscreen keyboard (akak "virtual keyboard") would be kept here and not in the model itself. |
1494 | + */ |
1495 | + Q_PROPERTY(unity::shell::application::MirSurfaceInterface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged) |
1496 | + |
1497 | + /** |
1498 | + * @brief The currently focused window, if any |
1499 | + */ |
1500 | + Q_PROPERTY(Window* focusedWindow READ focusedWindow NOTIFY focusedWindowChanged) |
1501 | + |
1502 | + Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager |
1503 | + READ surfaceManager |
1504 | + WRITE setSurfaceManager |
1505 | + NOTIFY surfaceManagerChanged) |
1506 | + |
1507 | + Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager |
1508 | + READ applicationManager |
1509 | + WRITE setApplicationManager |
1510 | + NOTIFY applicationManagerChanged) |
1511 | + |
1512 | + /** |
1513 | + The id to be used on the next entry created |
1514 | + Useful for tests |
1515 | + */ |
1516 | + Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged) |
1517 | + |
1518 | +public: |
1519 | + /** |
1520 | + * @brief The Roles supported by the model |
1521 | + * |
1522 | + * WindowRole - A Window. |
1523 | + * ApplicationRole - An ApplicationInfoInterface |
1524 | + */ |
1525 | + enum Roles { |
1526 | + WindowRole = Qt::UserRole, |
1527 | + ApplicationRole = Qt::UserRole + 1, |
1528 | + }; |
1529 | + |
1530 | + TopLevelWindowModel(); |
1531 | + |
1532 | + // From QAbstractItemModel |
1533 | + int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
1534 | + QVariant data(const QModelIndex& index, int role) const override; |
1535 | + QHash<int, QByteArray> roleNames() const override { |
1536 | + QHash<int, QByteArray> roleNames { {WindowRole, "window"}, |
1537 | + {ApplicationRole, "application"} }; |
1538 | + return roleNames; |
1539 | + } |
1540 | + |
1541 | + // Own API |
1542 | + |
1543 | + unity::shell::application::MirSurfaceInterface* inputMethodSurface() const; |
1544 | + Window* focusedWindow() const; |
1545 | + |
1546 | + unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; } |
1547 | + void setApplicationManager(unity::shell::application::ApplicationManagerInterface*); |
1548 | + |
1549 | + unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; } |
1550 | + void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*); |
1551 | + |
1552 | + int nextId() const { return m_nextId; } |
1553 | + |
1554 | +public: |
1555 | + /** |
1556 | + * @brief Returns the surface at the given index |
1557 | + * |
1558 | + * It will be a nullptr if the application is still starting up and thus hasn't yet created |
1559 | + * and drawn into a surface. |
1560 | + * |
1561 | + * Same as windowAt(index).surface() |
1562 | + */ |
1563 | + Q_INVOKABLE unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const; |
1564 | + |
1565 | + /** |
1566 | + * @brief Returns the window at the given index |
1567 | + * |
1568 | + * Will always be valid |
1569 | + */ |
1570 | + Q_INVOKABLE Window *windowAt(int index) const; |
1571 | + |
1572 | + /** |
1573 | + * @brief Returns the application at the given index |
1574 | + */ |
1575 | + Q_INVOKABLE unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const; |
1576 | + |
1577 | + /** |
1578 | + * @brief Returns the unique id of the element at the given index |
1579 | + */ |
1580 | + Q_INVOKABLE int idAt(int index) const; |
1581 | + |
1582 | + /** |
1583 | + * @brief Returns the index where the row with the given id is located |
1584 | + * |
1585 | + * Returns -1 if there's no row with the given id. |
1586 | + */ |
1587 | + Q_INVOKABLE int indexForId(int id) const; |
1588 | + |
1589 | + /** |
1590 | + * @brief Raises the row with the given id to the top of the window stack (index == count-1) |
1591 | + */ |
1592 | + Q_INVOKABLE void raiseId(int id); |
1593 | + |
1594 | +Q_SIGNALS: |
1595 | + void countChanged(); |
1596 | + void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface); |
1597 | + void focusedWindowChanged(Window *focusedWindow); |
1598 | + void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*); |
1599 | + void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*); |
1600 | + |
1601 | + /** |
1602 | + * @brief Emitted when the list changes |
1603 | + * |
1604 | + * Emitted when model gains an element, loses an element or when elements exchange positions. |
1605 | + */ |
1606 | + void listChanged(); |
1607 | + |
1608 | + void nextIdChanged(); |
1609 | + |
1610 | +private Q_SLOTS: |
1611 | + void onSurfaceCreated(unity::shell::application::MirSurfaceInterface *surface); |
1612 | + void onSurfacesRaised(const QVector<unity::shell::application::MirSurfaceInterface*> &surfaces); |
1613 | + |
1614 | + void onModificationsStarted(); |
1615 | + void onModificationsEnded(); |
1616 | + |
1617 | +private: |
1618 | + void doRaiseId(int id); |
1619 | + int generateId(); |
1620 | + int nextFreeId(int candidateId, const int latestId); |
1621 | + int nextId(int id) const; |
1622 | + QString toString(); |
1623 | + int indexOf(unity::shell::application::MirSurfaceInterface *surface); |
1624 | + |
1625 | + void setInputMethodWindow(Window *window); |
1626 | + void setFocusedWindow(Window *window); |
1627 | + void removeInputMethodWindow(); |
1628 | + int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const; |
1629 | + void removeAt(int index); |
1630 | + |
1631 | + void addApplication(unity::shell::application::ApplicationInfoInterface *application); |
1632 | + void removeApplication(unity::shell::application::ApplicationInfoInterface *application); |
1633 | + |
1634 | + void prependPlaceholder(unity::shell::application::ApplicationInfoInterface *application); |
1635 | + void prependSurface(unity::shell::application::MirSurfaceInterface *surface, |
1636 | + unity::shell::application::ApplicationInfoInterface *application); |
1637 | + void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface, |
1638 | + unity::shell::application::ApplicationInfoInterface *application); |
1639 | + |
1640 | + void connectWindow(Window *window); |
1641 | + void connectSurface(unity::shell::application::MirSurfaceInterface *surface); |
1642 | + |
1643 | + void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface); |
1644 | + void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface); |
1645 | + |
1646 | + void move(int from, int to); |
1647 | + |
1648 | + void activateEmptyWindow(Window *window); |
1649 | + |
1650 | + struct ModelEntry { |
1651 | + ModelEntry() {} |
1652 | + ModelEntry(Window *window, |
1653 | + unity::shell::application::ApplicationInfoInterface *application) |
1654 | + : window(window), application(application) {} |
1655 | + Window *window{nullptr}; |
1656 | + unity::shell::application::ApplicationInfoInterface *application{nullptr}; |
1657 | + bool removeOnceSurfaceDestroyed{false}; |
1658 | + }; |
1659 | + |
1660 | + QVector<ModelEntry> m_windowModel; |
1661 | + Window* m_inputMethodWindow{nullptr}; |
1662 | + Window* m_focusedWindow{nullptr}; |
1663 | + |
1664 | + int m_nextId{1}; |
1665 | + // Just something big enough that we don't risk running out of unused id numbers. |
1666 | + // Not sure if QML int type supports something close to std::numeric_limits<int>::max() and |
1667 | + // there's no reason to try out its limits. |
1668 | + static const int m_maxId{1000000}; |
1669 | + |
1670 | + unity::shell::application::ApplicationManagerInterface* m_applicationManager{nullptr}; |
1671 | + unity::shell::application::SurfaceManagerInterface *m_surfaceManager{nullptr}; |
1672 | + |
1673 | + enum ModelState { |
1674 | + IdleState, |
1675 | + InsertingState, |
1676 | + RemovingState, |
1677 | + MovingState, |
1678 | + ResettingState |
1679 | + }; |
1680 | + ModelState m_modelState{IdleState}; |
1681 | + |
1682 | + // Valid between modificationsStarted and modificationsEnded |
1683 | + bool m_focusedWindowChanged{false}; |
1684 | + Window *m_newlyFocusedWindow{nullptr}; |
1685 | +}; |
1686 | + |
1687 | +#endif // TOPLEVELWINDOWMODEL_H |
1688 | |
1689 | === added file 'plugins/WindowManager/Window.cpp' |
1690 | --- plugins/WindowManager/Window.cpp 1970-01-01 00:00:00 +0000 |
1691 | +++ plugins/WindowManager/Window.cpp 2016-11-23 15:15:21 +0000 |
1692 | @@ -0,0 +1,234 @@ |
1693 | +/* |
1694 | + * Copyright (C) 2016 Canonical, Ltd. |
1695 | + * |
1696 | + * This program is free software; you can redistribute it and/or modify |
1697 | + * it under the terms of the GNU General Public License as published by |
1698 | + * the Free Software Foundation; version 3. |
1699 | + * |
1700 | + * This program is distributed in the hope that it will be useful, |
1701 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1702 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1703 | + * GNU General Public License for more details. |
1704 | + * |
1705 | + * You should have received a copy of the GNU General Public License |
1706 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1707 | + */ |
1708 | + |
1709 | +#include "Window.h" |
1710 | + |
1711 | +// unity-api |
1712 | +#include <unity/shell/application/MirSurfaceInterface.h> |
1713 | + |
1714 | +#include <QQmlEngine> |
1715 | + |
1716 | +namespace unityapi = unity::shell::application; |
1717 | + |
1718 | +Q_LOGGING_CATEGORY(UNITY_WINDOW, "unity.window", QtWarningMsg) |
1719 | + |
1720 | +#define DEBUG_MSG qCDebug(UNITY_WINDOW).nospace() << qPrintable(toString()) << "::" << __func__ |
1721 | + |
1722 | +Window::Window(int id, QObject *parent) |
1723 | + : QObject(parent) |
1724 | + , m_id(id) |
1725 | +{ |
1726 | + DEBUG_MSG << "()"; |
1727 | + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); |
1728 | +} |
1729 | + |
1730 | +Window::~Window() |
1731 | +{ |
1732 | + DEBUG_MSG << "()"; |
1733 | +} |
1734 | + |
1735 | +QPoint Window::position() const |
1736 | +{ |
1737 | + return m_position; |
1738 | +} |
1739 | + |
1740 | +QPoint Window::requestedPosition() const |
1741 | +{ |
1742 | + return m_requestedPosition; |
1743 | +} |
1744 | + |
1745 | +void Window::setRequestedPosition(const QPoint &value) |
1746 | +{ |
1747 | + m_positionRequested = true; |
1748 | + if (value != m_requestedPosition) { |
1749 | + m_requestedPosition = value; |
1750 | + Q_EMIT requestedPositionChanged(m_requestedPosition); |
1751 | + if (m_surface) { |
1752 | + m_surface->setRequestedPosition(value); |
1753 | + } else { |
1754 | + // fake-miral: always comply |
1755 | + m_position = m_requestedPosition; |
1756 | + Q_EMIT positionChanged(m_position); |
1757 | + } |
1758 | + } |
1759 | +} |
1760 | + |
1761 | +Mir::State Window::state() const |
1762 | +{ |
1763 | + return m_state; |
1764 | +} |
1765 | + |
1766 | +bool Window::focused() const |
1767 | +{ |
1768 | + return m_focused; |
1769 | +} |
1770 | + |
1771 | +bool Window::confinesMousePointer() const |
1772 | +{ |
1773 | + if (m_surface) { |
1774 | + return m_surface->confinesMousePointer(); |
1775 | + } else { |
1776 | + return false; |
1777 | + } |
1778 | +} |
1779 | + |
1780 | +int Window::id() const |
1781 | +{ |
1782 | + return m_id; |
1783 | +} |
1784 | + |
1785 | +unityapi::MirSurfaceInterface* Window::surface() const |
1786 | +{ |
1787 | + return m_surface; |
1788 | +} |
1789 | + |
1790 | +void Window::requestState(Mir::State state) |
1791 | +{ |
1792 | + m_stateRequested = true; |
1793 | + if (m_surface) { |
1794 | + m_surface->requestState(state); |
1795 | + } else if (m_state != state) { |
1796 | + m_state = state; |
1797 | + Q_EMIT stateChanged(m_state); |
1798 | + } |
1799 | +} |
1800 | + |
1801 | +void Window::close() |
1802 | +{ |
1803 | + if (m_surface) { |
1804 | + m_surface->close(); |
1805 | + } else { |
1806 | + Q_EMIT closeRequested(); |
1807 | + } |
1808 | +} |
1809 | + |
1810 | +void Window::activate() |
1811 | +{ |
1812 | + if (m_surface) { |
1813 | + m_surface->activate(); |
1814 | + } else { |
1815 | + Q_EMIT emptyWindowActivated(); |
1816 | + } |
1817 | +} |
1818 | + |
1819 | +void Window::setSurface(unityapi::MirSurfaceInterface *surface) |
1820 | +{ |
1821 | + DEBUG_MSG << "(" << surface << ")"; |
1822 | + if (m_surface) { |
1823 | + disconnect(m_surface, 0, this, 0); |
1824 | + } |
1825 | + |
1826 | + m_surface = surface; |
1827 | + |
1828 | + if (m_surface) { |
1829 | + connect(surface, &unityapi::MirSurfaceInterface::focusRequested, this, [this]() { |
1830 | + Q_EMIT focusRequested(); |
1831 | + }); |
1832 | + |
1833 | + connect(surface, &unityapi::MirSurfaceInterface::closeRequested, this, &Window::closeRequested); |
1834 | + |
1835 | + connect(surface, &unityapi::MirSurfaceInterface::positionChanged, this, [this]() { |
1836 | + updatePosition(); |
1837 | + }); |
1838 | + |
1839 | + connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [this]() { |
1840 | + updateState(); |
1841 | + }); |
1842 | + |
1843 | + connect(surface, &unityapi::MirSurfaceInterface::focusedChanged, this, [this]() { |
1844 | + updateFocused(); |
1845 | + }); |
1846 | + |
1847 | + // bring it up to speed |
1848 | + if (m_positionRequested) { |
1849 | + m_surface->setRequestedPosition(m_requestedPosition); |
1850 | + } |
1851 | + if (m_stateRequested) { |
1852 | + m_surface->requestState(m_state); |
1853 | + } |
1854 | + |
1855 | + // and sync with surface |
1856 | + updatePosition(); |
1857 | + updateState(); |
1858 | + updateFocused(); |
1859 | + } |
1860 | + |
1861 | + Q_EMIT surfaceChanged(surface); |
1862 | +} |
1863 | + |
1864 | +void Window::updatePosition() |
1865 | +{ |
1866 | + if (m_surface->position() != m_position) { |
1867 | + m_position = m_surface->position(); |
1868 | + Q_EMIT positionChanged(m_position); |
1869 | + } |
1870 | +} |
1871 | + |
1872 | +void Window::updateState() |
1873 | +{ |
1874 | + if (m_surface->state() != m_state) { |
1875 | + m_state = m_surface->state(); |
1876 | + Q_EMIT stateChanged(m_state); |
1877 | + } |
1878 | +} |
1879 | + |
1880 | +void Window::updateFocused() |
1881 | +{ |
1882 | + if (m_surface->focused() != m_focused) { |
1883 | + m_focused = m_surface->focused(); |
1884 | + Q_EMIT focusedChanged(m_focused); |
1885 | + } |
1886 | +} |
1887 | + |
1888 | +void Window::setFocused(bool value) |
1889 | +{ |
1890 | + if (value != m_focused) { |
1891 | + DEBUG_MSG << "(" << value << ")"; |
1892 | + m_focused = value; |
1893 | + Q_EMIT focusedChanged(m_focused); |
1894 | + // when we have a surface we get focus changes from updateFocused() instead |
1895 | + Q_ASSERT(!m_surface); |
1896 | + } |
1897 | +} |
1898 | + |
1899 | +QString Window::toString() const |
1900 | +{ |
1901 | + if (surface()) { |
1902 | + return QString("Window[0x%1, id=%2, MirSurface[0x%3,\"%4\"]]").arg( |
1903 | + QString::number((quintptr)this, 16), |
1904 | + QString::number(id()), |
1905 | + QString::number((quintptr)surface(), 16), |
1906 | + surface()->name()); |
1907 | + } else { |
1908 | + return QString("Window[0x%1, id=%2, null]").arg( |
1909 | + QString::number((quintptr)this, 0, 16), |
1910 | + QString::number(id())); |
1911 | + } |
1912 | +} |
1913 | + |
1914 | +QDebug operator<<(QDebug dbg, const Window *window) |
1915 | +{ |
1916 | + QDebugStateSaver saver(dbg); |
1917 | + dbg.nospace(); |
1918 | + |
1919 | + if (window) { |
1920 | + dbg << qPrintable(window->toString()); |
1921 | + } else { |
1922 | + dbg << (void*)(window); |
1923 | + } |
1924 | + |
1925 | + return dbg; |
1926 | +} |
1927 | |
1928 | === added file 'plugins/WindowManager/Window.h' |
1929 | --- plugins/WindowManager/Window.h 1970-01-01 00:00:00 +0000 |
1930 | +++ plugins/WindowManager/Window.h 2016-11-23 15:15:21 +0000 |
1931 | @@ -0,0 +1,161 @@ |
1932 | +/* |
1933 | + * Copyright (C) 2016 Canonical, Ltd. |
1934 | + * |
1935 | + * This program is free software; you can redistribute it and/or modify |
1936 | + * it under the terms of the GNU General Public License as published by |
1937 | + * the Free Software Foundation; version 3. |
1938 | + * |
1939 | + * This program is distributed in the hope that it will be useful, |
1940 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1941 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1942 | + * GNU General Public License for more details. |
1943 | + * |
1944 | + * You should have received a copy of the GNU General Public License |
1945 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1946 | + */ |
1947 | + |
1948 | +#ifndef QTMIR_WINDOW_H |
1949 | +#define QTMIR_WINDOW_H |
1950 | + |
1951 | +#include <QLoggingCategory> |
1952 | +#include <QObject> |
1953 | +#include <QPoint> |
1954 | + |
1955 | +// Unity API |
1956 | +#include <unity/shell/application/Mir.h> |
1957 | + |
1958 | +namespace unity { |
1959 | + namespace shell { |
1960 | + namespace application { |
1961 | + class MirSurfaceInterface; |
1962 | + } |
1963 | + } |
1964 | +} |
1965 | + |
1966 | + |
1967 | +Q_DECLARE_LOGGING_CATEGORY(UNITY_WINDOW) |
1968 | + |
1969 | +/** |
1970 | + @brief A slightly higher concept than MirSurface |
1971 | + |
1972 | + A Window exists before its MirSurface gets created (for splashscreen purposes) |
1973 | + and might also hang around after the backing surface is gone (In case the application |
1974 | + was killed to free up memory, as it should still remain in the window list since the user |
1975 | + did not explicitly close it). |
1976 | + */ |
1977 | +class Window : public QObject |
1978 | +{ |
1979 | + Q_OBJECT |
1980 | + |
1981 | + /** |
1982 | + * @brief Position of the current surface buffer, in pixels. |
1983 | + */ |
1984 | + Q_PROPERTY(QPoint position READ position NOTIFY positionChanged) |
1985 | + |
1986 | + /** |
1987 | + * @brief Requested position of the current surface buffer, in pixels. |
1988 | + */ |
1989 | + Q_PROPERTY(QPoint requestedPosition READ requestedPosition WRITE setRequestedPosition NOTIFY requestedPositionChanged) |
1990 | + |
1991 | + /** |
1992 | + * @brief State of the surface |
1993 | + */ |
1994 | + Q_PROPERTY(Mir::State state READ state NOTIFY stateChanged) |
1995 | + |
1996 | + /** |
1997 | + * @brief Whether the surface is focused |
1998 | + * |
1999 | + * It will be true if this surface is MirFocusControllerInterface::focusedSurface |
2000 | + */ |
2001 | + Q_PROPERTY(bool focused READ focused NOTIFY focusedChanged) |
2002 | + |
2003 | + /** |
2004 | + * @brief Whether the surface wants to confine the mouse pointer within its boundaries |
2005 | + * |
2006 | + * If true, the surface doesn't want the mouse pointer to leave its boundaries while it's focused. |
2007 | + */ |
2008 | + Q_PROPERTY(bool confinesMousePointer READ confinesMousePointer NOTIFY confinesMousePointerChanged) |
2009 | + |
2010 | + /** |
2011 | + * @brief A unique identifier for this window. |
2012 | + * Useful for telling windows apart in a list model as they get moved around |
2013 | + */ |
2014 | + Q_PROPERTY(int id READ id CONSTANT) |
2015 | + |
2016 | + /** |
2017 | + * @brief Surface backing up this window |
2018 | + * It might be null if a surface hasn't been created yet (application is starting up) or if |
2019 | + * the corresponding application has been killed (but can still get restarted to continue from |
2020 | + * where it left) |
2021 | + */ |
2022 | + Q_PROPERTY(unity::shell::application::MirSurfaceInterface* surface READ surface NOTIFY surfaceChanged) |
2023 | + |
2024 | +public: |
2025 | + Window(int id, QObject *parent = nullptr); |
2026 | + virtual ~Window(); |
2027 | + QPoint position() const; |
2028 | + QPoint requestedPosition() const; |
2029 | + void setRequestedPosition(const QPoint &); |
2030 | + Mir::State state() const; |
2031 | + bool focused() const; |
2032 | + bool confinesMousePointer() const; |
2033 | + int id() const; |
2034 | + unity::shell::application::MirSurfaceInterface* surface() const; |
2035 | + |
2036 | + void setSurface(unity::shell::application::MirSurfaceInterface *surface); |
2037 | + void setFocused(bool value); |
2038 | + |
2039 | + QString toString() const; |
2040 | + |
2041 | +public Q_SLOTS: |
2042 | + /** |
2043 | + * @brief Requests a change to the specified state |
2044 | + */ |
2045 | + void requestState(Mir::State state); |
2046 | + |
2047 | + /** |
2048 | + * @brief Sends a close request |
2049 | + * |
2050 | + */ |
2051 | + void close(); |
2052 | + |
2053 | + /** |
2054 | + * @brief Focuses and raises the window |
2055 | + */ |
2056 | + void activate(); |
2057 | + |
2058 | +Q_SIGNALS: |
2059 | + void closeRequested(); |
2060 | + void emptyWindowActivated(); |
2061 | + |
2062 | + void positionChanged(QPoint position); |
2063 | + void requestedPositionChanged(QPoint position); |
2064 | + void stateChanged(Mir::State value); |
2065 | + void focusedChanged(bool value); |
2066 | + void confinesMousePointerChanged(bool value); |
2067 | + void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface); |
2068 | + |
2069 | + /** |
2070 | + * @brief Emitted when focus for this window is requested by an external party |
2071 | + */ |
2072 | + void focusRequested(); |
2073 | + |
2074 | +private: |
2075 | + void updatePosition(); |
2076 | + void updateState(); |
2077 | + void updateFocused(); |
2078 | + |
2079 | + QPoint m_position; |
2080 | + QPoint m_requestedPosition; |
2081 | + bool m_positionRequested{false}; |
2082 | + bool m_focused{false}; |
2083 | + int m_id; |
2084 | + Mir::State m_state{Mir::RestoredState}; |
2085 | + bool m_stateRequested{false}; |
2086 | + unity::shell::application::MirSurfaceInterface *m_surface{nullptr}; |
2087 | +}; |
2088 | + |
2089 | +QDebug operator<<(QDebug dbg, const Window *window); |
2090 | + |
2091 | +Q_DECLARE_METATYPE(Window*) |
2092 | +#endif // QTMIR_WINDOW_H |
2093 | |
2094 | === modified file 'plugins/WindowManager/WindowManagerPlugin.cpp' |
2095 | --- plugins/WindowManager/WindowManagerPlugin.cpp 2016-04-04 13:37:49 +0000 |
2096 | +++ plugins/WindowManager/WindowManagerPlugin.cpp 2016-11-23 15:15:21 +0000 |
2097 | @@ -16,13 +16,16 @@ |
2098 | |
2099 | #include "WindowManagerPlugin.h" |
2100 | |
2101 | -#include "TopLevelSurfaceList.h" |
2102 | +#include "TopLevelWindowModel.h" |
2103 | +#include "Window.h" |
2104 | |
2105 | #include <QtQml> |
2106 | |
2107 | void WindowManagerPlugin::registerTypes(const char *uri) |
2108 | { |
2109 | - qmlRegisterType<TopLevelSurfaceList>(uri, 0, 1, "TopLevelSurfaceList"); |
2110 | + qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel"); |
2111 | + |
2112 | + qRegisterMetaType<Window*>("Window*"); |
2113 | |
2114 | qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*"); |
2115 | } |
2116 | |
2117 | === modified file 'qml/Components/InputMethod.qml' |
2118 | --- qml/Components/InputMethod.qml 2016-10-06 13:06:31 +0000 |
2119 | +++ qml/Components/InputMethod.qml 2016-11-23 15:15:21 +0000 |
2120 | @@ -23,6 +23,8 @@ |
2121 | |
2122 | readonly property rect visibleRect: surfaceItem.surface && visible ? surfaceItem.surface.inputBounds : Qt.rect(0, 0, 0, 0) |
2123 | |
2124 | + property var surface |
2125 | + |
2126 | MirSurfaceItem { |
2127 | id: surfaceItem |
2128 | anchors.fill: parent |
2129 | @@ -31,7 +33,7 @@ |
2130 | |
2131 | surfaceWidth: root.enabled ? width : -1 |
2132 | surfaceHeight: root.enabled ? height : -1 |
2133 | - surface: SurfaceManager.inputMethodSurface |
2134 | + surface: root.surface |
2135 | |
2136 | onLiveChanged: { |
2137 | if (surface !== null && !live) { |
2138 | @@ -50,7 +52,6 @@ |
2139 | } |
2140 | |
2141 | visible: surfaceItem.surface && |
2142 | - surfaceItem.surfaceState != Mir.HiddenState && |
2143 | - surfaceItem.surfaceState != Mir.MinimizedState && |
2144 | - root.enabled |
2145 | + surfaceItem.surface.visible && |
2146 | + root.enabled |
2147 | } |
2148 | |
2149 | === modified file 'qml/Components/KeymapSwitcher.qml' |
2150 | --- qml/Components/KeymapSwitcher.qml 2016-07-11 14:40:56 +0000 |
2151 | +++ qml/Components/KeymapSwitcher.qml 2016-11-23 15:15:21 +0000 |
2152 | @@ -23,6 +23,9 @@ |
2153 | QtObject { |
2154 | id: root |
2155 | |
2156 | + // to be set from outside |
2157 | + property var focusedSurface: null |
2158 | + |
2159 | property GlobalShortcut shortcutNext: GlobalShortcut { |
2160 | shortcut: Qt.MetaModifier|Qt.Key_Space |
2161 | onTriggered: root.nextKeymap() |
2162 | @@ -60,7 +63,7 @@ |
2163 | } |
2164 | |
2165 | property Binding surfaceKeymapBinding: Binding { |
2166 | - target: MirFocusController.focusedSurface |
2167 | + target: root.focusedSurface |
2168 | property: "keymap" |
2169 | value: root.currentKeymap |
2170 | } |
2171 | |
2172 | === modified file 'qml/Shell.qml' |
2173 | --- qml/Shell.qml 2016-11-09 15:07:26 +0000 |
2174 | +++ qml/Shell.qml 2016-11-23 15:15:21 +0000 |
2175 | @@ -43,7 +43,7 @@ |
2176 | import Unity.DashCommunicator 0.1 |
2177 | import Unity.Indicators 0.1 as Indicators |
2178 | import Cursor 1.1 |
2179 | -import WindowManager 0.1 |
2180 | +import WindowManager 1.0 |
2181 | |
2182 | |
2183 | StyledItem { |
2184 | @@ -264,10 +264,15 @@ |
2185 | width: parent.width |
2186 | height: parent.height |
2187 | |
2188 | - TopLevelSurfaceList { |
2189 | + SurfaceManager { |
2190 | + id: surfaceMan |
2191 | + objectName: "surfaceManager" |
2192 | + } |
2193 | + TopLevelWindowModel { |
2194 | id: topLevelSurfaceList |
2195 | objectName: "topLevelSurfaceList" |
2196 | - applicationsModel: ApplicationManager |
2197 | + applicationManager: ApplicationManager // it's a singleton |
2198 | + surfaceManager: surfaceMan |
2199 | } |
2200 | |
2201 | Stage { |
2202 | @@ -320,6 +325,7 @@ |
2203 | InputMethod { |
2204 | id: inputMethod |
2205 | objectName: "inputMethod" |
2206 | + surface: topLevelSurfaceList.inputMethodSurface |
2207 | anchors { |
2208 | fill: parent |
2209 | topMargin: panel.panelHeight |
2210 | @@ -486,8 +492,8 @@ |
2211 | greeterShown: greeter.shown |
2212 | } |
2213 | |
2214 | - readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface |
2215 | - ? MirFocusController.focusedSurface.state === Mir.FullscreenState |
2216 | + readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow |
2217 | + ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState |
2218 | : false |
2219 | fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0) |
2220 | || greeter.hasLockedApp |
2221 | @@ -787,7 +793,9 @@ |
2222 | } |
2223 | |
2224 | // non-visual object |
2225 | - KeymapSwitcher {} |
2226 | + KeymapSwitcher { |
2227 | + focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null |
2228 | + } |
2229 | |
2230 | Rectangle { |
2231 | id: shutdownFadeOutRectangle |
2232 | |
2233 | === modified file 'qml/Stage/DecoratedWindow.qml' |
2234 | --- qml/Stage/DecoratedWindow.qml 2016-10-07 11:20:09 +0000 |
2235 | +++ qml/Stage/DecoratedWindow.qml 2016-11-23 15:15:21 +0000 |
2236 | @@ -149,7 +149,7 @@ |
2237 | |
2238 | WindowDecoration { |
2239 | id: decoration |
2240 | - target: root.parent || null |
2241 | + closeButtonVisible: root.application.appId !== "unity8-dash" |
2242 | objectName: "appWindowDecoration" |
2243 | anchors { left: parent.left; top: parent.top; right: parent.right } |
2244 | height: units.gu(3) |
2245 | |
2246 | === modified file 'qml/Stage/FakeMaximizeDelegate.qml' |
2247 | --- qml/Stage/FakeMaximizeDelegate.qml 2016-09-13 11:53:40 +0000 |
2248 | +++ qml/Stage/FakeMaximizeDelegate.qml 2016-11-23 15:15:21 +0000 |
2249 | @@ -98,19 +98,19 @@ |
2250 | function commit() { |
2251 | if (progress > hintThreshold && edge != -1) { |
2252 | if (edge == Item.Top) { |
2253 | - target.maximize(); |
2254 | + target.requestMaximize(); |
2255 | } else if (edge == Item.Left) { |
2256 | - target.maximizeLeft(); |
2257 | + target.requestMaximizeLeft(); |
2258 | } else if (edge == Item.Right) { |
2259 | - target.maximizeRight(); |
2260 | + target.requestMaximizeRight(); |
2261 | } else if (edge == Item.TopLeft) { |
2262 | - target.maximizeTopLeft(); |
2263 | + target.requestMaximizeTopLeft(); |
2264 | } else if (edge == Item.TopRight) { |
2265 | - target.maximizeTopRight(); |
2266 | + target.requestMaximizeTopRight(); |
2267 | } else if (edge == Item.BottomLeft) { |
2268 | - target.maximizeBottomLeft(); |
2269 | + target.requestMaximizeBottomLeft(); |
2270 | } else if (edge == Item.BottomRight) { |
2271 | - target.maximizeBottomRight(); |
2272 | + target.requestMaximizeBottomRight(); |
2273 | } |
2274 | } else { |
2275 | stop(); |
2276 | |
2277 | === modified file 'qml/Stage/Stage.qml' |
2278 | --- qml/Stage/Stage.qml 2016-11-12 16:05:40 +0000 |
2279 | +++ qml/Stage/Stage.qml 2016-11-23 15:15:21 +0000 |
2280 | @@ -117,8 +117,7 @@ |
2281 | } |
2282 | } |
2283 | |
2284 | - property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.surface && |
2285 | - priv.focusedAppDelegate.surface.confinesMousePointer ? |
2286 | + property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window.confinesMousePointer ? |
2287 | priv.focusedAppDelegate.clientAreaItem : null; |
2288 | |
2289 | signal itemSnapshotRequested(Item item) |
2290 | @@ -181,29 +180,34 @@ |
2291 | GlobalShortcut { |
2292 | id: maximizeWindowShortcut |
2293 | shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up |
2294 | - onTriggered: priv.focusedAppDelegate.maximize() |
2295 | + onTriggered: priv.focusedAppDelegate.requestMaximize() |
2296 | active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized |
2297 | } |
2298 | |
2299 | GlobalShortcut { |
2300 | id: maximizeWindowLeftShortcut |
2301 | shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left |
2302 | - onTriggered: priv.focusedAppDelegate.maximizeLeft() |
2303 | + onTriggered: priv.focusedAppDelegate.requestMaximizeLeft() |
2304 | active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight |
2305 | } |
2306 | |
2307 | GlobalShortcut { |
2308 | id: maximizeWindowRightShortcut |
2309 | shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right |
2310 | - onTriggered: priv.focusedAppDelegate.maximizeRight() |
2311 | + onTriggered: priv.focusedAppDelegate.requestMaximizeRight() |
2312 | active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight |
2313 | } |
2314 | |
2315 | GlobalShortcut { |
2316 | id: minimizeRestoreShortcut |
2317 | shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down |
2318 | - onTriggered: priv.focusedAppDelegate.anyMaximized |
2319 | - ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize() |
2320 | + onTriggered: { |
2321 | + if (priv.focusedAppDelegate.anyMaximized) { |
2322 | + priv.focusedAppDelegate.requestRestore(); |
2323 | + } else { |
2324 | + priv.focusedAppDelegate.requestMinimize(); |
2325 | + } |
2326 | + } |
2327 | active: root.state == "windowed" && priv.focusedAppDelegate |
2328 | } |
2329 | |
2330 | @@ -239,20 +243,10 @@ |
2331 | } |
2332 | |
2333 | function minimizeAllWindows() { |
2334 | - for (var i = 0; i < appRepeater.count; i++) { |
2335 | - var appDelegate = appRepeater.itemAt(i); |
2336 | - if (appDelegate && !appDelegate.minimized) { |
2337 | - appDelegate.minimize(); |
2338 | - } |
2339 | - } |
2340 | - } |
2341 | - |
2342 | - function focusNext() { |
2343 | - for (var i = 0; i < appRepeater.count; i++) { |
2344 | - var appDelegate = appRepeater.itemAt(i); |
2345 | - if (appDelegate && !appDelegate.minimized) { |
2346 | - appDelegate.focus = true; |
2347 | - return; |
2348 | + for (var i = appRepeater.count - 1; i >= 0; i--) { |
2349 | + var appDelegate = appRepeater.itemAt(i); |
2350 | + if (appDelegate && !appDelegate.minimized) { |
2351 | + appDelegate.requestMinimize(); |
2352 | } |
2353 | } |
2354 | } |
2355 | @@ -353,8 +347,8 @@ |
2356 | Connections { |
2357 | target: PanelState |
2358 | onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } } |
2359 | - onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } } |
2360 | - onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } } |
2361 | + onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } } |
2362 | + onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } } |
2363 | } |
2364 | |
2365 | Binding { |
2366 | @@ -429,13 +423,6 @@ |
2367 | } |
2368 | } |
2369 | |
2370 | - Binding { |
2371 | - target: MirFocusController |
2372 | - property: "focusedSurface" |
2373 | - value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null |
2374 | - when: !appRepeater.startingUp && root.parent |
2375 | - } |
2376 | - |
2377 | states: [ |
2378 | State { |
2379 | name: "spread"; when: priv.goneToSpread |
2380 | @@ -628,7 +615,7 @@ |
2381 | |
2382 | onShownChanged: { |
2383 | if (!shown && priv.mainStageDelegate && !root.spreadShown) { |
2384 | - priv.mainStageDelegate.claimFocus(); |
2385 | + priv.mainStageDelegate.activate(); |
2386 | } |
2387 | } |
2388 | |
2389 | @@ -661,14 +648,23 @@ |
2390 | } |
2391 | } |
2392 | |
2393 | - TopLevelSurfaceRepeater { |
2394 | + Repeater { |
2395 | id: appRepeater |
2396 | model: topLevelSurfaceList |
2397 | objectName: "appRepeater" |
2398 | |
2399 | + function indexOf(delegateItem) { |
2400 | + for (var i = 0; i < count; i++) { |
2401 | + if (itemAt(i) === delegateItem) { |
2402 | + return i; |
2403 | + } |
2404 | + } |
2405 | + return -1; |
2406 | + } |
2407 | + |
2408 | delegate: FocusScope { |
2409 | id: appDelegate |
2410 | - objectName: "appDelegate_" + model.id |
2411 | + objectName: "appDelegate_" + model.window.id |
2412 | property int itemIndex: index // We need this from outside the repeater |
2413 | // z might be overriden in some cases by effects, but we need z ordering |
2414 | // to calculate occlusion detection |
2415 | @@ -680,11 +676,11 @@ |
2416 | } |
2417 | z: normalZ |
2418 | |
2419 | - // Normally we want x/y where we request it to be. Width/height of our delegate will |
2420 | + // Normally we want x/y where the surface thinks it is. Width/height of our delegate will |
2421 | // match what the actual surface size is. |
2422 | // Don't write to those, they will be set by states |
2423 | - x: requestedX |
2424 | - y: requestedY |
2425 | + x: model.window.position.x - clientAreaItem.x |
2426 | + y: model.window.position.y - clientAreaItem.y |
2427 | width: decoratedWindow.implicitWidth |
2428 | height: decoratedWindow.implicitHeight |
2429 | |
2430 | @@ -694,6 +690,12 @@ |
2431 | property real requestedY: windowedY |
2432 | property real requestedWidth: windowedWidth |
2433 | property real requestedHeight: windowedHeight |
2434 | + Binding { |
2435 | + target: model.window; property: "requestedPosition" |
2436 | + // miral doesn't know about our window decorations. So we have to deduct them |
2437 | + value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x, |
2438 | + appDelegate.requestedY + appDelegate.clientAreaItem.y) |
2439 | + } |
2440 | |
2441 | // In those are for windowed mode. Those values basically store the window's properties |
2442 | // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to. |
2443 | @@ -762,7 +764,7 @@ |
2444 | maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight |
2445 | |
2446 | readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized |
2447 | - readonly property bool fullscreen: surface ? surface.state === Mir.FullscreenState : application.fullscreen |
2448 | + readonly property bool fullscreen: window.state === Mir.FullscreenState |
2449 | |
2450 | readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically |
2451 | readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) && |
2452 | @@ -788,7 +790,9 @@ |
2453 | priv.updateMainAndSideStageIndexes() |
2454 | } |
2455 | |
2456 | - readonly property var surface: model.surface |
2457 | + readonly property var surface: model.window.surface |
2458 | + readonly property var window: model.window |
2459 | + |
2460 | readonly property alias resizeArea: resizeArea |
2461 | readonly property alias focusedSurface: decoratedWindow.focusedSurface |
2462 | readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging |
2463 | @@ -797,6 +801,25 @@ |
2464 | readonly property bool isDash: appId == "unity8-dash" |
2465 | readonly property alias clientAreaItem: decoratedWindow.clientAreaItem |
2466 | |
2467 | + function activate() { |
2468 | + if (model.window.focused) { |
2469 | + updateQmlFocusFromMirSurfaceFocus(); |
2470 | + } else { |
2471 | + model.window.activate(); |
2472 | + } |
2473 | + } |
2474 | + function requestMaximize() { model.window.requestState(Mir.MaximizedState); } |
2475 | + function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); } |
2476 | + function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); } |
2477 | + function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); } |
2478 | + function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); } |
2479 | + function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); } |
2480 | + function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); } |
2481 | + function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); } |
2482 | + function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); } |
2483 | + function requestMinimize() { model.window.requestState(Mir.MinimizedState); } |
2484 | + function requestRestore() { model.window.requestState(Mir.RestoredState); } |
2485 | + |
2486 | function claimFocus() { |
2487 | if (root.state == "spread") { |
2488 | spreadItem.highlightedIndex = index |
2489 | @@ -808,45 +831,58 @@ |
2490 | } |
2491 | priv.updateMainAndSideStageIndexes(); |
2492 | } |
2493 | - |
2494 | if (root.mode == "windowed") { |
2495 | appDelegate.restore(true /* animated */, appDelegate.windowState); |
2496 | - } else { |
2497 | - appDelegate.focus = true; |
2498 | } |
2499 | + appDelegate.focus = true; |
2500 | } |
2501 | - Connections { |
2502 | - target: model.surface |
2503 | - onFocusRequested: { |
2504 | + |
2505 | + function updateQmlFocusFromMirSurfaceFocus() { |
2506 | + if (model.window.focused) { |
2507 | claimFocus(); |
2508 | - } |
2509 | - } |
2510 | - Connections { |
2511 | - target: model.application |
2512 | - onFocusRequested: { |
2513 | - if (!model.surface) { |
2514 | - // when an app has no surfaces, we assume there's only one entry representing it: |
2515 | - // this delegate. |
2516 | - claimFocus(); |
2517 | - } else { |
2518 | - // if the application has surfaces, focus request should be at surface-level. |
2519 | - } |
2520 | - } |
2521 | - } |
2522 | - |
2523 | - onFocusChanged: { |
2524 | - if (appRepeater.startingUp) |
2525 | - return; |
2526 | - |
2527 | - if (focus) { |
2528 | - topLevelSurfaceList.raiseId(model.id); |
2529 | priv.focusedAppDelegate = appDelegate; |
2530 | - } else if (!focus && priv.focusedAppDelegate === appDelegate && root.state != "spread") { |
2531 | - priv.focusedAppDelegate = null; |
2532 | - // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null |
2533 | - MirFocusController.focusedSurface = null; |
2534 | - } |
2535 | - } |
2536 | + } |
2537 | + } |
2538 | + |
2539 | + Connections { |
2540 | + target: model.window |
2541 | + onFocusedChanged: { |
2542 | + updateQmlFocusFromMirSurfaceFocus(); |
2543 | + } |
2544 | + onFocusRequested: { |
2545 | + appDelegate.activate(); |
2546 | + } |
2547 | + onStateChanged: { |
2548 | + if (model.window.state === Mir.MinimizedState) { |
2549 | + appDelegate.minimize(); |
2550 | + } else if (model.window.state === Mir.MaximizedState) { |
2551 | + appDelegate.maximize(); |
2552 | + } else if (model.window.state === Mir.VertMaximizedState) { |
2553 | + appDelegate.maximizeVertically(); |
2554 | + } else if (model.window.state === Mir.HorizMaximizedState) { |
2555 | + appDelegate.maximizeHorizontally(); |
2556 | + } else if (model.window.state === Mir.MaximizedLeftState) { |
2557 | + appDelegate.maximizeLeft(); |
2558 | + } else if (model.window.state === Mir.MaximizedRightState) { |
2559 | + appDelegate.maximizeRight(); |
2560 | + } else if (model.window.state === Mir.MaximizedTopLeftState) { |
2561 | + appDelegate.maximizeTopLeft(); |
2562 | + } else if (model.window.state === Mir.MaximizedTopRightState) { |
2563 | + appDelegate.maximizeTopRight(); |
2564 | + } else if (model.window.state === Mir.MaximizedBottomLeftState) { |
2565 | + appDelegate.maximizeBottomLeft(); |
2566 | + } else if (model.window.state === Mir.MaximizedBottomRightState) { |
2567 | + appDelegate.maximizeBottomRight(); |
2568 | + } else if (model.window.state === Mir.RestoredState) { |
2569 | + if (appDelegate.anyMaximized) { |
2570 | + appDelegate.restoreFromMaximized() |
2571 | + } else { |
2572 | + appDelegate.restore(); |
2573 | + } |
2574 | + } |
2575 | + } |
2576 | + } |
2577 | + |
2578 | Component.onCompleted: { |
2579 | if (application && application.rotatesWindowContents) { |
2580 | decoratedWindow.surfaceOrientationAngle = shellOrientationAngle; |
2581 | @@ -860,16 +896,7 @@ |
2582 | // Now load any saved state. This needs to happen *after* the cascading! |
2583 | resizeArea.loadWindowState(); |
2584 | |
2585 | - // NB: We're differentiating if this delegate was created in response to a new entry in the model |
2586 | - // or if the Repeater is just populating itself with delegates to match the model it received. |
2587 | - if (!appRepeater.startingUp) { |
2588 | - // a top level window is always the focused one when it first appears, unfocusing |
2589 | - // any preexisting one |
2590 | - if (root.state == "spread") { |
2591 | - spreadItem.highlightedIndex = index; |
2592 | - } |
2593 | - claimFocus(); |
2594 | - } |
2595 | + updateQmlFocusFromMirSurfaceFocus(); |
2596 | |
2597 | refreshStage(); |
2598 | _constructing = false; |
2599 | @@ -884,16 +911,6 @@ |
2600 | priv.updateForegroundMaximizedApp(); |
2601 | } |
2602 | |
2603 | - if (focus) { |
2604 | - // focus some other window |
2605 | - for (var i = 0; i < appRepeater.count; i++) { |
2606 | - var appDelegate = appRepeater.itemAt(i); |
2607 | - if (appDelegate && !appDelegate.minimized && i != index) { |
2608 | - appDelegate.focus = true; |
2609 | - return; |
2610 | - } |
2611 | - } |
2612 | - } |
2613 | } |
2614 | |
2615 | onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp() |
2616 | @@ -914,7 +931,7 @@ |
2617 | || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running |
2618 | |
2619 | function close() { |
2620 | - model.surface.close(); |
2621 | + model.window.close(); |
2622 | } |
2623 | |
2624 | function maximize(animated) { |
2625 | @@ -965,7 +982,6 @@ |
2626 | animationsEnabled = (animated === undefined) || animated; |
2627 | windowState = state || WindowStateStorage.WindowStateRestored; |
2628 | windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit |
2629 | - focus = true; |
2630 | } |
2631 | |
2632 | function playFocusAnimation() { |
2633 | @@ -979,7 +995,7 @@ |
2634 | rightEdgeFocusAnimation.start() |
2635 | } |
2636 | } else if (state == "windowedRightEdge" || state == "windowed") { |
2637 | - claimFocus(); |
2638 | + activate(); |
2639 | } else { |
2640 | focusAnimation.start() |
2641 | } |
2642 | @@ -1017,10 +1033,10 @@ |
2643 | to: 1 |
2644 | duration: UbuntuAnimation.SnapDuration |
2645 | onStarted: { |
2646 | - topLevelSurfaceList.raiseId(model.id); |
2647 | + topLevelSurfaceList.raiseId(model.window.id); |
2648 | } |
2649 | onStopped: { |
2650 | - appDelegate.claimFocus(); |
2651 | + appDelegate.activate(); |
2652 | } |
2653 | } |
2654 | ParallelAnimation { |
2655 | @@ -1030,7 +1046,7 @@ |
2656 | UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration } |
2657 | UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration } |
2658 | onStopped: { |
2659 | - appDelegate.focus = true |
2660 | + appDelegate.activate(); |
2661 | } |
2662 | } |
2663 | ParallelAnimation { |
2664 | @@ -1394,14 +1410,6 @@ |
2665 | SequentialAnimation { |
2666 | UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" } |
2667 | PropertyAction { target: appDelegate; property: "visuallyMinimized" } |
2668 | - ScriptAction { |
2669 | - script: { |
2670 | - if (appDelegate.minimized) { |
2671 | - appDelegate.focus = false; |
2672 | - priv.focusNext(); |
2673 | - } |
2674 | - } |
2675 | - } |
2676 | } |
2677 | }, |
2678 | Transition { |
2679 | @@ -1492,7 +1500,7 @@ |
2680 | visible: enabled |
2681 | |
2682 | onPressed: { |
2683 | - appDelegate.focus = true; |
2684 | + appDelegate.activate(); |
2685 | } |
2686 | |
2687 | Component.onDestruction: { |
2688 | @@ -1506,7 +1514,7 @@ |
2689 | anchors.left: appDelegate.left |
2690 | anchors.top: appDelegate.top |
2691 | application: model.application |
2692 | - surface: model.surface |
2693 | + surface: model.window.surface |
2694 | active: appDelegate.focus |
2695 | focus: true |
2696 | interactive: root.interactive |
2697 | @@ -1529,11 +1537,23 @@ |
2698 | onRequestedHeightChanged: oldRequestedHeight = requestedHeight |
2699 | |
2700 | onCloseClicked: { appDelegate.close(); } |
2701 | - onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize(); |
2702 | - onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally() |
2703 | - onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically() |
2704 | - onMinimizeClicked: appDelegate.minimize() |
2705 | - onDecorationPressed: { appDelegate.focus = true; } |
2706 | + onMaximizeClicked: { |
2707 | + if (appDelegate.canBeMaximized) { |
2708 | + appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize(); |
2709 | + } |
2710 | + } |
2711 | + onMaximizeHorizontallyClicked: { |
2712 | + if (appDelegate.canBeMaximizedHorizontally) { |
2713 | + appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally() |
2714 | + } |
2715 | + } |
2716 | + onMaximizeVerticallyClicked: { |
2717 | + if (appDelegate.canBeMaximizedVertically) { |
2718 | + appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically() |
2719 | + } |
2720 | + } |
2721 | + onMinimizeClicked: { appDelegate.requestMinimize(); } |
2722 | + onDecorationPressed: { appDelegate.activate(); } |
2723 | onDecorationReleased: fakeRectangle.commit(); |
2724 | |
2725 | property real angle: 0 |
2726 | @@ -1580,12 +1600,12 @@ |
2727 | WindowedFullscreenPolicy { |
2728 | id: windowedFullscreenPolicy |
2729 | active: root.mode == "windowed" |
2730 | - surface: model.surface |
2731 | + surface: model.window.surface |
2732 | } |
2733 | StagedFullscreenPolicy { |
2734 | id: stagedFullscreenPolicy |
2735 | active: root.mode == "staged" || root.mode == "stagedWithSideStage" |
2736 | - surface: model.surface |
2737 | + surface: model.window.surface |
2738 | } |
2739 | |
2740 | SpreadDelegateInputArea { |
2741 | @@ -1598,16 +1618,13 @@ |
2742 | onClicked: { |
2743 | spreadItem.highlightedIndex = index; |
2744 | if (distance == 0) { |
2745 | + model.window.activate(); |
2746 | priv.goneToSpread = false; |
2747 | } |
2748 | } |
2749 | onClose: { |
2750 | priv.closingIndex = index |
2751 | - if (model.surface) { // could be stopped by OOM |
2752 | - model.surface.close() |
2753 | - } else if (model.application) { |
2754 | - root.applicationManager.stopApplication(model.application.appId); |
2755 | - } |
2756 | + model.window.close(); |
2757 | } |
2758 | } |
2759 | |
2760 | |
2761 | === modified file 'qml/Stage/StagedFullscreenPolicy.qml' |
2762 | --- qml/Stage/StagedFullscreenPolicy.qml 2016-04-04 13:41:19 +0000 |
2763 | +++ qml/Stage/StagedFullscreenPolicy.qml 2016-11-23 15:15:21 +0000 |
2764 | @@ -32,7 +32,7 @@ |
2765 | onSurfaceChanged: { |
2766 | if (!active || !surface) return; |
2767 | if (surface.shellChrome === Mir.LowChrome) { |
2768 | - surface.state = Mir.FullscreenState; |
2769 | + surface.requestState(Mir.FullscreenState); |
2770 | } |
2771 | } |
2772 | |
2773 | @@ -41,15 +41,15 @@ |
2774 | onShellChromeChanged: { |
2775 | if (!active || !surface) return; |
2776 | if (surface.shellChrome === Mir.LowChrome) { |
2777 | - surface.state = Mir.FullscreenState; |
2778 | + surface.requestState(Mir.FullscreenState); |
2779 | } else { |
2780 | - surface.state = Mir.RestoredState; |
2781 | + surface.requestState(Mir.RestoredState); |
2782 | } |
2783 | } |
2784 | onStateChanged: { |
2785 | if (!active) return; |
2786 | if (surface.state === Mir.RestoredState && surface.shellChrome === Mir.LowChrome) { |
2787 | - surface.state = Mir.FullscreenState; |
2788 | + surface.requestState(Mir.FullscreenState); |
2789 | } |
2790 | } |
2791 | } |
2792 | |
2793 | === removed file 'qml/Stage/TopLevelSurfaceRepeater.qml' |
2794 | --- qml/Stage/TopLevelSurfaceRepeater.qml 2016-09-12 17:05:43 +0000 |
2795 | +++ qml/Stage/TopLevelSurfaceRepeater.qml 1970-01-01 00:00:00 +0000 |
2796 | @@ -1,67 +0,0 @@ |
2797 | -/* |
2798 | - * Copyright (C) 2016 Canonical, Ltd. |
2799 | - * |
2800 | - * This program is free software; you can redistribute it and/or modify |
2801 | - * it under the terms of the GNU General Public License as published by |
2802 | - * the Free Software Foundation; version 3. |
2803 | - * |
2804 | - * This program is distributed in the hope that it will be useful, |
2805 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2806 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2807 | - * GNU General Public License for more details. |
2808 | - * |
2809 | - * You should have received a copy of the GNU General Public License |
2810 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2811 | - */ |
2812 | - |
2813 | -import QtQuick 2.4 |
2814 | - |
2815 | -Repeater { |
2816 | - id: root |
2817 | - // FIXME: This is a hack around us not knowing whether the Repeater has finished creating its |
2818 | - // delegates on start up. |
2819 | - // This is a problem when the stage gets a TopLevelSurfaceList already populated with several |
2820 | - // rows. |
2821 | - property bool startingUp: true |
2822 | - onStartingUpChanged: { |
2823 | - if (!startingUp) { |
2824 | - // the top-most surface must be the focused one. |
2825 | - var topmostDelegate = itemAt(0); |
2826 | - if (topmostDelegate.focus) { |
2827 | - // Delegate got focused while we were still starting up. Not good. |
2828 | - // Force signal handler to run again |
2829 | - topmostDelegate.onFocusChanged(true); |
2830 | - } else { |
2831 | - topmostDelegate.focus = true; |
2832 | - } |
2833 | - } |
2834 | - } |
2835 | - |
2836 | - onItemAdded: { |
2837 | - if (startingUp) { |
2838 | - checkIfStillStartingUp(); |
2839 | - } |
2840 | - } |
2841 | - |
2842 | - function checkIfStillStartingUp() { |
2843 | - var i = 0; |
2844 | - var missingDelegate = false; |
2845 | - for (i = 0; i < model.count && !missingDelegate; ++i) { |
2846 | - if (!itemAt(i)) { |
2847 | - missingDelegate = true; |
2848 | - } |
2849 | - } |
2850 | - if (!missingDelegate) { |
2851 | - startingUp = false; |
2852 | - } |
2853 | - } |
2854 | - |
2855 | - function indexOf(delegateItem) { |
2856 | - for (var i = 0; i < count; i++) { |
2857 | - if (itemAt(i) === delegateItem) { |
2858 | - return i; |
2859 | - } |
2860 | - } |
2861 | - return -1; |
2862 | - } |
2863 | -} |
2864 | |
2865 | === modified file 'qml/Stage/WindowDecoration.qml' |
2866 | --- qml/Stage/WindowDecoration.qml 2016-09-22 10:33:39 +0000 |
2867 | +++ qml/Stage/WindowDecoration.qml 2016-11-23 15:15:21 +0000 |
2868 | @@ -22,7 +22,7 @@ |
2869 | id: root |
2870 | clip: true |
2871 | |
2872 | - property Item target // appDelegate |
2873 | + property alias closeButtonVisible: buttons.closeButtonShown |
2874 | property alias title: titleLabel.text |
2875 | property alias maximizeButtonShown: buttons.maximizeButtonShown |
2876 | property bool active: false |
2877 | @@ -40,7 +40,7 @@ |
2878 | signal maximizeVerticallyClicked() |
2879 | |
2880 | onDoubleClicked: { |
2881 | - if (target.canBeMaximized && mouse.button == Qt.LeftButton) { |
2882 | + if (mouse.button == Qt.LeftButton) { |
2883 | root.maximizeClicked(); |
2884 | } |
2885 | } |
2886 | @@ -77,9 +77,8 @@ |
2887 | onCloseClicked: root.closeClicked(); |
2888 | onMinimizeClicked: root.minimizeClicked(); |
2889 | onMaximizeClicked: root.maximizeClicked(); |
2890 | - onMaximizeHorizontallyClicked: if (root.target.canBeMaximizedHorizontally) root.maximizeHorizontallyClicked(); |
2891 | - onMaximizeVerticallyClicked: if (root.target.canBeMaximizedVertically) root.maximizeVerticallyClicked(); |
2892 | - closeButtonShown: root.target.appId !== "unity8-dash" |
2893 | + onMaximizeHorizontallyClicked: root.maximizeHorizontallyClicked(); |
2894 | + onMaximizeVerticallyClicked: root.maximizeVerticallyClicked(); |
2895 | } |
2896 | |
2897 | Label { |
2898 | |
2899 | === modified file 'qml/Stage/WindowedFullscreenPolicy.qml' |
2900 | --- qml/Stage/WindowedFullscreenPolicy.qml 2016-04-04 13:37:49 +0000 |
2901 | +++ qml/Stage/WindowedFullscreenPolicy.qml 2016-11-23 15:15:21 +0000 |
2902 | @@ -33,7 +33,7 @@ |
2903 | _firstTimeSurface = false; |
2904 | |
2905 | if (surface.state === Mir.FullscreenState && surface.shellChrome === Mir.LowChrome) { |
2906 | - surface.state = Mir.RestoredState; |
2907 | + surface.requestState(Mir.RestoredState); |
2908 | } |
2909 | } |
2910 | } |
2911 | |
2912 | === modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp' |
2913 | --- tests/mocks/Unity/Application/ApplicationInfo.cpp 2016-09-12 17:05:43 +0000 |
2914 | +++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2016-11-23 15:15:21 +0000 |
2915 | @@ -75,6 +75,31 @@ |
2916 | { |
2917 | } |
2918 | |
2919 | +void ApplicationInfo::createPromptSurface() |
2920 | +{ |
2921 | + if (state() == ApplicationInfo::Stopped) { return; } |
2922 | + |
2923 | + auto surfaceManager = SurfaceManager::instance(); |
2924 | + if (!surfaceManager) { |
2925 | + WARNING_MSG("No SurfaceManager"); |
2926 | + return; |
2927 | + } |
2928 | + |
2929 | + QStringList screenshotIds = {"gallery", "map", "facebook", "camera", "browser", "music", "twitter"}; |
2930 | + int i = rand() % screenshotIds.count(); |
2931 | + |
2932 | + QUrl screenshotUrl = QString("qrc:///Unity/Application/screenshots/%1@12.png") |
2933 | + .arg(screenshotIds[i]); |
2934 | + |
2935 | + auto surface = surfaceManager->createSurface(QString("prompt foo"), |
2936 | + Mir::NormalType, |
2937 | + Mir::RestoredState, |
2938 | + screenshotUrl); |
2939 | + surfaceManager->notifySurfaceCreated(surface); |
2940 | + |
2941 | + m_promptSurfaceList->addSurface(surface); |
2942 | +} |
2943 | + |
2944 | void ApplicationInfo::createSurface() |
2945 | { |
2946 | if (state() == ApplicationInfo::Stopped) { return; } |
2947 | @@ -90,14 +115,16 @@ |
2948 | return; |
2949 | } |
2950 | |
2951 | + bool wasFocused = focused(); |
2952 | + |
2953 | auto surface = surfaceManager->createSurface(surfaceName, |
2954 | Mir::NormalType, |
2955 | - fullscreen() ? Mir::FullscreenState : Mir::MaximizedState, |
2956 | + fullscreen() ? Mir::FullscreenState : Mir::RestoredState, |
2957 | m_screenshotFileName); |
2958 | |
2959 | surface->setShellChrome(m_shellChrome); |
2960 | |
2961 | - m_surfaceList->appendSurface(surface); |
2962 | + m_surfaceList->addSurface(surface); |
2963 | |
2964 | ++m_liveSurfaceCount; |
2965 | connect(surface, &MirSurface::liveChanged, this, [this, surface](){ |
2966 | @@ -125,8 +152,19 @@ |
2967 | setState(Running); |
2968 | } |
2969 | }); |
2970 | + connect(surface, &MirSurfaceInterface::focusedChanged, this, [&](bool /*value*/) { |
2971 | + #if APPLICATION_DEBUG |
2972 | + qDebug().nospace() << "Application[" << appId() << "].focusedChanged(" << focused() << ")"; |
2973 | + #endif |
2974 | + Q_EMIT focusedChanged(focused()); |
2975 | + }); |
2976 | + |
2977 | connect(surface, &MirSurface::focusRequested, this, &ApplicationInfo::focusRequested); |
2978 | |
2979 | + if (wasFocused != focused()) { |
2980 | + Q_EMIT focusedChanged(focused()); |
2981 | + } |
2982 | + |
2983 | if (m_state == Starting) { |
2984 | if (m_requestedState == RequestedRunning) { |
2985 | setState(Running); |
2986 | @@ -134,6 +172,8 @@ |
2987 | setState(Suspended); |
2988 | } |
2989 | } |
2990 | + |
2991 | + surfaceManager->notifySurfaceCreated(surface); |
2992 | } |
2993 | |
2994 | void ApplicationInfo::setIconId(const QString &iconId) |
2995 | @@ -218,7 +258,7 @@ |
2996 | { |
2997 | m_fullscreen = value; |
2998 | if (m_surfaceList->rowCount() > 0) { |
2999 | - m_surfaceList->get(0)->setState(Mir::FullscreenState); |
3000 | + m_surfaceList->get(0)->requestState(Mir::FullscreenState); |
3001 | } |
3002 | } |
3003 | |
3004 | @@ -349,26 +389,6 @@ |
3005 | return someSurfaceHasFocus; |
3006 | } |
3007 | |
3008 | -void ApplicationInfo::setFocused(bool value) |
3009 | -{ |
3010 | - if (focused() == value) { |
3011 | - return; |
3012 | - } |
3013 | - |
3014 | - if (value) { |
3015 | - if (m_surfaceList->count() > 0) { |
3016 | - m_surfaceList->get(0)->requestFocus(); |
3017 | - } |
3018 | - } else { |
3019 | - for (int i = 0; i < m_surfaceList->count(); ++i) { |
3020 | - MirSurface *surface = static_cast<MirSurface*>(m_surfaceList->get(i)); |
3021 | - if (surface->focused()) { |
3022 | - surface->setFocused(false); |
3023 | - } |
3024 | - } |
3025 | - } |
3026 | -} |
3027 | - |
3028 | void ApplicationInfo::onSurfaceCountChanged() |
3029 | { |
3030 | if (m_surfaceList->count() == 0 && m_state == Running) { |
3031 | @@ -381,6 +401,7 @@ |
3032 | if (m_surfaceList->count() == 0) { |
3033 | Q_EMIT focusRequested(); |
3034 | } else { |
3035 | - m_surfaceList->get(0)->requestFocus(); |
3036 | + auto surface = static_cast<MirSurface*>(m_surfaceList->get(0)); |
3037 | + surface->requestFocus(); |
3038 | } |
3039 | } |
3040 | |
3041 | === modified file 'tests/mocks/Unity/Application/ApplicationInfo.h' |
3042 | --- tests/mocks/Unity/Application/ApplicationInfo.h 2016-07-25 14:57:11 +0000 |
3043 | +++ tests/mocks/Unity/Application/ApplicationInfo.h 2016-11-23 15:15:21 +0000 |
3044 | @@ -51,6 +51,8 @@ |
3045 | ApplicationInfo(const QString &appId, QObject *parent = nullptr); |
3046 | ~ApplicationInfo(); |
3047 | |
3048 | + Q_INVOKABLE void createPromptSurface(); |
3049 | + |
3050 | RequestedState requestedState() const override; |
3051 | void setRequestedState(RequestedState) override; |
3052 | |
3053 | @@ -108,8 +110,6 @@ |
3054 | MirSurfaceListInterface* promptSurfaceList() const override { return m_promptSurfaceList; } |
3055 | int surfaceCount() const override { return m_surfaceList->count(); } |
3056 | |
3057 | - void setFocused(bool value); |
3058 | - |
3059 | ////// |
3060 | // internal mock stuff |
3061 | void close(); |
3062 | |
3063 | === modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp' |
3064 | --- tests/mocks/Unity/Application/ApplicationManager.cpp 2016-08-23 12:04:11 +0000 |
3065 | +++ tests/mocks/Unity/Application/ApplicationManager.cpp 2016-11-23 15:15:21 +0000 |
3066 | @@ -34,30 +34,24 @@ |
3067 | |
3068 | #if APPLICATIONMANAGER_DEBUG |
3069 | #define DEBUG_MSG(params) qDebug().nospace() << "ApplicationManager::" << __func__ << " " << params |
3070 | +#define XDEBUG_MSG(params) qDebug().nospace() << "ApplicationManager::" << params |
3071 | #else |
3072 | #define DEBUG_MSG(params) ((void)0) |
3073 | +#define XDEBUG_MSG(params) ((void)0) |
3074 | #endif |
3075 | |
3076 | namespace unityapi = unity::shell::application; |
3077 | |
3078 | + |
3079 | ApplicationManager::ApplicationManager(QObject *parent) |
3080 | : ApplicationManagerInterface(parent) |
3081 | { |
3082 | DEBUG_MSG(""); |
3083 | + |
3084 | + ApplicationManagerNotifier::instance()->setApplicationManager(this); |
3085 | + |
3086 | buildListOfAvailableApplications(); |
3087 | |
3088 | - // polling to find out when the toplevel window has been created as there's |
3089 | - // no signal telling us that |
3090 | - connect(&m_windowCreatedTimer, &QTimer::timeout, |
3091 | - this, &ApplicationManager::onWindowCreatedTimerTimeout); |
3092 | - m_windowCreatedTimer.setSingleShot(false); |
3093 | - m_windowCreatedTimer.start(200); |
3094 | - |
3095 | - Q_ASSERT(MirFocusController::instance()); |
3096 | - connect(MirFocusController::instance(), &MirFocusController::focusedSurfaceChanged, |
3097 | - this, &ApplicationManager::updateFocusedApplication, Qt::QueuedConnection); |
3098 | - |
3099 | - |
3100 | // Emit signal to notify Upstart that Mir is ready to receive client connections |
3101 | // see http://upstart.ubuntu.com/cookbook/#expect-stop |
3102 | // We do this because some autopilot tests actually use this mock Unity.Application module, |
3103 | @@ -69,19 +63,7 @@ |
3104 | |
3105 | ApplicationManager::~ApplicationManager() |
3106 | { |
3107 | -} |
3108 | - |
3109 | -void ApplicationManager::onWindowCreatedTimerTimeout() |
3110 | -{ |
3111 | - if (QGuiApplication::topLevelWindows().count() > 0) { |
3112 | - m_windowCreatedTimer.stop(); |
3113 | - onWindowCreated(); |
3114 | - } |
3115 | -} |
3116 | - |
3117 | -void ApplicationManager::onWindowCreated() |
3118 | -{ |
3119 | - startApplication("unity8-dash"); |
3120 | + ApplicationManagerNotifier::instance()->setApplicationManager(nullptr); |
3121 | } |
3122 | |
3123 | int ApplicationManager::rowCount(const QModelIndex& parent) const { |
3124 | @@ -132,6 +114,17 @@ |
3125 | return nullptr; |
3126 | } |
3127 | |
3128 | +unityapi::ApplicationInfoInterface *ApplicationManager::findApplicationWithSurface(unityapi::MirSurfaceInterface* surface) |
3129 | +{ |
3130 | + for (ApplicationInfo *app : m_runningApplications) { |
3131 | + auto surfaceList = static_cast<MirSurfaceListModel*>(app->surfaceList()); |
3132 | + if (surfaceList->contains(static_cast<MirSurface*>(surface))) { |
3133 | + return app; |
3134 | + } |
3135 | + } |
3136 | + return nullptr; |
3137 | +} |
3138 | + |
3139 | QModelIndex ApplicationManager::findIndex(ApplicationInfo* application) |
3140 | { |
3141 | for (int i = 0; i < m_runningApplications.size(); ++i) { |
3142 | @@ -158,6 +151,11 @@ |
3143 | QModelIndex appIndex = findIndex(application); |
3144 | if (!appIndex.isValid()) return; |
3145 | Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleFocused); |
3146 | + XDEBUG_MSG("focusedApplicationId = " << focusedApplicationId()); |
3147 | + Q_EMIT focusedApplicationIdChanged(); |
3148 | + if (application->focused()) { |
3149 | + raiseApp(application->appId()); |
3150 | + } |
3151 | }); |
3152 | connect(application, &ApplicationInfo::stateChanged, this, [application, this]() { |
3153 | QModelIndex appIndex = findIndex(application); |
3154 | @@ -181,15 +179,40 @@ |
3155 | |
3156 | void ApplicationManager::remove(ApplicationInfo *application) { |
3157 | int i = m_runningApplications.indexOf(application); |
3158 | + application->disconnect(this); |
3159 | if (i != -1) { |
3160 | DEBUG_MSG(application->appId()); |
3161 | + Q_ASSERT(!m_modelBusy); |
3162 | + m_modelBusy = true; |
3163 | beginRemoveRows(QModelIndex(), i, i); |
3164 | m_runningApplications.removeAt(i); |
3165 | endRemoveRows(); |
3166 | + m_modelBusy = false; |
3167 | Q_EMIT countChanged(); |
3168 | if (isEmpty()) Q_EMIT emptyChanged(isEmpty()); |
3169 | - } |
3170 | - application->disconnect(this); |
3171 | + DEBUG_MSG(application->appId() << " after: " << qPrintable(toString())); |
3172 | + } |
3173 | +} |
3174 | + |
3175 | +void ApplicationManager::raiseApp(const QString &appId) |
3176 | +{ |
3177 | + |
3178 | + int index = -1; |
3179 | + for (int i = 0; i < m_runningApplications.count() && index == -1; ++i) { |
3180 | + if (m_runningApplications[i]->appId() == appId) { |
3181 | + index = i; |
3182 | + } |
3183 | + } |
3184 | + |
3185 | + if (index >= 0) { |
3186 | + if (m_modelBusy) { |
3187 | + DEBUG_MSG(appId << " - model busy. Try again later."); |
3188 | + QMetaObject::invokeMethod(this, "raiseApp", Qt::QueuedConnection, Q_ARG(QString, appId)); |
3189 | + } else { |
3190 | + DEBUG_MSG(appId); |
3191 | + move(index, 0); |
3192 | + } |
3193 | + } |
3194 | } |
3195 | |
3196 | void ApplicationManager::move(int from, int to) { |
3197 | @@ -197,12 +220,15 @@ |
3198 | |
3199 | if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) { |
3200 | QModelIndex parent; |
3201 | + Q_ASSERT(!m_modelBusy); |
3202 | + m_modelBusy = true; |
3203 | /* When moving an item down, the destination index needs to be incremented |
3204 | * by one, as explained in the documentation: |
3205 | * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ |
3206 | beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); |
3207 | m_runningApplications.move(from, to); |
3208 | endMoveRows(); |
3209 | + m_modelBusy = false; |
3210 | } |
3211 | } |
3212 | |
3213 | @@ -473,40 +499,6 @@ |
3214 | return m_runningApplications.isEmpty(); |
3215 | } |
3216 | |
3217 | -void ApplicationManager::updateFocusedApplication() |
3218 | -{ |
3219 | - ApplicationInfo *focusedApplication = nullptr; |
3220 | - ApplicationInfo *previouslyFocusedApplication = nullptr; |
3221 | - |
3222 | - auto controller = MirFocusController::instance(); |
3223 | - if (!controller) { |
3224 | - return; |
3225 | - } |
3226 | - |
3227 | - MirSurface *surface = static_cast<MirSurface*>(controller->focusedSurface()); |
3228 | - if (surface) { |
3229 | - focusedApplication = findApplication(surface); |
3230 | - } |
3231 | - |
3232 | - surface = static_cast<MirSurface*>(controller->previouslyFocusedSurface()); |
3233 | - if (surface) { |
3234 | - previouslyFocusedApplication = findApplication(surface); |
3235 | - } |
3236 | - |
3237 | - if (focusedApplication != previouslyFocusedApplication) { |
3238 | - if (focusedApplication) { |
3239 | - DEBUG_MSG("focused " << focusedApplication->appId()); |
3240 | - Q_EMIT focusedApplication->focusedChanged(true); |
3241 | - this->move(this->m_runningApplications.indexOf(focusedApplication), 0); |
3242 | - } |
3243 | - if (previouslyFocusedApplication) { |
3244 | - DEBUG_MSG("unfocused " << previouslyFocusedApplication->appId()); |
3245 | - Q_EMIT previouslyFocusedApplication->focusedChanged(false); |
3246 | - } |
3247 | - Q_EMIT focusedApplicationIdChanged(); |
3248 | - } |
3249 | -} |
3250 | - |
3251 | ApplicationInfo *ApplicationManager::findApplication(MirSurface* surface) |
3252 | { |
3253 | for (ApplicationInfo *app : m_runningApplications) { |
3254 | @@ -517,3 +509,42 @@ |
3255 | } |
3256 | return nullptr; |
3257 | } |
3258 | + |
3259 | +QString ApplicationManager::toString() |
3260 | +{ |
3261 | + QString str; |
3262 | + for (int i = 0; i < m_runningApplications.count(); ++i) { |
3263 | + auto *application = m_runningApplications.at(i); |
3264 | + |
3265 | + QString itemStr = QString("(index=%1,appId=%2)") |
3266 | + .arg(i) |
3267 | + .arg(application->appId()); |
3268 | + |
3269 | + if (i > 0) { |
3270 | + str.append(","); |
3271 | + } |
3272 | + str.append(itemStr); |
3273 | + } |
3274 | + return str; |
3275 | +} |
3276 | + |
3277 | +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
3278 | +// ApplicationManagerNotifier |
3279 | + |
3280 | +ApplicationManagerNotifier *ApplicationManagerNotifier::m_instance = nullptr; |
3281 | + |
3282 | +ApplicationManagerNotifier *ApplicationManagerNotifier::instance() |
3283 | +{ |
3284 | + if (!m_instance) { |
3285 | + m_instance = new ApplicationManagerNotifier; |
3286 | + } |
3287 | + return m_instance; |
3288 | +} |
3289 | + |
3290 | +void ApplicationManagerNotifier::setApplicationManager(ApplicationManager *appMan) |
3291 | +{ |
3292 | + if (appMan != m_applicationManager) { |
3293 | + m_applicationManager = appMan; |
3294 | + Q_EMIT applicationManagerChanged(m_applicationManager); |
3295 | + } |
3296 | +} |
3297 | |
3298 | === modified file 'tests/mocks/Unity/Application/ApplicationManager.h' |
3299 | --- tests/mocks/Unity/Application/ApplicationManager.h 2016-04-27 15:01:10 +0000 |
3300 | +++ tests/mocks/Unity/Application/ApplicationManager.h 2016-11-23 15:15:21 +0000 |
3301 | @@ -50,8 +50,11 @@ |
3302 | // QAbstractItemModel methods. |
3303 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; |
3304 | QVariant data(const QModelIndex& index, int role) const override; |
3305 | + |
3306 | + // ApplicationManagerInterface methods |
3307 | Q_INVOKABLE ApplicationInfo *get(int index) const override; |
3308 | Q_INVOKABLE ApplicationInfo *findApplication(const QString &appId) const override; |
3309 | + unity::shell::application::ApplicationInfoInterface *findApplicationWithSurface(unity::shell::application::MirSurfaceInterface* surface) override; |
3310 | |
3311 | Q_INVOKABLE void move(int from, int to); |
3312 | |
3313 | @@ -76,18 +79,42 @@ |
3314 | void availableApplicationsChanged(QStringList list); |
3315 | |
3316 | private Q_SLOTS: |
3317 | - void onWindowCreatedTimerTimeout(); |
3318 | - void updateFocusedApplication(); |
3319 | + void raiseApp(const QString &appId); |
3320 | |
3321 | private: |
3322 | bool add(ApplicationInfo *application); |
3323 | void remove(ApplicationInfo* application); |
3324 | void buildListOfAvailableApplications(); |
3325 | - void onWindowCreated(); |
3326 | + QString toString(); |
3327 | ApplicationInfo *findApplication(MirSurface* surface); |
3328 | QList<ApplicationInfo*> m_runningApplications; |
3329 | QList<ApplicationInfo*> m_availableApplications; |
3330 | - QTimer m_windowCreatedTimer; |
3331 | + bool m_modelBusy{false}; |
3332 | +}; |
3333 | + |
3334 | +/* |
3335 | + Lifecycle of the ApplicationManager instance belongs to the QML plugin. |
3336 | + So this guy here is used to notify other parts of the system when the plugin creates and destroys |
3337 | + the ApplicationManager. |
3338 | + |
3339 | + Unlike ApplicationManager, we create ApplicationManagerNotifier whenever we want. |
3340 | + */ |
3341 | +class ApplicationManagerNotifier : public QObject { |
3342 | + Q_OBJECT |
3343 | +public: |
3344 | + static ApplicationManagerNotifier *instance(); |
3345 | + |
3346 | + ApplicationManager *applicationManager() { return m_applicationManager; } |
3347 | + |
3348 | +Q_SIGNALS: |
3349 | + void applicationManagerChanged(ApplicationManager *applicationManager); |
3350 | + |
3351 | +private: |
3352 | + void setApplicationManager(ApplicationManager *); |
3353 | + static ApplicationManagerNotifier *m_instance; |
3354 | + ApplicationManager *m_applicationManager{nullptr}; |
3355 | + |
3356 | +friend class ApplicationManager; |
3357 | }; |
3358 | |
3359 | Q_DECLARE_METATYPE(ApplicationManager*) |
3360 | |
3361 | === modified file 'tests/mocks/Unity/Application/CMakeLists.txt' |
3362 | --- tests/mocks/Unity/Application/CMakeLists.txt 2016-06-30 13:51:32 +0000 |
3363 | +++ tests/mocks/Unity/Application/CMakeLists.txt 2016-11-23 15:15:21 +0000 |
3364 | @@ -14,7 +14,7 @@ |
3365 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h |
3366 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceItemInterface.h |
3367 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h |
3368 | - ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirFocusControllerInterface.h |
3369 | + ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h |
3370 | resources/surfaces.qrc |
3371 | ) |
3372 | |
3373 | |
3374 | === modified file 'tests/mocks/Unity/Application/MirSurface.cpp' |
3375 | --- tests/mocks/Unity/Application/MirSurface.cpp 2016-09-26 12:25:19 +0000 |
3376 | +++ tests/mocks/Unity/Application/MirSurface.cpp 2016-11-23 15:15:21 +0000 |
3377 | @@ -19,14 +19,18 @@ |
3378 | #include <QDebug> |
3379 | #include <QQmlEngine> |
3380 | |
3381 | +// local |
3382 | +#include "SurfaceManager.h" |
3383 | + |
3384 | #define MIRSURFACE_DEBUG 0 |
3385 | |
3386 | #if MIRSURFACE_DEBUG |
3387 | #define DEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << __func__ << " " << params |
3388 | +#define XDEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << params |
3389 | |
3390 | const char *stateToStr(Mir::State state) |
3391 | { |
3392 | - switch(state) { |
3393 | + switch (state) { |
3394 | case Mir::UnknownState: |
3395 | return "unknown"; |
3396 | case Mir::RestoredState: |
3397 | @@ -36,26 +40,37 @@ |
3398 | case Mir::MaximizedState: |
3399 | return "maximized"; |
3400 | case Mir::VertMaximizedState: |
3401 | - return "vert-maximized"; |
3402 | + return "vertMaximized"; |
3403 | case Mir::FullscreenState: |
3404 | return "fullscreen"; |
3405 | case Mir::HorizMaximizedState: |
3406 | - return "horiz-maximized"; |
3407 | + return "horizMaximized"; |
3408 | + case Mir::MaximizedLeftState: |
3409 | + return "maximizedLeft"; |
3410 | + case Mir::MaximizedRightState: |
3411 | + return "maximizedRight"; |
3412 | + case Mir::MaximizedTopLeftState: |
3413 | + return "maximizedTopLeft"; |
3414 | + case Mir::MaximizedTopRightState: |
3415 | + return "maximizedTopRight"; |
3416 | + case Mir::MaximizedBottomLeftState: |
3417 | + return "maximizedBottomLeft"; |
3418 | + case Mir::MaximizedBottomRightState: |
3419 | + return "maximizedBottomRight"; |
3420 | case Mir::HiddenState: |
3421 | return "hidden"; |
3422 | default: |
3423 | return "???"; |
3424 | - }; |
3425 | + } |
3426 | } |
3427 | |
3428 | #else |
3429 | #define DEBUG_MSG(params) ((void)0) |
3430 | +#define XDEBUG_MSG(params) ((void)0) |
3431 | #endif |
3432 | |
3433 | using namespace unity::shell::application; |
3434 | |
3435 | -MirFocusController *MirFocusController::m_instance = nullptr; |
3436 | - |
3437 | MirSurface::MirSurface(const QString& name, |
3438 | Mir::Type type, |
3439 | Mir::State state, |
3440 | @@ -69,7 +84,7 @@ |
3441 | , m_screenshotUrl(screenshot) |
3442 | , m_qmlFilePath(qmlFilePath) |
3443 | , m_live(true) |
3444 | - , m_visible(true) |
3445 | + , m_focused(false) |
3446 | , m_activeFocus(false) |
3447 | , m_width(-1) |
3448 | , m_height(-1) |
3449 | @@ -95,11 +110,8 @@ |
3450 | { |
3451 | DEBUG_MSG(""); |
3452 | |
3453 | - // controller instance might have been already destroyed by QQmlEngine destructor |
3454 | - auto controller = MirFocusController::instance(); |
3455 | - if (controller && controller->focusedSurface() == this) { |
3456 | - controller->clear(); |
3457 | - } |
3458 | + // Early warning, while MirSurface methods can still be accessed. |
3459 | + Q_EMIT destroyed(this); |
3460 | } |
3461 | |
3462 | QString MirSurface::name() const |
3463 | @@ -122,14 +134,31 @@ |
3464 | return m_state; |
3465 | } |
3466 | |
3467 | +void MirSurface::requestState(Mir::State state) |
3468 | +{ |
3469 | + if (state == m_state) { |
3470 | + return; |
3471 | + } |
3472 | + DEBUG_MSG(stateToStr(state)); |
3473 | + Q_EMIT stateRequested(state); |
3474 | +} |
3475 | + |
3476 | void MirSurface::setState(Mir::State state) |
3477 | { |
3478 | - if (state == m_state) |
3479 | + if (state == m_state) { |
3480 | return; |
3481 | - |
3482 | + } |
3483 | DEBUG_MSG(stateToStr(state)); |
3484 | + |
3485 | + bool oldVisible = visible(); |
3486 | + |
3487 | m_state = state; |
3488 | Q_EMIT stateChanged(state); |
3489 | + |
3490 | + if (visible() != oldVisible) { |
3491 | + XDEBUG_MSG("visibleChanged("<<visible()<<")"); |
3492 | + Q_EMIT visibleChanged(visible()); |
3493 | + } |
3494 | } |
3495 | |
3496 | bool MirSurface::live() const |
3497 | @@ -139,7 +168,7 @@ |
3498 | |
3499 | bool MirSurface::visible() const |
3500 | { |
3501 | - return m_visible; |
3502 | + return m_state != Mir::MinimizedState && m_state != Mir::HiddenState; |
3503 | } |
3504 | |
3505 | void MirSurface::setLive(bool live) |
3506 | @@ -231,31 +260,30 @@ |
3507 | if (!m_live && m_views.count() == 0) { |
3508 | deleteLater(); |
3509 | } |
3510 | - updateVisibility(); |
3511 | + updateExposure(); |
3512 | } |
3513 | |
3514 | -void MirSurface::setViewVisibility(qintptr viewId, bool visible) |
3515 | +void MirSurface::setViewExposure(qintptr viewId, bool visible) |
3516 | { |
3517 | if (!m_views.contains(viewId)) return; |
3518 | |
3519 | m_views[viewId].visible = visible; |
3520 | - updateVisibility(); |
3521 | + updateExposure(); |
3522 | } |
3523 | |
3524 | -void MirSurface::updateVisibility() |
3525 | +void MirSurface::updateExposure() |
3526 | { |
3527 | - bool newVisible = false; |
3528 | + bool newExposure = false; |
3529 | QHashIterator<qintptr, View> i(m_views); |
3530 | while (i.hasNext()) { |
3531 | i.next(); |
3532 | - newVisible |= i.value().visible; |
3533 | + newExposure |= i.value().visible; |
3534 | } |
3535 | |
3536 | - if (newVisible != visible()) { |
3537 | -// qDebug().nospace() << "MirSurface[" << name() << "]::updateVisibility(" << newVisible << ")"; |
3538 | - |
3539 | - m_visible = newVisible; |
3540 | - Q_EMIT visibleChanged(m_visible); |
3541 | + if (newExposure != m_exposed) { |
3542 | + m_exposed = newExposure; |
3543 | + DEBUG_MSG(m_exposed); |
3544 | + Q_EMIT exposedChanged(m_exposed); |
3545 | updateInputBoundsAfterResize(); |
3546 | } |
3547 | } |
3548 | @@ -273,6 +301,10 @@ |
3549 | m_activeFocus = value; |
3550 | |
3551 | Q_EMIT activeFocusChanged(value); |
3552 | + |
3553 | + if (m_activeFocus && !m_focused) { |
3554 | + requestFocus(); |
3555 | + } |
3556 | } |
3557 | |
3558 | int MirSurface::width() const |
3559 | @@ -332,6 +364,7 @@ |
3560 | } |
3561 | |
3562 | if (changed) { |
3563 | + XDEBUG_MSG("sizeChanged(width="<<width<<", height="<<height<<")"); |
3564 | Q_EMIT sizeChanged(QSize(width, height)); |
3565 | } |
3566 | |
3567 | @@ -409,11 +442,6 @@ |
3568 | } |
3569 | } |
3570 | |
3571 | -void MirSurface::raise() |
3572 | -{ |
3573 | - Q_EMIT raiseRequested(); |
3574 | -} |
3575 | - |
3576 | void MirSurface::close() |
3577 | { |
3578 | DEBUG_MSG(""); |
3579 | @@ -423,6 +451,12 @@ |
3580 | } |
3581 | } |
3582 | |
3583 | +void MirSurface::activate() |
3584 | +{ |
3585 | + DEBUG_MSG(""); |
3586 | + SurfaceManager::instance()->activate(this); |
3587 | +} |
3588 | + |
3589 | void MirSurface::requestFocus() |
3590 | { |
3591 | DEBUG_MSG(""); |
3592 | @@ -431,27 +465,18 @@ |
3593 | |
3594 | void MirSurface::setFocused(bool value) |
3595 | { |
3596 | - DEBUG_MSG(value); |
3597 | - |
3598 | - auto controller = MirFocusController::instance(); |
3599 | - // controller instance might have been already destroyed by QQmlEngine destructor |
3600 | - if (!controller) { |
3601 | + if (m_focused == value) |
3602 | return; |
3603 | - } |
3604 | - |
3605 | - if (value) { |
3606 | - controller->setFocusedSurface(this); |
3607 | - } else if (controller->focusedSurface() == this) { |
3608 | - controller->setFocusedSurface(nullptr); |
3609 | - } |
3610 | + |
3611 | + DEBUG_MSG("(" << value << ")"); |
3612 | + |
3613 | + m_focused = value; |
3614 | + Q_EMIT focusedChanged(value); |
3615 | } |
3616 | |
3617 | bool MirSurface::focused() const |
3618 | { |
3619 | - auto controller = MirFocusController::instance(); |
3620 | - |
3621 | - // controller instance might have been already destroyed by QQmlEngine destructor |
3622 | - return controller ? controller->focusedSurface() == this : false; |
3623 | + return m_focused; |
3624 | } |
3625 | |
3626 | QRect MirSurface::inputBounds() const |
3627 | @@ -463,58 +488,20 @@ |
3628 | { |
3629 | if (boundsRect != m_inputBounds) { |
3630 | m_inputBounds = boundsRect; |
3631 | + DEBUG_MSG("(" << m_inputBounds << ")"); |
3632 | Q_EMIT inputBoundsChanged(m_inputBounds); |
3633 | } |
3634 | } |
3635 | -#if MIRSURFACE_DEBUG |
3636 | -#undef DEBUG_MSG |
3637 | -#define DEBUG_MSG(params) qDebug().nospace() << "MirFocusController::" << __func__ << " " << params |
3638 | -#endif |
3639 | - |
3640 | -void MirFocusController::setFocusedSurface(MirSurfaceInterface *surface) |
3641 | -{ |
3642 | - if (m_focusedSurface == surface) { |
3643 | - return; |
3644 | - } |
3645 | - DEBUG_MSG("MirSurface[" << (void*)surface << "," << (surface?surface->name():"") << "]"); |
3646 | - |
3647 | - m_previouslyFocusedSurface = m_focusedSurface; |
3648 | - m_focusedSurface = surface; |
3649 | - |
3650 | - if (m_previouslyFocusedSurface != m_focusedSurface) { |
3651 | - Q_EMIT focusedSurfaceChanged(); |
3652 | - } |
3653 | - |
3654 | - if (m_previouslyFocusedSurface) { |
3655 | - Q_EMIT m_previouslyFocusedSurface->focusedChanged(false); |
3656 | - } |
3657 | - |
3658 | - if (m_focusedSurface) { |
3659 | - Q_EMIT m_focusedSurface->focusedChanged(true); |
3660 | - m_focusedSurface->raise(); |
3661 | - } |
3662 | -} |
3663 | - |
3664 | -MirFocusController* MirFocusController::instance() |
3665 | -{ |
3666 | - return m_instance; |
3667 | -} |
3668 | - |
3669 | -MirFocusController::MirFocusController() |
3670 | -{ |
3671 | - DEBUG_MSG(""); |
3672 | - Q_ASSERT(m_instance == nullptr); |
3673 | - m_instance = this; |
3674 | -} |
3675 | - |
3676 | -MirFocusController::~MirFocusController() |
3677 | -{ |
3678 | - Q_ASSERT(m_instance == this); |
3679 | - m_instance = nullptr; |
3680 | -} |
3681 | - |
3682 | -void MirFocusController::clear() |
3683 | -{ |
3684 | - m_focusedSurface = m_previouslyFocusedSurface = nullptr; |
3685 | - Q_EMIT focusedSurfaceChanged(); |
3686 | + |
3687 | +void MirSurface::setRequestedPosition(const QPoint &value) |
3688 | +{ |
3689 | + if (value != m_requestedPosition) { |
3690 | + m_requestedPosition = value; |
3691 | + Q_EMIT requestedPositionChanged(value); |
3692 | + |
3693 | + // fake-miral: always comply |
3694 | + m_position = m_requestedPosition; |
3695 | + XDEBUG_MSG("positionChanged("<<m_position<<")"); |
3696 | + Q_EMIT positionChanged(m_position); |
3697 | + } |
3698 | } |
3699 | |
3700 | === modified file 'tests/mocks/Unity/Application/MirSurface.h' |
3701 | --- tests/mocks/Unity/Application/MirSurface.h 2016-09-07 08:50:19 +0000 |
3702 | +++ tests/mocks/Unity/Application/MirSurface.h 2016-11-23 15:15:21 +0000 |
3703 | @@ -23,31 +23,12 @@ |
3704 | #include <QHash> |
3705 | |
3706 | // unity-api |
3707 | -#include <unity/shell/application/MirFocusControllerInterface.h> |
3708 | #include <unity/shell/application/MirSurfaceInterface.h> |
3709 | |
3710 | #include "MirSurfaceListModel.h" |
3711 | |
3712 | class MirSurface; |
3713 | |
3714 | -class MirFocusController : public unity::shell::application::MirFocusControllerInterface |
3715 | -{ |
3716 | - Q_OBJECT |
3717 | -public: |
3718 | - MirFocusController(); |
3719 | - virtual ~MirFocusController(); |
3720 | - static MirFocusController* instance(); |
3721 | - |
3722 | - void setFocusedSurface(unity::shell::application::MirSurfaceInterface *surface) override; |
3723 | - unity::shell::application::MirSurfaceInterface* focusedSurface() const override { return m_focusedSurface; } |
3724 | - unity::shell::application::MirSurfaceInterface* previouslyFocusedSurface() { return m_previouslyFocusedSurface; } |
3725 | - void clear(); |
3726 | -private: |
3727 | - static MirFocusController *m_instance; |
3728 | - unity::shell::application::MirSurfaceInterface* m_previouslyFocusedSurface{nullptr}; |
3729 | - unity::shell::application::MirSurfaceInterface* m_focusedSurface{nullptr}; |
3730 | -}; |
3731 | - |
3732 | class MirSurface : public unity::shell::application::MirSurfaceInterface |
3733 | { |
3734 | Q_OBJECT |
3735 | @@ -58,6 +39,7 @@ |
3736 | Q_PROPERTY(int height READ height NOTIFY heightChanged) |
3737 | Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged) |
3738 | Q_PROPERTY(bool slowToResize READ isSlowToResize WRITE setSlowToResize NOTIFY slowToResizeChanged) |
3739 | + Q_PROPERTY(bool exposed READ exposed NOTIFY exposedChanged) |
3740 | |
3741 | public: |
3742 | MirSurface(const QString& name, |
3743 | @@ -76,13 +58,14 @@ |
3744 | |
3745 | QString persistentId() const override; |
3746 | |
3747 | + QPoint position() const override { return m_position; } |
3748 | + |
3749 | QSize size() const override { return QSize(width(),height()); } |
3750 | void resize(int width, int height) override; |
3751 | void resize(const QSize &size) override { resize(size.width(), size.height()); } |
3752 | |
3753 | |
3754 | Mir::State state() const override; |
3755 | - Q_INVOKABLE void setState(Mir::State) override; |
3756 | |
3757 | bool live() const override; |
3758 | |
3759 | @@ -108,15 +91,16 @@ |
3760 | |
3761 | bool confinesMousePointer() const override { return false; } |
3762 | |
3763 | - Q_INVOKABLE void requestFocus() override; |
3764 | + QPoint requestedPosition() const override { return m_requestedPosition; } |
3765 | + void setRequestedPosition(const QPoint &) override; |
3766 | |
3767 | Q_INVOKABLE void close() override; |
3768 | - |
3769 | - Q_INVOKABLE void raise() override; |
3770 | + Q_INVOKABLE void activate() override; |
3771 | |
3772 | //// |
3773 | // API for tests |
3774 | |
3775 | + Q_INVOKABLE void requestFocus(); |
3776 | Q_INVOKABLE void setLive(bool live); |
3777 | Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome); |
3778 | |
3779 | @@ -126,6 +110,8 @@ |
3780 | bool isSlowToResize() const; |
3781 | void setSlowToResize(bool value); |
3782 | |
3783 | + bool exposed() const { return m_exposed; } |
3784 | + |
3785 | Q_INVOKABLE void setMinimumWidth(int); |
3786 | Q_INVOKABLE void setMaximumWidth(int); |
3787 | Q_INVOKABLE void setMinimumHeight(int); |
3788 | @@ -148,24 +134,32 @@ |
3789 | |
3790 | void registerView(qintptr viewId); |
3791 | void unregisterView(qintptr viewId); |
3792 | - void setViewVisibility(qintptr viewId, bool visible); |
3793 | + void setViewExposure(qintptr viewId, bool visible); |
3794 | int viewCount() const { return m_views.count(); } |
3795 | |
3796 | void setFocused(bool value); |
3797 | |
3798 | + void setState(Mir::State state); |
3799 | + |
3800 | +public Q_SLOTS: |
3801 | + //// |
3802 | + // unity.shell.application.MirSurface |
3803 | + void requestState(Mir::State) override; |
3804 | + |
3805 | Q_SIGNALS: |
3806 | //// |
3807 | // API for tests |
3808 | void widthChanged(); |
3809 | void heightChanged(); |
3810 | void slowToResizeChanged(); |
3811 | + void exposedChanged(bool exposed); |
3812 | |
3813 | //// |
3814 | // internal mock stuff |
3815 | void screenshotUrlChanged(QUrl); |
3816 | void activeFocusChanged(bool); |
3817 | - void raiseRequested(); |
3818 | void closeRequested(); |
3819 | + void stateRequested(Mir::State); |
3820 | |
3821 | protected: |
3822 | virtual void updateInputBoundsAfterResize(); |
3823 | @@ -175,7 +169,7 @@ |
3824 | |
3825 | private: |
3826 | void doResize(int width, int height); |
3827 | - void updateVisibility(); |
3828 | + void updateExposure(); |
3829 | |
3830 | const QString m_name; |
3831 | const Mir::Type m_type; |
3832 | @@ -185,7 +179,7 @@ |
3833 | QUrl m_screenshotUrl; |
3834 | QUrl m_qmlFilePath; |
3835 | bool m_live; |
3836 | - bool m_visible; |
3837 | + bool m_focused; |
3838 | bool m_activeFocus; |
3839 | int m_width; |
3840 | int m_height; |
3841 | @@ -210,10 +204,14 @@ |
3842 | bool visible; |
3843 | }; |
3844 | QHash<qintptr, View> m_views; |
3845 | + bool m_exposed{false}; |
3846 | |
3847 | QTimer m_zombieTimer; |
3848 | |
3849 | QRect m_inputBounds; |
3850 | + |
3851 | + QPoint m_position; |
3852 | + QPoint m_requestedPosition; |
3853 | }; |
3854 | |
3855 | #endif // MOCK_MIR_SURFACE_H |
3856 | |
3857 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.cpp' |
3858 | --- tests/mocks/Unity/Application/MirSurfaceItem.cpp 2016-08-23 11:11:32 +0000 |
3859 | +++ tests/mocks/Unity/Application/MirSurfaceItem.cpp 2016-11-23 15:15:21 +0000 |
3860 | @@ -59,7 +59,7 @@ |
3861 | Qt::ExtraButton12 | Qt::ExtraButton13); |
3862 | |
3863 | connect(this, &QQuickItem::activeFocusChanged, this, &MirSurfaceItem::updateMirSurfaceActiveFocus); |
3864 | - connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility); |
3865 | + connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceExposure); |
3866 | |
3867 | connect(this, &MirSurfaceItem::consumesInputChanged, this, [this]() { |
3868 | updateMirSurfaceActiveFocus(hasActiveFocus()); |
3869 | @@ -278,7 +278,7 @@ |
3870 | m_qmlSurface->registerView((qintptr)this); |
3871 | |
3872 | updateSurfaceSize(); |
3873 | - updateMirSurfaceVisibility(); |
3874 | + updateMirSurfaceExposure(); |
3875 | |
3876 | if (m_orientationAngle) { |
3877 | m_qmlSurface->setOrientationAngle(*m_orientationAngle); |
3878 | @@ -335,11 +335,11 @@ |
3879 | } |
3880 | } |
3881 | |
3882 | -void MirSurfaceItem::updateMirSurfaceVisibility() |
3883 | +void MirSurfaceItem::updateMirSurfaceExposure() |
3884 | { |
3885 | if (!m_qmlSurface) return; |
3886 | |
3887 | - m_qmlSurface->setViewVisibility((qintptr)this, isVisible()); |
3888 | + m_qmlSurface->setViewExposure((qintptr)this, isVisible()); |
3889 | } |
3890 | |
3891 | void MirSurfaceItem::setConsumesInput(bool value) |
3892 | |
3893 | === modified file 'tests/mocks/Unity/Application/MirSurfaceItem.h' |
3894 | --- tests/mocks/Unity/Application/MirSurfaceItem.h 2016-06-22 13:53:00 +0000 |
3895 | +++ tests/mocks/Unity/Application/MirSurfaceItem.h 2016-11-23 15:15:21 +0000 |
3896 | @@ -50,7 +50,6 @@ |
3897 | Mir::ShellChrome shellChrome() const override; |
3898 | |
3899 | Mir::State surfaceState() const override; |
3900 | - void setSurfaceState(Mir::State) override {} |
3901 | |
3902 | Mir::OrientationAngle orientationAngle() const override; |
3903 | void setOrientationAngle(Mir::OrientationAngle angle) override; |
3904 | @@ -102,7 +101,7 @@ |
3905 | private Q_SLOTS: |
3906 | void onComponentStatusChanged(QQmlComponent::Status status); |
3907 | void updateScreenshot(QUrl screenshot); |
3908 | - void updateMirSurfaceVisibility(); |
3909 | + void updateMirSurfaceExposure(); |
3910 | void updateMirSurfaceActiveFocus(bool focused); |
3911 | |
3912 | private: |
3913 | |
3914 | === modified file 'tests/mocks/Unity/Application/MirSurfaceListModel.cpp' |
3915 | --- tests/mocks/Unity/Application/MirSurfaceListModel.cpp 2016-09-23 13:38:46 +0000 |
3916 | +++ tests/mocks/Unity/Application/MirSurfaceListModel.cpp 2016-11-23 15:15:21 +0000 |
3917 | @@ -21,8 +21,9 @@ |
3918 | |
3919 | #define MIRSURFACELISTMODEL_DEBUG 0 |
3920 | |
3921 | -#ifdef MIRSURFACELISTMODEL_DEBUG |
3922 | -#define DEBUG_MSG(params) qDebug().nospace() << "MirSurfaceListModel::" << __func__ << " " << params |
3923 | +#if MIRSURFACELISTMODEL_DEBUG |
3924 | +#include <QDebug> |
3925 | +#define DEBUG_MSG(params) qDebug().nospace() << "MirSurfaceListModel::" << __func__ << params |
3926 | #else |
3927 | #define DEBUG_MSG(params) ((void)0) |
3928 | #endif |
3929 | @@ -54,26 +55,16 @@ |
3930 | |
3931 | void MirSurfaceListModel::raise(MirSurface *surface) |
3932 | { |
3933 | + DEBUG_MSG("(" << surface << ")"); |
3934 | int i = m_surfaceList.indexOf(surface); |
3935 | if (i != -1) { |
3936 | moveSurface(i, 0); |
3937 | } |
3938 | } |
3939 | |
3940 | -void MirSurfaceListModel::appendSurface(MirSurface *surface) |
3941 | -{ |
3942 | - beginInsertRows(QModelIndex(), m_surfaceList.size(), m_surfaceList.size()); |
3943 | - m_surfaceList.append(surface); |
3944 | - connectSurface(surface); |
3945 | - endInsertRows(); |
3946 | - Q_EMIT countChanged(m_surfaceList.count()); |
3947 | - if (m_surfaceList.count() == 1) { |
3948 | - Q_EMIT firstChanged(); |
3949 | - } |
3950 | -} |
3951 | - |
3952 | -void MirSurfaceListModel::prependSurface(MirSurface *surface) |
3953 | -{ |
3954 | +void MirSurfaceListModel::addSurface(MirSurface *surface) |
3955 | +{ |
3956 | + DEBUG_MSG("(" << surface << ")"); |
3957 | beginInsertRows(QModelIndex(), 0, 0); |
3958 | m_surfaceList.prepend(surface); |
3959 | connectSurface(surface); |
3960 | @@ -85,6 +76,11 @@ |
3961 | void MirSurfaceListModel::connectSurface(MirSurface *surface) |
3962 | { |
3963 | connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); }); |
3964 | + connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool surfaceFocused){ |
3965 | + if (surfaceFocused) { |
3966 | + raise(surface); |
3967 | + } |
3968 | + }); |
3969 | } |
3970 | |
3971 | void MirSurfaceListModel::removeSurface(MirSurface *surface) |
3972 | @@ -138,21 +134,3 @@ |
3973 | return nullptr; |
3974 | } |
3975 | } |
3976 | - |
3977 | -MirSurfaceInterface *MirSurfaceListModel::createSurface() |
3978 | -{ |
3979 | - QStringList screenshotIds = {"gallery", "map", "facebook", "camera", "browser", "music", "twitter"}; |
3980 | - int i = rand() % screenshotIds.count(); |
3981 | - |
3982 | - QUrl screenshotUrl = QString("qrc:///Unity/Application/screenshots/%1@12.png") |
3983 | - .arg(screenshotIds[i]); |
3984 | - |
3985 | - auto surface = new MirSurface(QString("prompt foo"), |
3986 | - Mir::NormalType, |
3987 | - Mir::RestoredState, |
3988 | - screenshotUrl); |
3989 | - |
3990 | - prependSurface(surface); |
3991 | - |
3992 | - return surface; |
3993 | -} |
3994 | |
3995 | === modified file 'tests/mocks/Unity/Application/MirSurfaceListModel.h' |
3996 | --- tests/mocks/Unity/Application/MirSurfaceListModel.h 2016-06-02 12:02:35 +0000 |
3997 | +++ tests/mocks/Unity/Application/MirSurfaceListModel.h 2016-11-23 15:15:21 +0000 |
3998 | @@ -38,18 +38,13 @@ |
3999 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
4000 | QVariant data(const QModelIndex& index, int role) const override; |
4001 | |
4002 | - void appendSurface(MirSurface *surface); |
4003 | + void addSurface(MirSurface *surface); |
4004 | void removeSurface(MirSurface *surface); |
4005 | |
4006 | bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); } |
4007 | |
4008 | - //// |
4009 | - // API for tests |
4010 | - |
4011 | - Q_INVOKABLE unity::shell::application::MirSurfaceInterface *createSurface(); |
4012 | - |
4013 | private: |
4014 | - void prependSurface(MirSurface *surface); |
4015 | + void appendSurface(MirSurface *surface); |
4016 | void raise(MirSurface *surface); |
4017 | void moveSurface(int from, int to); |
4018 | void connectSurface(MirSurface *surface); |
4019 | |
4020 | === modified file 'tests/mocks/Unity/Application/SurfaceManager.cpp' |
4021 | --- tests/mocks/Unity/Application/SurfaceManager.cpp 2016-04-27 15:01:10 +0000 |
4022 | +++ tests/mocks/Unity/Application/SurfaceManager.cpp 2016-11-23 15:15:21 +0000 |
4023 | @@ -21,33 +21,41 @@ |
4024 | |
4025 | #include <paths.h> |
4026 | |
4027 | -SurfaceManager *SurfaceManager::the_surface_manager = nullptr; |
4028 | +#define SURFACEMANAGER_DEBUG 0 |
4029 | + |
4030 | +#if SURFACEMANAGER_DEBUG |
4031 | +#define DEBUG_MSG(params) qDebug().nospace() << "SurfaceManager[" << (void*)this << "]::" << __func__ << params |
4032 | +#else |
4033 | +#define DEBUG_MSG(params) ((void)0) |
4034 | +#endif |
4035 | + |
4036 | +namespace unityapi = unity::shell::application; |
4037 | + |
4038 | +SurfaceManager *SurfaceManager::m_instance = nullptr; |
4039 | |
4040 | SurfaceManager *SurfaceManager::instance() |
4041 | { |
4042 | - return the_surface_manager; |
4043 | + return m_instance; |
4044 | } |
4045 | |
4046 | -SurfaceManager::SurfaceManager(QObject *parent) : |
4047 | - QObject(parent) |
4048 | - , m_virtualKeyboard(nullptr) |
4049 | +SurfaceManager::SurfaceManager(QObject *) |
4050 | { |
4051 | - Q_ASSERT(the_surface_manager == nullptr); |
4052 | - the_surface_manager = this; |
4053 | + DEBUG_MSG(""); |
4054 | |
4055 | - m_virtualKeyboard = new VirtualKeyboard; |
4056 | - connect(m_virtualKeyboard, &QObject::destroyed, this, [this](QObject *obj) { |
4057 | - MirSurface* surface = qobject_cast<MirSurface*>(obj); |
4058 | - m_virtualKeyboard = nullptr; |
4059 | - Q_EMIT inputMethodSurfaceChanged(); |
4060 | - Q_EMIT surfaceDestroyed(surface); |
4061 | - }); |
4062 | + Q_ASSERT(m_instance == nullptr); |
4063 | + m_instance = this; |
4064 | } |
4065 | |
4066 | SurfaceManager::~SurfaceManager() |
4067 | { |
4068 | - Q_ASSERT(the_surface_manager == this); |
4069 | - the_surface_manager = nullptr; |
4070 | + DEBUG_MSG(""); |
4071 | + |
4072 | + if (m_virtualKeyboard) { |
4073 | + m_virtualKeyboard->setLive(false); |
4074 | + } |
4075 | + |
4076 | + Q_ASSERT(m_instance == this); |
4077 | + m_instance = nullptr; |
4078 | } |
4079 | |
4080 | MirSurface *SurfaceManager::createSurface(const QString& name, |
4081 | @@ -56,10 +64,13 @@ |
4082 | const QUrl& screenshot) |
4083 | { |
4084 | MirSurface* surface = new MirSurface(name, type, state, screenshot); |
4085 | - connect(surface, &QObject::destroyed, this, [this](QObject *obj) { |
4086 | - MirSurface* surface = qobject_cast<MirSurface*>(obj); |
4087 | - Q_EMIT surfaceDestroyed(surface); |
4088 | - }); |
4089 | + registerSurface(surface); |
4090 | + return surface; |
4091 | +} |
4092 | + |
4093 | +void SurfaceManager::registerSurface(MirSurface *surface) |
4094 | +{ |
4095 | + m_surfaces.prepend(surface); |
4096 | |
4097 | surface->setMinimumWidth(m_newSurfaceMinimumWidth); |
4098 | surface->setMaximumWidth(m_newSurfaceMaximumWidth); |
4099 | @@ -68,13 +79,18 @@ |
4100 | surface->setWidthIncrement(m_newSurfaceWidthIncrement); |
4101 | surface->setHeightIncrement(m_newSurfaceHeightIncrement); |
4102 | |
4103 | + connect(surface, &MirSurface::stateRequested, this, [=](Mir::State state) { |
4104 | + this->onStateRequested(surface, state); |
4105 | + }); |
4106 | + |
4107 | + connect(surface, &QObject::destroyed, this, [=]() { |
4108 | + this->onSurfaceDestroyed(surface); |
4109 | + }); |
4110 | +} |
4111 | + |
4112 | +void SurfaceManager::notifySurfaceCreated(unityapi::MirSurfaceInterface *surface) |
4113 | +{ |
4114 | Q_EMIT surfaceCreated(surface); |
4115 | - return surface; |
4116 | -} |
4117 | - |
4118 | -MirSurface *SurfaceManager::inputMethodSurface() const |
4119 | -{ |
4120 | - return m_virtualKeyboard; |
4121 | } |
4122 | |
4123 | void SurfaceManager::setNewSurfaceMinimumWidth(int value) |
4124 | @@ -124,3 +140,130 @@ |
4125 | Q_EMIT newSurfaceHeightIncrementChanged(m_newSurfaceHeightIncrement); |
4126 | } |
4127 | } |
4128 | + |
4129 | +void SurfaceManager::raise(unityapi::MirSurfaceInterface *surface) |
4130 | +{ |
4131 | + if (m_underModification) |
4132 | + return; |
4133 | + |
4134 | + DEBUG_MSG("("<<surface<<") started"); |
4135 | + Q_EMIT modificationsStarted(); |
4136 | + m_underModification = true; |
4137 | + |
4138 | + doRaise(surface); |
4139 | + |
4140 | + m_underModification = false; |
4141 | + Q_EMIT modificationsEnded(); |
4142 | + DEBUG_MSG("("<<surface<<") ended"); |
4143 | +} |
4144 | + |
4145 | +void SurfaceManager::doRaise(unityapi::MirSurfaceInterface *apiSurface) |
4146 | +{ |
4147 | + auto surface = static_cast<MirSurface*>(apiSurface); |
4148 | + int index = m_surfaces.indexOf(surface); |
4149 | + Q_ASSERT(index != -1); |
4150 | + m_surfaces.move(index, 0); |
4151 | + |
4152 | + QVector<MirSurfaceInterface*> surfaces; |
4153 | + surfaces.append(surface); |
4154 | + Q_EMIT surfacesRaised(surfaces); |
4155 | +} |
4156 | + |
4157 | +void SurfaceManager::activate(unityapi::MirSurfaceInterface *apiSurface) |
4158 | +{ |
4159 | + auto surface = static_cast<MirSurface*>(apiSurface); |
4160 | + |
4161 | + if (surface == m_focusedSurface) { |
4162 | + return; |
4163 | + } |
4164 | + |
4165 | + Q_ASSERT(!m_underModification); |
4166 | + |
4167 | + DEBUG_MSG("("<<surface<<") started"); |
4168 | + Q_EMIT modificationsStarted(); |
4169 | + m_underModification = true; |
4170 | + if (m_focusedSurface) { |
4171 | + m_focusedSurface->setFocused(false); |
4172 | + } |
4173 | + if (surface) { |
4174 | + if (surface->state() == Mir::HiddenState || surface->state() == Mir::MinimizedState) { |
4175 | + surface->setState(Mir::RestoredState); |
4176 | + } |
4177 | + surface->setFocused(true); |
4178 | + doRaise(surface); |
4179 | + } |
4180 | + m_focusedSurface = surface; |
4181 | + m_underModification = false; |
4182 | + Q_EMIT modificationsEnded(); |
4183 | + DEBUG_MSG("("<<surface<<") ended"); |
4184 | +} |
4185 | + |
4186 | +void SurfaceManager::onStateRequested(MirSurface *surface, Mir::State state) |
4187 | +{ |
4188 | + Q_ASSERT(!m_underModification); |
4189 | + |
4190 | + DEBUG_MSG("("<<surface<<","<<state<<") started"); |
4191 | + Q_EMIT modificationsStarted(); |
4192 | + m_underModification = true; |
4193 | + |
4194 | + surface->setState(state); |
4195 | + |
4196 | + if ((state == Mir::MinimizedState || state == Mir::HiddenState) && surface->focused()) { |
4197 | + Q_ASSERT(m_focusedSurface == surface); |
4198 | + surface->setFocused(false); |
4199 | + m_focusedSurface = nullptr; |
4200 | + focusFirstAvailableSurface(); |
4201 | + } |
4202 | + |
4203 | + m_underModification = false; |
4204 | + Q_EMIT modificationsEnded(); |
4205 | + DEBUG_MSG("("<<surface<<","<<state<<") ended"); |
4206 | +} |
4207 | + |
4208 | +void SurfaceManager::onSurfaceDestroyed(MirSurface *surface) |
4209 | +{ |
4210 | + m_surfaces.removeAll(surface); |
4211 | + if (m_focusedSurface == surface) { |
4212 | + m_focusedSurface = nullptr; |
4213 | + |
4214 | + Q_EMIT modificationsStarted(); |
4215 | + m_underModification = true; |
4216 | + |
4217 | + focusFirstAvailableSurface(); |
4218 | + |
4219 | + m_underModification = false; |
4220 | + Q_EMIT modificationsEnded(); |
4221 | + } |
4222 | +} |
4223 | + |
4224 | +void SurfaceManager::focusFirstAvailableSurface() |
4225 | +{ |
4226 | + MirSurface *chosenSurface = nullptr; |
4227 | + for (int i = 0; i < m_surfaces.count() && !chosenSurface; ++i) { |
4228 | + auto *surface = m_surfaces[i]; |
4229 | + if (surface->state() != Mir::HiddenState && surface->state() != Mir::MinimizedState) { |
4230 | + chosenSurface = surface; |
4231 | + } |
4232 | + } |
4233 | + |
4234 | + if (!chosenSurface) { |
4235 | + return; |
4236 | + } |
4237 | + |
4238 | + if (m_focusedSurface) { |
4239 | + m_focusedSurface->setFocused(false); |
4240 | + } |
4241 | + if (chosenSurface) { |
4242 | + chosenSurface->setFocused(true); |
4243 | + } |
4244 | + m_focusedSurface = chosenSurface; |
4245 | +} |
4246 | + |
4247 | +void SurfaceManager::createInputMethodSurface() |
4248 | +{ |
4249 | + if (!m_virtualKeyboard) { |
4250 | + m_virtualKeyboard = new VirtualKeyboard; |
4251 | + registerSurface(m_virtualKeyboard); |
4252 | + Q_EMIT surfaceCreated(m_virtualKeyboard); |
4253 | + } |
4254 | +} |
4255 | |
4256 | === modified file 'tests/mocks/Unity/Application/SurfaceManager.h' |
4257 | --- tests/mocks/Unity/Application/SurfaceManager.h 2016-04-13 18:33:15 +0000 |
4258 | +++ tests/mocks/Unity/Application/SurfaceManager.h 2016-11-23 15:15:21 +0000 |
4259 | @@ -19,15 +19,16 @@ |
4260 | |
4261 | #include <QObject> |
4262 | |
4263 | +#include <unity/shell/application/SurfaceManagerInterface.h> |
4264 | + |
4265 | #include "MirSurface.h" |
4266 | #include "VirtualKeyboard.h" |
4267 | |
4268 | class ApplicationInfo; |
4269 | |
4270 | -class SurfaceManager : public QObject |
4271 | +class SurfaceManager : public unity::shell::application::SurfaceManagerInterface |
4272 | { |
4273 | Q_OBJECT |
4274 | - Q_PROPERTY(MirSurface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged) |
4275 | Q_PROPERTY(int newSurfaceMinimumWidth READ newSurfaceMinimumWidth WRITE setNewSurfaceMinimumWidth NOTIFY newSurfaceMinimumWidthChanged) |
4276 | Q_PROPERTY(int newSurfaceMaximumWidth READ newSurfaceMaximumWidth WRITE setNewSurfaceMaximumWidth NOTIFY newSurfaceMaximumWidthChanged) |
4277 | Q_PROPERTY(int newSurfaceMinimumHeight READ newSurfaceMinimumHeight WRITE setNewSurfaceMinimumHeight NOTIFY newSurfaceMinimumHeightChanged) |
4278 | @@ -41,12 +42,17 @@ |
4279 | |
4280 | static SurfaceManager *instance(); |
4281 | |
4282 | + // SurfaceManagerInterface |
4283 | + void raise(unity::shell::application::MirSurfaceInterface *surface) override; |
4284 | + void activate(unity::shell::application::MirSurfaceInterface *surface) override; |
4285 | + |
4286 | Q_INVOKABLE MirSurface* createSurface(const QString& name, |
4287 | Mir::Type type, |
4288 | Mir::State state, |
4289 | const QUrl& screenshot); |
4290 | |
4291 | - MirSurface* inputMethodSurface() const; |
4292 | + |
4293 | + void notifySurfaceCreated(unity::shell::application::MirSurfaceInterface *); |
4294 | |
4295 | int newSurfaceMinimumWidth() const { return m_newSurfaceMinimumWidth; } |
4296 | void setNewSurfaceMinimumWidth(int value); |
4297 | @@ -66,12 +72,10 @@ |
4298 | int newSurfaceHeightIncrement() const { return m_newSurfaceHeightIncrement; } |
4299 | void setNewSurfaceHeightIncrement(int); |
4300 | |
4301 | +public Q_SLOTS: |
4302 | + void createInputMethodSurface(); |
4303 | + |
4304 | Q_SIGNALS: |
4305 | - void inputMethodSurfaceChanged(); |
4306 | - void countChanged(); |
4307 | - void surfaceCreated(MirSurface *surface); |
4308 | - void surfaceDestroyed(MirSurface*surface); |
4309 | - |
4310 | void newSurfaceMinimumWidthChanged(int value); |
4311 | void newSurfaceMaximumWidthChanged(int value); |
4312 | void newSurfaceMinimumHeightChanged(int value); |
4313 | @@ -79,9 +83,16 @@ |
4314 | void newSurfaceWidthIncrementChanged(int value); |
4315 | void newSurfaceHeightIncrementChanged(int value); |
4316 | |
4317 | +private Q_SLOTS: |
4318 | + void onStateRequested(MirSurface *surface, Mir::State state); |
4319 | + void onSurfaceDestroyed(MirSurface *surface); |
4320 | + |
4321 | private: |
4322 | - static SurfaceManager *the_surface_manager; |
4323 | - VirtualKeyboard *m_virtualKeyboard; |
4324 | + void doRaise(unity::shell::application::MirSurfaceInterface *surface); |
4325 | + void focusFirstAvailableSurface(); |
4326 | + void registerSurface(MirSurface *surface); |
4327 | + |
4328 | + static SurfaceManager *m_instance; |
4329 | |
4330 | int m_newSurfaceMinimumWidth{0}; |
4331 | int m_newSurfaceMaximumWidth{0}; |
4332 | @@ -89,6 +100,13 @@ |
4333 | int m_newSurfaceMaximumHeight{0}; |
4334 | int m_newSurfaceWidthIncrement{1}; |
4335 | int m_newSurfaceHeightIncrement{1}; |
4336 | + |
4337 | + MirSurface *m_focusedSurface{nullptr}; |
4338 | + bool m_underModification{false}; |
4339 | + |
4340 | + QList<MirSurface*> m_surfaces; |
4341 | + |
4342 | + VirtualKeyboard *m_virtualKeyboard{nullptr}; |
4343 | }; |
4344 | |
4345 | #endif // SURFACEMANAGER_H |
4346 | |
4347 | === modified file 'tests/mocks/Unity/Application/plugin.cpp' |
4348 | --- tests/mocks/Unity/Application/plugin.cpp 2016-06-30 13:51:32 +0000 |
4349 | +++ tests/mocks/Unity/Application/plugin.cpp 2016-11-23 15:15:21 +0000 |
4350 | @@ -28,36 +28,11 @@ |
4351 | |
4352 | namespace { |
4353 | |
4354 | -// Creates the singletons that are called throughout C++ code |
4355 | -void createUnityApplicationSharedSingletons() |
4356 | -{ |
4357 | - // they have to be created in a specific order |
4358 | - if (!MirFocusController::instance()) { |
4359 | - new MirFocusController; |
4360 | - } |
4361 | - if (!SurfaceManager::instance()) { |
4362 | - new SurfaceManager; |
4363 | - } |
4364 | -} |
4365 | - |
4366 | QObject* applicationManagerSingleton(QQmlEngine*, QJSEngine*) |
4367 | { |
4368 | - createUnityApplicationSharedSingletons(); |
4369 | return new ApplicationManager; |
4370 | } |
4371 | |
4372 | -QObject* surfaceManagerSingleton(QQmlEngine*, QJSEngine*) |
4373 | -{ |
4374 | - createUnityApplicationSharedSingletons(); |
4375 | - return SurfaceManager::instance(); |
4376 | -} |
4377 | - |
4378 | -QObject* mirFocusControllerSingleton(QQmlEngine*, QJSEngine*) |
4379 | -{ |
4380 | - createUnityApplicationSharedSingletons(); |
4381 | - return MirFocusController::instance(); |
4382 | -} |
4383 | - |
4384 | } // anonymous namespace |
4385 | |
4386 | void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri) |
4387 | @@ -65,6 +40,7 @@ |
4388 | qRegisterMetaType<ApplicationInfo*>("ApplicationInfo*"); |
4389 | qRegisterMetaType<unity::shell::application::MirSurfaceInterface*>("unity::shell::application::MirSurfaceInterface*"); |
4390 | qRegisterMetaType<unity::shell::application::MirSurfaceListInterface*>("unity::shell::application::MirSurfaceListInterface*"); |
4391 | + qRegisterMetaType<unity::shell::application::SurfaceManagerInterface*>("unity::shell::application::SurfaceManagerInterface*"); |
4392 | qRegisterMetaType<Mir::Type>("Mir::Type"); |
4393 | qRegisterMetaType<Mir::State>("Mir::State"); |
4394 | |
4395 | @@ -73,12 +49,11 @@ |
4396 | qmlRegisterUncreatableType<MirSurface>(uri, 0, 1, "MirSurface", "MirSurface can't be instantiated from QML"); |
4397 | qmlRegisterUncreatableType<unity::shell::application::MirSurfaceInterface>( |
4398 | uri, 0, 1, "MirSurface", "MirSurface can't be instantiated from QML"); |
4399 | - qmlRegisterSingletonType<MirFocusController>(uri, 0, 1, "MirFocusController", mirFocusControllerSingleton); |
4400 | qmlRegisterType<MirSurfaceItem>(uri, 0, 1, "MirSurfaceItem"); |
4401 | qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo"); |
4402 | |
4403 | qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton); |
4404 | - qmlRegisterSingletonType<SurfaceManager>(uri, 0, 1, "SurfaceManager", surfaceManagerSingleton); |
4405 | + qmlRegisterType<SurfaceManager>(uri, 0, 1, "SurfaceManager"); |
4406 | |
4407 | qmlRegisterUncreatableType<Mir>(uri, 0, 1, "Mir", "Mir provides enum values, it can't be instantiated"); |
4408 | } |
4409 | |
4410 | === modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp' |
4411 | --- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2016-09-22 20:43:05 +0000 |
4412 | +++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2016-11-23 15:15:21 +0000 |
4413 | @@ -65,6 +65,7 @@ |
4414 | MirSurfaceListInterface* promptSurfaceList() const override { return nullptr; } |
4415 | int surfaceCount() const override { return m_surfaceCount; } |
4416 | void setSurfaceCount(int count) { m_surfaceCount = count; Q_EMIT surfaceCountChanged(count); } |
4417 | + void close() override {} |
4418 | |
4419 | // Methods used for mocking (not in the interface) |
4420 | void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); } |
4421 | @@ -98,6 +99,9 @@ |
4422 | } |
4423 | return nullptr; |
4424 | } |
4425 | + unity::shell::application::ApplicationInfoInterface *findApplicationWithSurface(unity::shell::application::MirSurfaceInterface* surface) override { |
4426 | + return nullptr; |
4427 | + } |
4428 | unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) override { return nullptr; } |
4429 | bool stopApplication(const QString &appId) override { |
4430 | Q_FOREACH(MockApp* app, m_list) { |
4431 | |
4432 | === modified file 'tests/qmltests/Stage/ApplicationCheckBox.qml' |
4433 | --- tests/qmltests/Stage/ApplicationCheckBox.qml 2016-06-02 12:02:35 +0000 |
4434 | +++ tests/qmltests/Stage/ApplicationCheckBox.qml 2016-11-23 15:15:21 +0000 |
4435 | @@ -149,7 +149,7 @@ |
4436 | width: height |
4437 | height: promptsLabel.height * 0.7 |
4438 | anchors.verticalCenter: parent.verticalCenter |
4439 | - onClicked: d.application.promptSurfaceList.createSurface() |
4440 | + onClicked: d.application.createPromptSurface() |
4441 | Label { text: "âž•"; anchors.centerIn: parent } |
4442 | } |
4443 | MouseArea { |
4444 | |
4445 | === modified file 'tests/qmltests/Stage/tst_DesktopStage.qml' |
4446 | --- tests/qmltests/Stage/tst_DesktopStage.qml 2016-11-08 15:35:35 +0000 |
4447 | +++ tests/qmltests/Stage/tst_DesktopStage.qml 2016-11-23 15:15:21 +0000 |
4448 | @@ -20,8 +20,8 @@ |
4449 | import Ubuntu.Components.ListItems 1.3 |
4450 | import Unity.Application 0.1 |
4451 | import Unity.Test 0.1 |
4452 | -import WindowManager 0.1 |
4453 | import Utils 0.1 |
4454 | +import WindowManager 1.0 |
4455 | |
4456 | import ".." // For EdgeBarrierControls |
4457 | import "../../../qml/Stage" |
4458 | @@ -57,9 +57,11 @@ |
4459 | } |
4460 | } |
4461 | |
4462 | - TopLevelSurfaceList { |
4463 | + SurfaceManager { id: sMgr } |
4464 | + TopLevelWindowModel { |
4465 | id: topSurfaceList |
4466 | - applicationsModel: ApplicationManager |
4467 | + applicationManager: ApplicationManager |
4468 | + surfaceManager: sMgr |
4469 | } |
4470 | |
4471 | Loader { |
4472 | @@ -80,6 +82,7 @@ |
4473 | |
4474 | Component.onCompleted: { |
4475 | edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController"); |
4476 | + ApplicationManager.startApplication("unity8-dash"); |
4477 | } |
4478 | Component.onDestruction: { |
4479 | stageLoader.itemDestroyed = true; |
4480 | @@ -170,8 +173,9 @@ |
4481 | tryCompare(dashApp, "state", ApplicationInfoInterface.Running); |
4482 | |
4483 | tryCompare(topSurfaceList, "count", 1); |
4484 | - tryCompareFunction(function(){return topSurfaceList.surfaceAt(0) != null;}, true); |
4485 | - compare(MirFocusController.focusedSurface, topSurfaceList.surfaceAt(0)); |
4486 | + tryCompareFunction(function(){return topSurfaceList.windowAt(0) != null;}, true); |
4487 | + topSurfaceList.windowAt(0).activate(); |
4488 | + tryCompare(topSurfaceList, "focusedWindow", topSurfaceList.windowAt(0)); |
4489 | } |
4490 | |
4491 | function cleanup() { |
4492 | @@ -284,13 +288,13 @@ |
4493 | verify(fromAppWindow); |
4494 | tap(fromAppWindow); |
4495 | compare(fromDelegate.surface.activeFocus, true); |
4496 | - compare(MirFocusController.focusedSurface, fromDelegate.surface); |
4497 | + compare(topSurfaceList.focusedWindow, fromDelegate.window); |
4498 | |
4499 | var toAppWindow = findChild(toDelegate, "appWindow"); |
4500 | verify(toAppWindow); |
4501 | tap(toAppWindow); |
4502 | compare(toDelegate.surface.activeFocus, true); |
4503 | - compare(MirFocusController.focusedSurface, toDelegate.surface); |
4504 | + compare(topSurfaceList.focusedWindow, toDelegate.window); |
4505 | } |
4506 | |
4507 | function test_clickingOnWindowChangesFocusedApp_data() { |
4508 | @@ -309,13 +313,13 @@ |
4509 | verify(fromAppWindow); |
4510 | mouseClick(fromAppWindow); |
4511 | compare(fromDelegate.surface.activeFocus, true); |
4512 | - compare(MirFocusController.focusedSurface, fromDelegate.surface); |
4513 | + compare(topSurfaceList.focusedWindow, fromDelegate.window); |
4514 | |
4515 | var toAppWindow = findChild(toDelegate, "appWindow"); |
4516 | verify(toAppWindow); |
4517 | mouseClick(toAppWindow); |
4518 | compare(toDelegate.surface.activeFocus, true); |
4519 | - compare(MirFocusController.focusedSurface, toDelegate.surface); |
4520 | + compare(topSurfaceList.focusedWindow, toDelegate.window); |
4521 | } |
4522 | |
4523 | function test_tappingOnDecorationFocusesApplication_data() { |
4524 | @@ -343,6 +347,13 @@ |
4525 | return findChild(appDelegate, "appWindowDecoration"); |
4526 | } |
4527 | |
4528 | + function maximizeDelegate(appDelegate) { |
4529 | + var maximizeButton = findChild(appDelegate, "maximizeWindowButton"); |
4530 | + verify(maximizeButton); |
4531 | + mouseClick(maximizeButton); |
4532 | + tryCompare(appDelegate, "visuallyMaximized", true); |
4533 | + } |
4534 | + |
4535 | function test_tappingOnDecorationFocusesApplication(data) { |
4536 | var appDelegates = []; |
4537 | for (var i = 0; i < data.apps.length; i++) { |
4538 | @@ -398,7 +409,7 @@ |
4539 | startApplication("camera-app"); |
4540 | |
4541 | tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true); |
4542 | - dialerDelegate.surface.requestFocus(); |
4543 | + dialerDelegate.surface.activate(); |
4544 | tryCompare(dialerDelegate, "focus", true); |
4545 | |
4546 | keyClick(Qt.Key_Up, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Up shortcut to maximize |
4547 | @@ -411,7 +422,7 @@ |
4548 | startApplication("camera-app"); |
4549 | |
4550 | tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true); |
4551 | - dialerDelegate.surface.requestFocus(); |
4552 | + dialerDelegate.surface.activate(); |
4553 | tryCompare(dialerDelegate, "focus", true); |
4554 | |
4555 | keyClick(Qt.Key_Left, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Left shortcut to maximizeLeft |
4556 | @@ -426,7 +437,7 @@ |
4557 | startApplication("camera-app"); |
4558 | |
4559 | tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true); |
4560 | - dialerDelegate.surface.requestFocus(); |
4561 | + dialerDelegate.surface.activate(); |
4562 | tryCompare(dialerDelegate, "focus", true); |
4563 | |
4564 | keyClick(Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Right shortcut to maximizeRight |
4565 | @@ -441,7 +452,7 @@ |
4566 | startApplication("camera-app"); |
4567 | |
4568 | tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true); |
4569 | - dialerDelegate.surface.requestFocus(); |
4570 | + dialerDelegate.surface.activate(); |
4571 | tryCompare(dialerDelegate, "focus", true); |
4572 | |
4573 | keyClick(Qt.Key_Down, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Down shortcut to minimize |
4574 | @@ -512,11 +523,11 @@ |
4575 | apps.forEach(startApplication); |
4576 | verify(topSurfaceList.count == 3); |
4577 | keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all |
4578 | - tryCompare(MirFocusController, "focusedSurface", null); // verify no surface is focused |
4579 | + tryCompare(topSurfaceList, "focusedWindow", null); // verify no window is focused |
4580 | |
4581 | // now try pressing all 4 arrow keys + ctrl + meta |
4582 | keyClick(Qt.Key_Up | Qt.Key_Down | Qt.Key_Left | Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // smash it!!! |
4583 | - tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused |
4584 | + tryCompare(topSurfaceList, "focusedWindow", null); // verify still no window is focused |
4585 | } |
4586 | |
4587 | function test_minimizeApplicationHidesSurface() { |
4588 | @@ -527,9 +538,12 @@ |
4589 | var decoratedWindow = findDecoratedWindow(dashSurfaceId); |
4590 | verify(decoratedWindow); |
4591 | |
4592 | - tryCompare(dashSurface, "visible", true); |
4593 | - decoratedWindow.minimizeClicked(); |
4594 | - tryCompare(dashSurface, "visible", false); |
4595 | + var minimizeButton = findChild(decoratedWindow, "minimizeWindowButton"); |
4596 | + verify(minimizeButton); |
4597 | + |
4598 | + tryCompare(dashSurface, "exposed", true); |
4599 | + mouseClick(minimizeButton); |
4600 | + tryCompare(dashSurface, "exposed", false); |
4601 | } |
4602 | |
4603 | function test_maximizeApplicationHidesSurfacesBehindIt() { |
4604 | @@ -538,16 +552,16 @@ |
4605 | var gmailDelegate = startApplication("gmail-webapp"); |
4606 | |
4607 | // maximize without raising |
4608 | - dialerDelegate.maximize(); |
4609 | + dialerDelegate.requestMaximize(); |
4610 | tryCompare(dialerDelegate, "visuallyMaximized", true); |
4611 | |
4612 | - tryCompare(dashDelegate.surface, "visible", false); |
4613 | - compare(gmailDelegate.surface.visible, true); |
4614 | + tryCompare(dashDelegate.surface, "exposed", false); |
4615 | + compare(gmailDelegate.surface.exposed, true); |
4616 | |
4617 | // restore without raising |
4618 | - dialerDelegate.restoreFromMaximized(); |
4619 | - compare(dashDelegate.surface.visible, true); |
4620 | - compare(gmailDelegate.surface.visible, true); |
4621 | + dialerDelegate.requestRestore(); |
4622 | + compare(dashDelegate.surface.exposed, true); |
4623 | + compare(gmailDelegate.surface.exposed, true); |
4624 | } |
4625 | |
4626 | function test_applicationsBecomeVisibleWhenOccludingAppRemoved() { |
4627 | @@ -580,26 +594,22 @@ |
4628 | tryCompare(dialerDelegate, "visuallyMaximized", true); |
4629 | tryCompare(gmailDelegate, "visuallyMaximized", true); |
4630 | |
4631 | - tryCompare(dashApp.surfaceList.get(0), "visible", false); |
4632 | - tryCompare(dialerApp.surfaceList.get(0), "visible", false); |
4633 | - tryCompare(mapApp.surfaceList.get(0), "visible", false); |
4634 | + tryCompare(dashApp.surfaceList.get(0), "exposed", false); |
4635 | + tryCompare(dialerApp.surfaceList.get(0), "exposed", false); |
4636 | + tryCompare(mapApp.surfaceList.get(0), "exposed", false); |
4637 | |
4638 | ApplicationManager.stopApplication("gmail-webapp"); |
4639 | wait(2000) |
4640 | |
4641 | - tryCompare(mapApp.surfaceList.get(0), "visible", true); |
4642 | - tryCompare(dialerApp.surfaceList.get(0), "visible", true); |
4643 | - tryCompare(dashApp.surfaceList.get(0), "visible", false); // still occluded by maximised dialer |
4644 | + tryCompare(mapApp.surfaceList.get(0), "exposed", true); |
4645 | + tryCompare(dialerApp.surfaceList.get(0), "exposed", true); |
4646 | + tryCompare(dashApp.surfaceList.get(0), "exposed", false); // still occluded by maximised dialer |
4647 | } |
4648 | |
4649 | function test_maximisedAppStaysVisibleWhenAppStarts() { |
4650 | var dashDelegate = startApplication("unity8-dash"); |
4651 | |
4652 | - // maximize |
4653 | - var dashMaximizeButton = findChild(dashDelegate, "maximizeWindowButton"); |
4654 | - verify(dashMaximizeButton); |
4655 | - mouseClick(dashMaximizeButton); |
4656 | - tryCompare(dashDelegate, "visuallyMaximized", true); |
4657 | + maximizeDelegate(dashDelegate); |
4658 | |
4659 | var dialerDelegate = startApplication("dialer-app"); |
4660 | verify(dialerDelegate); |
4661 | @@ -621,25 +631,25 @@ |
4662 | tryCompare(facebookAppDelegate, "visible", true); |
4663 | |
4664 | // Maximize the topmost and make sure the other two are hidden |
4665 | - facebookAppDelegate.maximize(); |
4666 | + maximizeDelegate(facebookAppDelegate); |
4667 | tryCompare(dashAppDelegate, "visible", false); |
4668 | tryCompare(dialerAppDelegate, "visible", false); |
4669 | tryCompare(facebookAppDelegate, "visible", true); |
4670 | |
4671 | // Bring dash to front. make sure dash and the maximized facebook are visible, the restored one behind is hidden |
4672 | - dashAppDelegate.focus = true; |
4673 | + dashAppDelegate.activate(); |
4674 | tryCompare(dashAppDelegate, "visible", true); |
4675 | tryCompare(dialerAppDelegate, "visible", false); |
4676 | tryCompare(facebookAppDelegate, "visible", true); |
4677 | |
4678 | // Now focus the dialer app. all 3 should be visible again |
4679 | - dialerAppDelegate.focus = true; |
4680 | + dialerAppDelegate.activate(); |
4681 | tryCompare(dashAppDelegate, "visible", true); |
4682 | tryCompare(dialerAppDelegate, "visible", true); |
4683 | tryCompare(facebookAppDelegate, "visible", true); |
4684 | |
4685 | // Maximize the dialer app. The other 2 should hide |
4686 | - dialerAppDelegate.maximize(); |
4687 | + maximizeDelegate(dialerAppDelegate); |
4688 | tryCompare(dashAppDelegate, "visible", false); |
4689 | tryCompare(dialerAppDelegate, "visible", true); |
4690 | tryCompare(facebookAppDelegate, "visible", false); |
4691 | @@ -648,7 +658,7 @@ |
4692 | function test_dropShadow() { |
4693 | // start an app, maximize it |
4694 | var facebookAppDelegate = startApplication("facebook-webapp"); |
4695 | - facebookAppDelegate.maximize(); |
4696 | + maximizeDelegate(facebookAppDelegate); |
4697 | |
4698 | // verify the drop shadow is still not visible |
4699 | verify(PanelState.dropShadow == false); |
4700 | |
4701 | === modified file 'tests/qmltests/Stage/tst_PhoneStage.qml' |
4702 | --- tests/qmltests/Stage/tst_PhoneStage.qml 2016-10-08 16:45:37 +0000 |
4703 | +++ tests/qmltests/Stage/tst_PhoneStage.qml 2016-11-23 15:15:21 +0000 |
4704 | @@ -22,7 +22,7 @@ |
4705 | import "../../../qml/Stage" |
4706 | import Ubuntu.Components 1.3 |
4707 | import Unity.Application 0.1 |
4708 | -import WindowManager 0.1 |
4709 | +import WindowManager 1.0 |
4710 | |
4711 | Item { |
4712 | id: root |
4713 | @@ -31,6 +31,7 @@ |
4714 | |
4715 | property var greeter: { fullyShown: true } |
4716 | |
4717 | + SurfaceManager { id: sMgr } |
4718 | Stage { |
4719 | id: stage |
4720 | anchors { fill: parent; rightMargin: units.gu(30) } |
4721 | @@ -41,9 +42,13 @@ |
4722 | orientations: Orientations {} |
4723 | applicationManager: ApplicationManager |
4724 | mode: "staged" |
4725 | - topLevelSurfaceList: TopLevelSurfaceList { |
4726 | + topLevelSurfaceList: TopLevelWindowModel { |
4727 | id: topLevelSurfaceList |
4728 | - applicationsModel: ApplicationManager |
4729 | + applicationManager: ApplicationManager |
4730 | + surfaceManager: sMgr |
4731 | + } |
4732 | + Component.onCompleted: { |
4733 | + ApplicationManager.startApplication("unity8-dash"); |
4734 | } |
4735 | } |
4736 | |
4737 | @@ -86,11 +91,16 @@ |
4738 | |
4739 | function init() { |
4740 | // wait until unity8-dash is up and running. |
4741 | - // it's started automatically by ApplicationManager mock implementation |
4742 | + ApplicationManager.startApplication("unity8-dash"); |
4743 | tryCompare(ApplicationManager, "count", 1); |
4744 | var dashApp = ApplicationManager.findApplication("unity8-dash"); |
4745 | verify(dashApp); |
4746 | tryCompare(dashApp, "state", ApplicationInfoInterface.Running); |
4747 | + |
4748 | + // wait for Stage to stabilize back into its initial state |
4749 | + var appRepeater = findChild(stage, "appRepeater"); |
4750 | + tryCompare(appRepeater, "count", 1); |
4751 | + tryCompare(appRepeater.itemAt(0), "x", 0); |
4752 | } |
4753 | |
4754 | function cleanup() { |
4755 | @@ -100,10 +110,6 @@ |
4756 | waitForRendering(stage); |
4757 | |
4758 | killApps(); |
4759 | - // wait for Stage to stabilize back into its initial state |
4760 | - var appRepeater = findChild(stage, "appRepeater"); |
4761 | - tryCompare(appRepeater, "count", 1); |
4762 | - tryCompare(appRepeater.itemAt(0), "x", 0); |
4763 | |
4764 | stage.shellOrientationAngle = 0; |
4765 | |
4766 | @@ -505,7 +511,7 @@ |
4767 | function test_selectSuspendedAppWithoutSurface() { |
4768 | compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash"); |
4769 | var dashSurfaceId = topLevelSurfaceList.idAt(0); |
4770 | - var dashSurface = topLevelSurfaceList.surfaceAt(0); |
4771 | + var dashWindow = topLevelSurfaceList.windowAt(0); |
4772 | |
4773 | var webbrowserSurfaceId = topLevelSurfaceList.nextId; |
4774 | var webbrowserApp = ApplicationManager.startApplication("webbrowser-app"); |
4775 | @@ -513,7 +519,7 @@ |
4776 | |
4777 | switchToSurface(dashSurfaceId); |
4778 | |
4779 | - tryCompare(MirFocusController, "focusedSurface", dashSurface); |
4780 | + tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow); |
4781 | tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended); |
4782 | |
4783 | compare(webbrowserApp.surfaceList.count, 1); |
4784 | @@ -549,7 +555,7 @@ |
4785 | { |
4786 | compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash"); |
4787 | var dashSurfaceId = topLevelSurfaceList.idAt(0); |
4788 | - var dashSurface = topLevelSurfaceList.surfaceAt(0); |
4789 | + var dashWindow = topLevelSurfaceList.windowAt(0); |
4790 | |
4791 | var webbrowserSurfaceId = topLevelSurfaceList.nextId; |
4792 | var webbrowserApp = ApplicationManager.startApplication("webbrowser-app"); |
4793 | @@ -557,7 +563,7 @@ |
4794 | |
4795 | switchToSurface(dashSurfaceId); |
4796 | |
4797 | - tryCompare(MirFocusController, "focusedSurface", dashSurface); |
4798 | + tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow); |
4799 | tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended); |
4800 | |
4801 | compare(webbrowserApp.surfaceList.count, 1); |
4802 | |
4803 | === modified file 'tests/qmltests/Stage/tst_TabletStage.qml' |
4804 | --- tests/qmltests/Stage/tst_TabletStage.qml 2016-10-08 16:45:37 +0000 |
4805 | +++ tests/qmltests/Stage/tst_TabletStage.qml 2016-11-23 15:15:21 +0000 |
4806 | @@ -21,7 +21,7 @@ |
4807 | import Unity.Application 0.1 |
4808 | import Unity.Test 0.1 |
4809 | import Utils 0.1 |
4810 | -import WindowManager 0.1 |
4811 | +import WindowManager 1.0 |
4812 | |
4813 | import ".." |
4814 | import "../../../qml/Stage" |
4815 | @@ -35,6 +35,7 @@ |
4816 | |
4817 | property var greeter: { fullyShown: true } |
4818 | |
4819 | + SurfaceManager { id: sMgr } |
4820 | Stage { |
4821 | id: stage |
4822 | anchors { fill: parent; rightMargin: units.gu(30) } |
4823 | @@ -50,9 +51,13 @@ |
4824 | focus: true |
4825 | mode: "stagedWithSideStage" |
4826 | applicationManager: ApplicationManager |
4827 | - topLevelSurfaceList: TopLevelSurfaceList { |
4828 | + topLevelSurfaceList: TopLevelWindowModel { |
4829 | id: topLevelSurfaceList |
4830 | - applicationsModel: ApplicationManager |
4831 | + applicationManager: ApplicationManager |
4832 | + surfaceManager: sMgr |
4833 | + } |
4834 | + Component.onCompleted: { |
4835 | + ApplicationManager.startApplication("unity8-dash"); |
4836 | } |
4837 | } |
4838 | |
4839 | @@ -129,6 +134,7 @@ |
4840 | function init() { |
4841 | stageSaver.clear(); |
4842 | |
4843 | + ApplicationManager.startApplication("unity8-dash"); |
4844 | tryCompare(topSurfaceList, "count", 1); |
4845 | compare(topSurfaceList.applicationAt(0).appId, "unity8-dash"); |
4846 | |
4847 | @@ -143,6 +149,11 @@ |
4848 | } |
4849 | } |
4850 | |
4851 | + // wait for Stage to stabilize back into its initial state |
4852 | + var appRepeater = findChild(stage, "appRepeater"); |
4853 | + tryCompare(appRepeater, "count", 1); |
4854 | + tryCompare(appRepeater.itemAt(0), "x", 0); |
4855 | + |
4856 | waitUntilAppSurfaceShowsUp(topSurfaceList.idAt(0)); |
4857 | sideStage.hideNow() |
4858 | tryCompare(sideStage, "x", stage.width) |
4859 | @@ -156,11 +167,6 @@ |
4860 | |
4861 | killApps(); |
4862 | |
4863 | - // wait for Stage to stabilize back into its initial state |
4864 | - var appRepeater = findChild(stage, "appRepeater"); |
4865 | - tryCompare(appRepeater, "count", 1); |
4866 | - tryCompare(appRepeater.itemAt(0), "x", 0); |
4867 | - |
4868 | sideStage.hideNow(); |
4869 | tryCompare(sideStage, "x", stage.width) |
4870 | waitForRendering(stage) |
4871 | @@ -628,7 +634,7 @@ |
4872 | function test_selectSuspendedAppWithoutSurface() { |
4873 | compare(topSurfaceList.applicationAt(0).appId, "unity8-dash"); |
4874 | var dashSurfaceId = topSurfaceList.idAt(0); |
4875 | - var dashSurface = topSurfaceList.surfaceAt(0); |
4876 | + var dashWindow = topSurfaceList.windowAt(0); |
4877 | |
4878 | var webbrowserSurfaceId = topSurfaceList.nextId; |
4879 | webbrowserCheckBox.checked = true; |
4880 | @@ -637,7 +643,7 @@ |
4881 | |
4882 | switchToSurface(dashSurfaceId); |
4883 | |
4884 | - tryCompare(MirFocusController, "focusedSurface", dashSurface); |
4885 | + tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow); |
4886 | tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended); |
4887 | |
4888 | compare(webbrowserApp.surfaceList.count, 1); |
4889 | |
4890 | === modified file 'tests/qmltests/tst_Shell.qml' |
4891 | --- tests/qmltests/tst_Shell.qml 2016-10-28 19:44:07 +0000 |
4892 | +++ tests/qmltests/tst_Shell.qml 2016-11-23 15:15:21 +0000 |
4893 | @@ -52,6 +52,15 @@ |
4894 | } |
4895 | |
4896 | property var shell: shellLoader.item ? shellLoader.item : null |
4897 | + onShellChanged: { |
4898 | + if (shell) { |
4899 | + topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList"); |
4900 | + } else { |
4901 | + topLevelSurfaceList = null; |
4902 | + } |
4903 | + } |
4904 | + |
4905 | + property var topLevelSurfaceList: null |
4906 | |
4907 | Item { |
4908 | id: shellContainer |
4909 | @@ -120,6 +129,9 @@ |
4910 | primary: shellLoader.primaryOrientation |
4911 | } |
4912 | mode: shellLoader.mode |
4913 | + Component.onCompleted: { |
4914 | + ApplicationManager.startApplication("unity8-dash"); |
4915 | + } |
4916 | Component.onDestruction: { |
4917 | shellLoader.itemDestroyed = true; |
4918 | } |
4919 | @@ -227,7 +239,7 @@ |
4920 | activeFocusOnPress: false |
4921 | model: ["single", "single-passphrase", "single-pin", "full"] |
4922 | onSelectedIndexChanged: { |
4923 | - shellLoader.active = false; |
4924 | + testCase.tearDown(); |
4925 | LightDM.Greeter.mockMode = model[selectedIndex]; |
4926 | LightDM.Users.mockMode = model[selectedIndex]; |
4927 | shellLoader.active = true; |
4928 | @@ -318,21 +330,21 @@ |
4929 | id: fullscreeAppCheck |
4930 | |
4931 | onTriggered: { |
4932 | - if (!MirFocusController.focusedSurface) return; |
4933 | - if (MirFocusController.focusedSurface.state == Mir.FullscreenState) { |
4934 | - MirFocusController.focusedSurface.state = Mir.RestoredState; |
4935 | + if (!topLevelSurfaceList.focusedWindow) return; |
4936 | + if (topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState) { |
4937 | + topLevelSurfaceList.focusedWindow.requestState(Mir.RestoredState); |
4938 | } else { |
4939 | - MirFocusController.focusedSurface.state = Mir.FullscreenState; |
4940 | + topLevelSurfaceList.focusedWindow.requestState(Mir.FullscreenState); |
4941 | } |
4942 | } |
4943 | |
4944 | Binding { |
4945 | target: fullscreeAppCheck |
4946 | - when: MirFocusController.focusedSurface |
4947 | + when: topLevelSurfaceList && topLevelSurfaceList.focusedWindow |
4948 | property: "checked" |
4949 | value: { |
4950 | - if (!MirFocusController.focusedSurface) return false; |
4951 | - return MirFocusController.focusedSurface.state === Mir.FullscreenState |
4952 | + if (!topLevelSurfaceList || !topLevelSurfaceList.focusedWindow) return false; |
4953 | + return topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState |
4954 | } |
4955 | } |
4956 | } |
4957 | @@ -346,21 +358,22 @@ |
4958 | id: chromeAppCheck |
4959 | |
4960 | onTriggered: { |
4961 | - if (!MirFocusController.focusedSurface) return; |
4962 | - if (MirFocusController.focusedSurface.shellChrome == Mir.LowChrome) { |
4963 | - MirFocusController.focusedSurface.setShellChrome(Mir.NormalChrome); |
4964 | + if (!topLevelSurfaceList.focusedWindow || !topLevelSurfaceList.focusedWindow.surface) return; |
4965 | + var surface = topLevelSurfaceList.focusedWindow.surface; |
4966 | + if (surface.shellChrome == Mir.LowChrome) { |
4967 | + surface.setShellChrome(Mir.NormalChrome); |
4968 | } else { |
4969 | - MirFocusController.focusedSurface.setShellChrome(Mir.LowChrome); |
4970 | + surface.setShellChrome(Mir.LowChrome); |
4971 | } |
4972 | } |
4973 | |
4974 | Binding { |
4975 | target: chromeAppCheck |
4976 | - when: MirFocusController.focusedSurface !== null |
4977 | + when: topLevelSurfaceList && topLevelSurfaceList.focusedWindow !== null && topLevelSurfaceList.focusedWindow.surface !== null |
4978 | property: "checked" |
4979 | value: { |
4980 | - if (!MirFocusController.focusedSurface) return false; |
4981 | - MirFocusController.focusedSurface.shellChrome === Mir.LowChrome |
4982 | + if (!topLevelSurfaceList || !topLevelSurfaceList.focusedWindow || !topLevelSurfaceList.focusedWindow.surface) return false; |
4983 | + topLevelSurfaceList.focusedWindow.surface.shellChrome === Mir.LowChrome |
4984 | } |
4985 | } |
4986 | } |
4987 | @@ -369,6 +382,57 @@ |
4988 | } |
4989 | } |
4990 | |
4991 | + Button { |
4992 | + text: "Toggle input method" |
4993 | + width: parent.width |
4994 | + activeFocusOnPress: false |
4995 | + onClicked: { |
4996 | + testCase.ensureInputMethodSurface(); |
4997 | + var inputMethod = root.topLevelSurfaceList.inputMethodSurface; |
4998 | + if (inputMethod.visible) { |
4999 | + inputMethod.requestState(Mir.HiddenState); |
5000 | + } else { |
FAILED: Continuous integration, rev:2674 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/2465/ /unity8- jenkins. ubuntu. com/job/ build/3241/ console /unity8- jenkins. ubuntu. com/job/ build-0- fetch/3269 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 3123/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 3123/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= zesty/3123/ console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 3123/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 3123/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= zesty/3123/ console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 3123/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 3123/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= zesty/3123/ console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/2465/ rebuild
https:/