Status: | Merged |
---|---|
Approved by: | Lukáš Tinkl |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2744 |
Proposed branch: | lp:~dandrader/unity8/miral |
Merge into: | lp:unity8 |
Prerequisite: | lp:~dandrader/unity8/fixMakeTryApplicationWindow |
Diff against target: |
11323 lines (+5093/-2297) 127 files modified
CMakeLists.txt (+14/-1) data/com.canonical.Unity8.gschema.xml (+2/-2) debian/changelog (+33/-0) debian/control (+5/-3) plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1) plugins/Greeter/Unity/Launcher/launcheritem.cpp (+13/-0) plugins/Greeter/Unity/Launcher/launcheritem.h (+3/-0) plugins/LightDM/FullLightDM/CMakeLists.txt (+5/-1) plugins/Unity/Indicators/CMakeLists.txt (+3/-0) plugins/Unity/Launcher/CMakeLists.txt (+4/-1) plugins/Unity/Launcher/appdrawermodel.cpp (+62/-0) plugins/Unity/Launcher/appdrawermodel.h (+33/-0) plugins/Unity/Launcher/launcheritem.cpp (+13/-0) plugins/Unity/Launcher/launcheritem.h (+4/-0) plugins/Unity/Launcher/launchermodel.cpp (+6/-37) plugins/Unity/Launcher/launchermodel.h (+0/-14) plugins/Unity/Launcher/plugin.cpp (+2/-1) plugins/Unity/Launcher/ualwrapper.cpp (+73/-0) plugins/Unity/Launcher/ualwrapper.h (+35/-0) plugins/Utils/CMakeLists.txt (+4/-0) plugins/Utils/ElapsedTimer.h (+1/-1) plugins/Utils/Timer.cpp (+5/-2) plugins/Utils/Timer.h (+5/-6) plugins/Utils/appdrawerproxymodel.cpp (+189/-0) plugins/Utils/appdrawerproxymodel.h (+87/-0) plugins/Utils/globalfunctions.cpp (+5/-0) plugins/Utils/globalfunctions.h (+2/-0) plugins/Utils/plugin.cpp (+2/-0) plugins/Utils/windowstatestorage.cpp (+23/-0) plugins/Utils/windowstatestorage.h (+5/-0) plugins/WindowManager/CMakeLists.txt (+3/-1) plugins/WindowManager/TopLevelSurfaceList.cpp (+0/-481) plugins/WindowManager/TopLevelSurfaceList.h (+0/-223) plugins/WindowManager/TopLevelWindowModel.cpp (+668/-0) plugins/WindowManager/TopLevelWindowModel.h (+260/-0) plugins/WindowManager/Window.cpp (+237/-0) plugins/WindowManager/Window.h (+161/-0) plugins/WindowManager/WindowManagerPlugin.cpp (+5/-2) po/unity8.pot (+80/-43) qml/Components/InputMethod.qml (+5/-4) qml/Components/KeyboardShortcutsOverlay.qml (+13/-0) qml/Components/KeymapSwitcher.qml (+4/-1) qml/Components/VirtualTouchPad.qml (+269/-1) qml/Dash/Previews/PreviewAudioPlayback.qml (+1/-0) qml/DisabledScreenNotice.qml (+6/-58) qml/Greeter/Greeter.qml (+1/-5) qml/Greeter/LoginList.qml (+1/-1) qml/Greeter/WideView.qml (+7/-2) qml/Launcher/BackgroundBlur.qml (+81/-0) qml/Launcher/Drawer.qml (+306/-0) qml/Launcher/DrawerGridView.qml (+47/-0) qml/Launcher/DrawerListView.qml (+32/-0) qml/Launcher/Launcher.qml (+182/-34) qml/Launcher/MoreAppsHeader.qml (+46/-0) qml/Panel/Indicators/MenuItemFactory.qml (+22/-1) qml/Shell.qml (+25/-41) qml/Stage/FakeMaximizeDelegate.qml (+7/-7) qml/Stage/MoveHandler.qml (+3/-3) qml/Stage/Spread/WindowedRightEdgeMaths.qml (+19/-4) qml/Stage/Stage.qml (+196/-186) qml/Stage/StageMaths.qml (+2/-7) qml/Stage/StagedFullscreenPolicy.qml (+4/-4) qml/Stage/TopLevelSurfaceRepeater.qml (+0/-67) qml/Stage/WindowControlsOverlay.qml (+1/-1) qml/Stage/WindowResizeArea.qml (+2/-70) qml/Stage/WindowStateSaver.qml (+63/-0) qml/Stage/WindowedFullscreenPolicy.qml (+1/-1) qml/Tutorial/TutorialLeftLong.qml (+1/-1) src/ShellApplication.cpp (+1/-0) tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp (+1/-1) tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp (+23/-23) tests/mocks/QtMultimedia/videooutput.h (+1/-1) tests/mocks/Unity/Application/ApplicationInfo.cpp (+48/-24) tests/mocks/Unity/Application/ApplicationInfo.h (+2/-2) tests/mocks/Unity/Application/ApplicationManager.cpp (+92/-63) tests/mocks/Unity/Application/ApplicationManager.h (+31/-4) tests/mocks/Unity/Application/CMakeLists.txt (+1/-1) tests/mocks/Unity/Application/MirSurface.cpp (+86/-99) tests/mocks/Unity/Application/MirSurface.h (+29/-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 (+172/-26) tests/mocks/Unity/Application/SurfaceManager.h (+28/-10) tests/mocks/Unity/Application/plugin.cpp (+4/-34) tests/mocks/Unity/Launcher/CMakeLists.txt (+5/-1) tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp (+75/-0) tests/mocks/Unity/Launcher/MockAppDrawerModel.h (+32/-0) tests/mocks/Unity/Launcher/MockLauncherItem.cpp (+13/-0) tests/mocks/Unity/Launcher/MockLauncherItem.h (+6/-0) tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+3/-0) tests/mocks/Unity/Launcher/plugin.cpp (+4/-0) tests/mocks/Utils/CMakeLists.txt (+4/-0) tests/mocks/Utils/plugin.cpp (+2/-0) tests/mocks/Utils/windowstatestorage.cpp (+23/-0) tests/mocks/Utils/windowstatestorage.h (+5/-0) tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt (+1/-1) tests/plugins/Unity/Launcher/CMakeLists.txt (+2/-1) tests/plugins/Unity/Launcher/launchermodeltest.cpp (+6/-0) tests/plugins/Utils/WindowInputMonitorTest.cpp (+76/-9) tests/qmltests/CMakeLists.txt (+1/-0) tests/qmltests/Components/tst_VirtualTouchPad.qml (+55/-1) tests/qmltests/Dash/Previews/tst_Preview.qml (+63/-0) tests/qmltests/Dash/tst_Dash.qml (+4/-3) tests/qmltests/Dash/tst_DashShell.qml (+0/-29) tests/qmltests/Launcher/tst_Drawer.qml (+264/-0) tests/qmltests/Launcher/tst_Launcher.qml (+4/-4) tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml (+6/-1) tests/qmltests/Panel/tst_ActiveCallHint.qml (+16/-2) tests/qmltests/Panel/tst_Panel.qml (+8/-1) tests/qmltests/Stage/ApplicationCheckBox.qml (+1/-1) tests/qmltests/Stage/tst_ApplicationWindow.qml (+10/-10) tests/qmltests/Stage/tst_DesktopStage.qml (+124/-80) tests/qmltests/Stage/tst_PhoneStage.qml (+18/-38) tests/qmltests/Stage/tst_SurfaceContainer.qml (+4/-0) tests/qmltests/Stage/tst_TabletStage.qml (+16/-10) tests/qmltests/Stage/tst_WindowResizeArea.qml (+1/-66) tests/qmltests/Tutorial/tst_Tutorial.qml (+30/-8) tests/qmltests/tst_DisabledScreenNotice.qml (+23/-9) tests/qmltests/tst_OrientedShell.qml (+6/-13) tests/qmltests/tst_Shell.qml (+143/-242) tests/qmltests/tst_ShellWithPin.qml (+18/-75) tests/uqmlscene/CMakeLists.txt (+5/-1) tests/utils/modules/Unity/Test/StageTestCase.qml (+68/-0) tests/utils/modules/Unity/Test/UnityTestCase.qml (+8/-9) tests/utils/modules/Unity/Test/qmldir (+2/-1) |
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 | |
Lukáš Tinkl (community) | Approve | ||
Emanuele Antonio Faraone (community) | Approve | ||
Michael Zanetti | Pending | ||
Review via email: mp+312194@code.launchpad.net |
This proposal supersedes 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 : Posted in a previous version of this proposal | # |
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
* 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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
- 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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
>>>> - 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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
> 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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
> 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 : Posted in a previous version of this proposal | # |
> 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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
> 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 : Posted in a previous version of this proposal | # |
> > 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/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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:/
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
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 : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2713
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 : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2714
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 : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2715
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
New instances of the same app do not cascade:
0. make tryShell
1. Launch dialer
2. Click the "+" button to launch a second instance
3. The new window doesn't cascade
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
If you add a trust prompt to a window and launch a second instance of the same app, the newly created window is not focused
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
Windows are not restored properly:
1. Maximize dash
2. Minimize it
3. Click the bfb icon
4. The dash window is restored to "normal" size, not to the maximized from step 1.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
On 29/11/2016 15:35, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> New instances of the same app do not cascade:
>
> 0. make tryShell
> 1. Launch dialer
> 2. Click the "+" button to launch a second instance
> 3. The new window doesn't cascade
Same happens in trunk
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
Double click to maximize breaks:
1. Double click the dash decoration to maximize the window
2. Drag it down from the panel to restore it
3. Double click again to maximize -> no longer works
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
In tablet mode, if you put a fullscreen app into side stage (like camera), the main app (dash usually) has its decoration cut at the top.
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal | # |
> In tablet mode, if you put a fullscreen app into side stage (like camera), the
> main app (dash usually) has its decoration cut at the top.
This happens in trunk too
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
On 29/11/2016 15:37, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> If you add a trust prompt to a window and launch a second instance of the same app, the newly created window is not focused
Fixed. Bug in the Unity.Application mock.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
On 29/11/2016 15:39, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> Windows are not restored properly:
>
> 1. Maximize dash
> 2. Minimize it
> 3. Click the bfb icon
> 4. The dash window is restored to "normal" size, not to the maximized from step 1.
That's due to the simplicity of the Unity.Application mock. It doesn't
have a full blown implementation of the window management bits/policy
that have been now moved to a lower level, behind
Unity.Applicati
Unity.Applicati
Unfortunately this means that right now we can no longer test everything
window management related in our qmltests. For some things untiy8 just
sits back and expects to get the correct changes to the model for free,
which it will then respond to by updating the qml items and animating
the qml scene (the view).
To be more specific, in response to the click on step 3 unity8 just
calls MirSurface-
that this surface is minimized and will restore it to its previous state
before finally focusing and raising it.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
On 29/11/2016 15:50, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> Double click to maximize breaks:
>
> 1. Double click the dash decoration to maximize the window
> 2. Drag it down from the panel to restore it
> 3. Double click again to maximize -> no longer works
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2727
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Lukáš Tinkl (lukas-kde) wrote : | # |
Doesn't compile any more (or run), need this patch: https:/
- 2726. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
Daniel d'Andrada (dandrader) wrote : | # |
On 30/11/2016 18:55, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> Doesn't compile any more (or run), need this patch: https:/
Ooops! Fixed, thanks.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2728
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2728
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2729
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2729
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: 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:2729
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2730
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2733
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2734
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2736
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Emanuele Antonio Faraone (emanueleant03) : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2737
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2738
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2742
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2742
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2743
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2744
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2744
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Lukáš Tinkl (lukas-kde) wrote : | # |
Looking good except this hard to track focus related issue:
It can happen when an application is taking a long time to start up, and you close it before it does. At that point, the focus chain is broken and the user is left with no app/window focused at all (as can be seen from the empty window title in the top panel)
Daniel d'Andrada (dandrader) wrote : | # |
> Looking good except this hard to track focus related issue:
>
> https:/
>
> It can happen when an application is taking a long time to start up, and you
> close it before it does. At that point, the focus chain is broken and the user
> is left with no app/window focused at all (as can be seen from the empty
> window title in the top panel)
Fixed, thanks.
Lukáš Tinkl (lukas-kde) wrote : | # |
In QString Window::toString() const:
I think what you're really after is this: http://
Daniel d'Andrada (dandrader) wrote : | # |
On 06/12/2016 12:57, Lukáš Tinkl wrote:
> Review: Needs Information
>
> In QString Window::toString() const:
>
> I think what you're really after is this: http://
Already have it. It uses toString() in its implementation.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2746
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2746
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2746
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: 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:2747
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Lukáš Tinkl (lukas-kde) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
Yes (they will again once the silo gets rebuilt)
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2748
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 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
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-10-24 11:34:08 +0000 |
3 | +++ CMakeLists.txt 2016-12-12 16:49:02 +0000 |
4 | @@ -40,6 +40,19 @@ |
5 | ENABLE_COVERAGE_REPORT(TARGETS ${SHELL_APP} FILTER /usr/include ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/*) |
6 | endif() |
7 | |
8 | +find_package (ECM 1.7.0 QUIET NO_MODULE) |
9 | +if (ECM_FOUND) |
10 | + # Provides us with -DECM_ENABLE_SANITIZERS='X' |
11 | + # Where X can be address, thread, memory, leak, undefined |
12 | + include("${ECM_MODULE_DIR}/ECMEnableSanitizers.cmake") |
13 | +endif() |
14 | + |
15 | +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") |
16 | + if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0") |
17 | + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override" ) |
18 | + endif() |
19 | +endif() |
20 | + |
21 | # Make sure we have all the needed symbols |
22 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs") |
23 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs") |
24 | @@ -57,7 +70,7 @@ |
25 | find_package(Qt5Concurrent 5.4 REQUIRED) |
26 | find_package(Qt5Sql 5.4 REQUIRED) |
27 | |
28 | -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=22) |
29 | +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=23) |
30 | pkg_check_modules(GEONAMES REQUIRED geonames>=0.2) |
31 | pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) |
32 | pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) |
33 | |
34 | === modified file 'data/com.canonical.Unity8.gschema.xml' |
35 | --- data/com.canonical.Unity8.gschema.xml 2016-11-16 05:54:50 +0000 |
36 | +++ data/com.canonical.Unity8.gschema.xml 2016-12-12 16:49:02 +0000 |
37 | @@ -12,7 +12,7 @@ |
38 | <description>The usage mode chosen will affect the Window Management behaviour.</description> |
39 | </key> |
40 | <key type="y" name="edge-barrier-sensitivity"> |
41 | - <default>35</default> |
42 | + <default>50</default> |
43 | <range min="1" max="100"/> |
44 | <summary>Sensitivity of screen edge barriers for the mouse pointer.</summary> |
45 | <description>Some UI actions like revealing the launcher or the applications spread are triggered by pushing the mouse pointer against a screen edge. This key defines how much you have to push in order to trigger the associated action.</description> |
46 | @@ -23,7 +23,7 @@ |
47 | <description>How much you have to push (in grid units)the mouse against an edge barrier when sensibility is 100.</description> |
48 | </key> |
49 | <key type="u" name="edge-barrier-max-push"> |
50 | - <default>60</default> |
51 | + <default>120</default> |
52 | <summary>Maximum push needed to overcome edge barrier</summary> |
53 | <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description> |
54 | </key> |
55 | |
56 | === modified file 'debian/changelog' |
57 | --- debian/changelog 2016-11-29 09:43:33 +0000 |
58 | +++ debian/changelog 2016-12-12 16:49:02 +0000 |
59 | @@ -1,3 +1,36 @@ |
60 | +unity8 (8.15+17.04.20161207.1-0ubuntu1) zesty; urgency=medium |
61 | + |
62 | + [ Albert Astals Cid ] |
63 | + * Add the Wsuggest-override flag to gcc |
64 | + * Add support for compiler sanitizers via ECM |
65 | + * Use timeStep as delay time (LP: #1642919) |
66 | + * Bring back fix for 1517830 (LP: #1517830) |
67 | + * Fix compile warnings in mocks |
68 | + * Do not hide panel when launching an application if the mouse is on |
69 | + the panel (LP: #1591311) |
70 | + |
71 | + [ Daniel d'Andrada ] |
72 | + * Fix "make tryApplicationWindow" |
73 | + * Take save/restore functions out of WindowResizeArea |
74 | + |
75 | + [ Josh Arenson ] |
76 | + * Enable the greeter to remember which session the user last logged |
77 | + into (LP: #1631365) |
78 | + |
79 | + [ Lukáš Tinkl ] |
80 | + * Fix the Super key not invoking the dash scope home (LP: #1607427) |
81 | + |
82 | + [ Michael Zanetti ] |
83 | + * Add the ApplicationDrawer |
84 | + * tune right edge push (LP: #1646094) |
85 | + * improve close button visiblity when hovering with the mouse |
86 | + * Update virtual touchpad visuals and add a tutorial. (LP: #1585220) |
87 | + |
88 | + [ Pete Woods ] |
89 | + * MenuItemFactory: Add subtitle support to SwitchItem widget |
90 | + |
91 | + -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 07 Dec 2016 13:49:24 +0000 |
92 | + |
93 | unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium |
94 | |
95 | [ Albert Astals Cid ] |
96 | |
97 | === modified file 'debian/control' |
98 | --- debian/control 2016-11-29 09:40:16 +0000 |
99 | +++ debian/control 2016-12-12 16:49:02 +0000 |
100 | @@ -38,7 +38,7 @@ |
101 | libubuntugestures5-private-dev (>= 1.3.2030), |
102 | libudev-dev, |
103 | libudm-common-dev, |
104 | - libunity-api-dev (>= 7.120), |
105 | + libunity-api-dev (>= 8.0), |
106 | libusermetricsoutput1-dev, |
107 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
108 | libx11-dev[!arm64 !armhf], |
109 | @@ -49,6 +49,7 @@ |
110 | python3-all:any, |
111 | python3-setuptools, |
112 | qml-module-qt-labs-folderlistmodel, |
113 | + qml-module-qt-labs-settings, |
114 | qml-module-qtqml-statemachine, |
115 | qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2), |
116 | qml-module-qtquick-layouts, |
117 | @@ -122,6 +123,7 @@ |
118 | qmenumodel-qml (>= 0.2.10), |
119 | qml-module-biometryd, |
120 | qml-module-qt-labs-folderlistmodel, |
121 | + qml-module-qt-labs-settings, |
122 | qml-module-qtqml-statemachine, |
123 | qml-module-qtquick-xmllistmodel, |
124 | qml-module-qtsysteminfo, |
125 | @@ -160,7 +162,7 @@ |
126 | qttranslations5-l10n, |
127 | ubuntu-thumbnailer-impl-0, |
128 | ubuntu-wallpapers, |
129 | - unity-application-impl-22, |
130 | + unity-application-impl-23, |
131 | unity-notifications-impl-3, |
132 | unity-plugin-scopes | unity-scopes-impl, |
133 | unity-scopes-impl-12, |
134 | @@ -206,7 +208,7 @@ |
135 | Depends: ${misc:Depends}, |
136 | ${shlibs:Depends}, |
137 | Provides: unity-application-impl, |
138 | - unity-application-impl-22, |
139 | + unity-application-impl-23, |
140 | Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1) |
141 | Description: Fake environment for running Unity 8 shell |
142 | Provides fake implementations of some QML modules used by Unity 8 shell |
143 | |
144 | === modified file 'plugins/Greeter/Unity/Launcher/CMakeLists.txt' |
145 | --- plugins/Greeter/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000 |
146 | +++ plugins/Greeter/Unity/Launcher/CMakeLists.txt 2016-12-12 16:49:02 +0000 |
147 | @@ -1,4 +1,4 @@ |
148 | -pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10) |
149 | +pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11) |
150 | pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt) |
151 | |
152 | add_definitions(-DSM_BUSNAME=systemBus) |
153 | |
154 | === modified file 'plugins/Greeter/Unity/Launcher/launcheritem.cpp' |
155 | --- plugins/Greeter/Unity/Launcher/launcheritem.cpp 2016-05-10 16:23:34 +0000 |
156 | +++ plugins/Greeter/Unity/Launcher/launcheritem.cpp 2016-12-12 16:49:02 +0000 |
157 | @@ -76,6 +76,19 @@ |
158 | } |
159 | } |
160 | |
161 | +QStringList LauncherItem::keywords() const |
162 | +{ |
163 | + return m_keywords; |
164 | +} |
165 | + |
166 | +void LauncherItem::setKeywords(const QStringList &keywords) |
167 | +{ |
168 | + if (m_keywords != keywords) { |
169 | + m_keywords = keywords; |
170 | + Q_EMIT keywordsChanged(keywords); |
171 | + } |
172 | +} |
173 | + |
174 | bool LauncherItem::pinned() const |
175 | { |
176 | return m_pinned; |
177 | |
178 | === modified file 'plugins/Greeter/Unity/Launcher/launcheritem.h' |
179 | --- plugins/Greeter/Unity/Launcher/launcheritem.h 2016-05-10 15:51:00 +0000 |
180 | +++ plugins/Greeter/Unity/Launcher/launcheritem.h 2016-12-12 16:49:02 +0000 |
181 | @@ -34,6 +34,7 @@ |
182 | QString appId() const override; |
183 | QString name() const override; |
184 | QString icon() const override; |
185 | + QStringList keywords() const override; |
186 | bool pinned() const override; |
187 | bool running() const override; |
188 | bool recent() const override; |
189 | @@ -49,6 +50,7 @@ |
190 | private: |
191 | void setName(const QString &name); |
192 | void setIcon(const QString &icon); |
193 | + void setKeywords(const QStringList &keywords); |
194 | void setPinned(bool pinned); |
195 | void setRunning(bool running); |
196 | void setRecent(bool recent); |
197 | @@ -63,6 +65,7 @@ |
198 | QString m_appId; |
199 | QString m_name; |
200 | QString m_icon; |
201 | + QStringList m_keywords; |
202 | bool m_pinned; |
203 | bool m_running; |
204 | bool m_recent; |
205 | |
206 | === modified file 'plugins/LightDM/FullLightDM/CMakeLists.txt' |
207 | --- plugins/LightDM/FullLightDM/CMakeLists.txt 2016-07-08 15:44:53 +0000 |
208 | +++ plugins/LightDM/FullLightDM/CMakeLists.txt 2016-12-12 16:49:02 +0000 |
209 | @@ -6,8 +6,12 @@ |
210 | |
211 | include_directories( |
212 | ../ |
213 | + ${CMAKE_CURRENT_BINARY_DIR} |
214 | + ) |
215 | + |
216 | +include_directories( |
217 | + SYSTEM |
218 | ${LIBLIGHTDM_INCLUDE_DIRS} |
219 | - ${CMAKE_CURRENT_BINARY_DIR} |
220 | ) |
221 | |
222 | foreach(source_file ${QMLPLUGIN_SRC}) |
223 | |
224 | === modified file 'plugins/Unity/Indicators/CMakeLists.txt' |
225 | --- plugins/Unity/Indicators/CMakeLists.txt 2016-06-02 09:32:33 +0000 |
226 | +++ plugins/Unity/Indicators/CMakeLists.txt 2016-12-12 16:49:02 +0000 |
227 | @@ -11,6 +11,9 @@ |
228 | include_directories( |
229 | SYSTEM |
230 | ${GLIB_INCLUDE_DIRS} |
231 | +) |
232 | +include_directories( |
233 | + SYSTEM |
234 | ${GIO_INCLUDE_DIRS} |
235 | ${QMENUMODEL_INCLUDE_DIRS} |
236 | ) |
237 | |
238 | === modified file 'plugins/Unity/Launcher/CMakeLists.txt' |
239 | --- plugins/Unity/Launcher/CMakeLists.txt 2016-10-28 12:08:59 +0000 |
240 | +++ plugins/Unity/Launcher/CMakeLists.txt 2016-12-12 16:49:02 +0000 |
241 | @@ -1,4 +1,4 @@ |
242 | -pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=10) |
243 | +pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=11) |
244 | pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt) |
245 | |
246 | add_definitions(-DSM_BUSNAME=systemBus) |
247 | @@ -25,6 +25,8 @@ |
248 | dbusinterface.cpp |
249 | gsettings.cpp |
250 | asadapter.cpp |
251 | + appdrawermodel.cpp |
252 | + ualwrapper.cpp |
253 | ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp |
254 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
255 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
256 | @@ -32,6 +34,7 @@ |
257 | ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h |
258 | ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h |
259 | ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h |
260 | + ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h |
261 | ) |
262 | |
263 | add_library(UnityLauncher-qml MODULE |
264 | |
265 | === added file 'plugins/Unity/Launcher/appdrawermodel.cpp' |
266 | --- plugins/Unity/Launcher/appdrawermodel.cpp 1970-01-01 00:00:00 +0000 |
267 | +++ plugins/Unity/Launcher/appdrawermodel.cpp 2016-12-12 16:49:02 +0000 |
268 | @@ -0,0 +1,62 @@ |
269 | +/* |
270 | + * Copyright (C) 2016 Canonical, Ltd. |
271 | + * |
272 | + * This program is free software; you can redistribute it and/or modify |
273 | + * it under the terms of the GNU General Public License as published by |
274 | + * the Free Software Foundation; version 3. |
275 | + * |
276 | + * This program is distributed in the hope that it will be useful, |
277 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
278 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
279 | + * GNU General Public License for more details. |
280 | + * |
281 | + * You should have received a copy of the GNU General Public License |
282 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
283 | + */ |
284 | + |
285 | +#include "appdrawermodel.h" |
286 | +#include "ualwrapper.h" |
287 | + |
288 | +#include <QDebug> |
289 | +#include <QDateTime> |
290 | + |
291 | +AppDrawerModel::AppDrawerModel(QObject *parent): |
292 | + AppDrawerModelInterface(parent) |
293 | +{ |
294 | + Q_FOREACH (const QString &appId, UalWrapper::installedApps()) { |
295 | + UalWrapper::AppInfo info = UalWrapper::getApplicationInfo(appId); |
296 | + if (!info.valid) { |
297 | + qWarning() << "Failed to get app info for app" << appId; |
298 | + continue; |
299 | + } |
300 | + m_list.append(new LauncherItem(appId, info.name, info.icon, this)); |
301 | + m_list.last()->setKeywords(info.keywords); |
302 | + } |
303 | + qsrand(QDateTime::currentMSecsSinceEpoch() / 100); |
304 | +} |
305 | + |
306 | +int AppDrawerModel::rowCount(const QModelIndex &parent) const |
307 | +{ |
308 | + Q_UNUSED(parent) |
309 | + return m_list.count(); |
310 | +} |
311 | + |
312 | +QVariant AppDrawerModel::data(const QModelIndex &index, int role) const |
313 | +{ |
314 | + switch (role) { |
315 | + case RoleAppId: |
316 | + return m_list.at(index.row())->appId(); |
317 | + case RoleName: |
318 | + return m_list.at(index.row())->name(); |
319 | + case RoleIcon: |
320 | + return m_list.at(index.row())->icon(); |
321 | + case RoleKeywords: |
322 | + return m_list.at(index.row())->keywords(); |
323 | + case RoleUsage: |
324 | + // FIXME: u-a-l needs to provide API for usage stats. |
325 | + // don't forget to drop the qsrand() call in the ctor when dropping this. |
326 | + return qrand(); |
327 | + } |
328 | + |
329 | + return QVariant(); |
330 | +} |
331 | |
332 | === added file 'plugins/Unity/Launcher/appdrawermodel.h' |
333 | --- plugins/Unity/Launcher/appdrawermodel.h 1970-01-01 00:00:00 +0000 |
334 | +++ plugins/Unity/Launcher/appdrawermodel.h 2016-12-12 16:49:02 +0000 |
335 | @@ -0,0 +1,33 @@ |
336 | +/* |
337 | + * Copyright (C) 2016 Canonical, Ltd. |
338 | + * |
339 | + * This program is free software; you can redistribute it and/or modify |
340 | + * it under the terms of the GNU General Public License as published by |
341 | + * the Free Software Foundation; version 3. |
342 | + * |
343 | + * This program is distributed in the hope that it will be useful, |
344 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
345 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
346 | + * GNU General Public License for more details. |
347 | + * |
348 | + * You should have received a copy of the GNU General Public License |
349 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
350 | + */ |
351 | + |
352 | + |
353 | +#include <unity/shell/launcher/AppDrawerModelInterface.h> |
354 | + |
355 | +#include "launcheritem.h" |
356 | + |
357 | +class AppDrawerModel: public AppDrawerModelInterface |
358 | +{ |
359 | + Q_OBJECT |
360 | +public: |
361 | + AppDrawerModel(QObject* parent = nullptr); |
362 | + |
363 | + int rowCount(const QModelIndex &parent) const override; |
364 | + QVariant data(const QModelIndex &index, int role) const override; |
365 | + |
366 | +private: |
367 | + QList<LauncherItem*> m_list; |
368 | +}; |
369 | |
370 | === modified file 'plugins/Unity/Launcher/launcheritem.cpp' |
371 | --- plugins/Unity/Launcher/launcheritem.cpp 2016-10-28 11:54:16 +0000 |
372 | +++ plugins/Unity/Launcher/launcheritem.cpp 2016-12-12 16:49:02 +0000 |
373 | @@ -90,6 +90,19 @@ |
374 | } |
375 | } |
376 | |
377 | +QStringList LauncherItem::keywords() const |
378 | +{ |
379 | + return m_keywords; |
380 | +} |
381 | + |
382 | +void LauncherItem::setKeywords(const QStringList &keywords) |
383 | +{ |
384 | + if (m_keywords != keywords) { |
385 | + m_keywords = keywords; |
386 | + Q_EMIT keywordsChanged(keywords); |
387 | + } |
388 | +} |
389 | + |
390 | bool LauncherItem::pinned() const |
391 | { |
392 | return m_pinned; |
393 | |
394 | === modified file 'plugins/Unity/Launcher/launcheritem.h' |
395 | --- plugins/Unity/Launcher/launcheritem.h 2016-05-10 15:51:00 +0000 |
396 | +++ plugins/Unity/Launcher/launcheritem.h 2016-12-12 16:49:02 +0000 |
397 | @@ -37,6 +37,7 @@ |
398 | QString appId() const override; |
399 | QString name() const override; |
400 | QString icon() const override; |
401 | + QStringList keywords() const override; |
402 | bool pinned() const override; |
403 | bool running() const override; |
404 | bool recent() const override; |
405 | @@ -52,6 +53,7 @@ |
406 | private: |
407 | void setName(const QString &name); |
408 | void setIcon(const QString &icon); |
409 | + void setKeywords(const QStringList &keywords); |
410 | void setPinned(bool pinned); |
411 | void setRunning(bool running); |
412 | void setRecent(bool recent); |
413 | @@ -66,6 +68,7 @@ |
414 | QString m_appId; |
415 | QString m_name; |
416 | QString m_icon; |
417 | + QStringList m_keywords; |
418 | bool m_pinned; |
419 | bool m_running; |
420 | bool m_recent; |
421 | @@ -79,6 +82,7 @@ |
422 | QuickListEntry m_quitAction; |
423 | |
424 | friend class LauncherModel; |
425 | + friend class AppDrawerModel; |
426 | }; |
427 | |
428 | #endif // LAUNCHERITEM_H |
429 | |
430 | === modified file 'plugins/Unity/Launcher/launchermodel.cpp' |
431 | --- plugins/Unity/Launcher/launchermodel.cpp 2016-09-23 15:10:26 +0000 |
432 | +++ plugins/Unity/Launcher/launchermodel.cpp 2016-12-12 16:49:02 +0000 |
433 | @@ -19,18 +19,14 @@ |
434 | #include "gsettings.h" |
435 | #include "dbusinterface.h" |
436 | #include "asadapter.h" |
437 | +#include "ualwrapper.h" |
438 | |
439 | -#include <ubuntu-app-launch/appid.h> |
440 | -#include <ubuntu-app-launch/application.h> |
441 | -#include <ubuntu-app-launch/registry.h> |
442 | #include <unity/shell/application/ApplicationInfoInterface.h> |
443 | #include <unity/shell/application/MirSurfaceListInterface.h> |
444 | |
445 | #include <QDesktopServices> |
446 | #include <QDebug> |
447 | |
448 | -namespace ual = ubuntu::app_launch; |
449 | - |
450 | using namespace unity::shell::application; |
451 | |
452 | LauncherModel::LauncherModel(QObject *parent): |
453 | @@ -38,8 +34,7 @@ |
454 | m_settings(new GSettings(this)), |
455 | m_dbusIface(new DBusInterface(this)), |
456 | m_asAdapter(new ASAdapter()), |
457 | - m_appManager(nullptr), |
458 | - m_ualRegistry(std::make_shared<ual::Registry>()) |
459 | + m_appManager(nullptr) |
460 | { |
461 | connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged); |
462 | connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged); |
463 | @@ -159,7 +154,7 @@ |
464 | index = m_list.count(); |
465 | } |
466 | |
467 | - auto appInfo = getApplicationInfo(appId); |
468 | + UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId); |
469 | if (!appInfo.valid) { |
470 | qWarning() << "Can't pin application, appId not found:" << appId; |
471 | return; |
472 | @@ -335,32 +330,6 @@ |
473 | return -1; |
474 | } |
475 | |
476 | -LauncherModel::AppInfo LauncherModel::getApplicationInfo(const QString &appId) |
477 | -{ |
478 | - AppInfo info; |
479 | - |
480 | - ual::AppID ualAppId = ual::AppID::find(m_ualRegistry, appId.toStdString()); |
481 | - if (ualAppId.empty()) { |
482 | - return info; |
483 | - } |
484 | - |
485 | - std::shared_ptr<ual::Application> ualApp; |
486 | - try |
487 | - { |
488 | - ualApp = ual::Application::create(ualAppId, m_ualRegistry); |
489 | - } |
490 | - catch (std::runtime_error &e) |
491 | - { |
492 | - qWarning() << "Couldn't find application info for" << appId << "-" << e.what(); |
493 | - return info; |
494 | - } |
495 | - |
496 | - info.valid = true; |
497 | - info.name = QString::fromStdString(ualApp->info()->name()); |
498 | - info.icon = QString::fromStdString(ualApp->info()->iconPath()); |
499 | - return info; |
500 | -} |
501 | - |
502 | void LauncherModel::progressChanged(const QString &appId, int progress) |
503 | { |
504 | const int idx = findApplication(appId); |
505 | @@ -408,7 +377,7 @@ |
506 | } |
507 | } else { |
508 | // Need to create a new LauncherItem and show the highlight |
509 | - auto appInfo = getApplicationInfo(appId); |
510 | + UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(appId); |
511 | if (countVisible && appInfo.valid) { |
512 | LauncherItem *item = new LauncherItem(appId, |
513 | appInfo.name, |
514 | @@ -428,7 +397,7 @@ |
515 | // First walk through all the existing items and see if we need to remove something |
516 | QList<LauncherItem*> toBeRemoved; |
517 | Q_FOREACH (LauncherItem* item, m_list) { |
518 | - auto appInfo = getApplicationInfo(item->appId()); |
519 | + UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(item->appId()); |
520 | if (!appInfo.valid) { |
521 | // Application no longer available => drop it! |
522 | toBeRemoved << item; |
523 | @@ -480,7 +449,7 @@ |
524 | if (itemIndex == -1) { |
525 | // Need to add it. Just add it into the addedIndex to keep same ordering as the list |
526 | // in the settings. |
527 | - auto appInfo = getApplicationInfo(entry); |
528 | + UalWrapper::AppInfo appInfo = UalWrapper::getApplicationInfo(entry); |
529 | if (!appInfo.valid) { |
530 | continue; |
531 | } |
532 | |
533 | === modified file 'plugins/Unity/Launcher/launchermodel.h' |
534 | --- plugins/Unity/Launcher/launchermodel.h 2016-09-23 14:44:26 +0000 |
535 | +++ plugins/Unity/Launcher/launchermodel.h 2016-12-12 16:49:02 +0000 |
536 | @@ -27,12 +27,6 @@ |
537 | class DBusInterface; |
538 | class ASAdapter; |
539 | |
540 | -namespace ubuntu { |
541 | - namespace app_launch { |
542 | - class Registry; |
543 | - } |
544 | -} |
545 | - |
546 | using namespace unity::shell::launcher; |
547 | using namespace unity::shell::application; |
548 | |
549 | @@ -73,13 +67,6 @@ |
550 | |
551 | void unpin(const QString &appId); |
552 | |
553 | - struct AppInfo { |
554 | - bool valid = false; |
555 | - QString name; |
556 | - QString icon; |
557 | - }; |
558 | - AppInfo getApplicationInfo(const QString &appId); |
559 | - |
560 | private Q_SLOTS: |
561 | void countChanged(const QString &appId, int count); |
562 | void countVisibleChanged(const QString &appId, bool count); |
563 | @@ -98,7 +85,6 @@ |
564 | ASAdapter *m_asAdapter; |
565 | |
566 | ApplicationManagerInterface *m_appManager; |
567 | - std::shared_ptr<ubuntu::app_launch::Registry> m_ualRegistry; |
568 | |
569 | friend class LauncherModelTest; |
570 | }; |
571 | |
572 | === modified file 'plugins/Unity/Launcher/plugin.cpp' |
573 | --- plugins/Unity/Launcher/plugin.cpp 2015-09-14 09:11:08 +0000 |
574 | +++ plugins/Unity/Launcher/plugin.cpp 2016-12-12 16:49:02 +0000 |
575 | @@ -26,7 +26,7 @@ |
576 | // local |
577 | #include "launchermodel.h" |
578 | #include "launcheritem.h" |
579 | - |
580 | +#include "appdrawermodel.h" |
581 | |
582 | using namespace unity::shell::launcher; |
583 | |
584 | @@ -46,4 +46,5 @@ |
585 | qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider); |
586 | qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", QStringLiteral("Can't create new Launcher Items in QML. Get them from the LauncherModel.")); |
587 | qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", QStringLiteral("Can't create a QuickListModel in QML. Get them from the LauncherItems.")); |
588 | + qmlRegisterType<AppDrawerModel>(uri, 0, 1, "AppDrawerModel"); |
589 | } |
590 | |
591 | === added file 'plugins/Unity/Launcher/ualwrapper.cpp' |
592 | --- plugins/Unity/Launcher/ualwrapper.cpp 1970-01-01 00:00:00 +0000 |
593 | +++ plugins/Unity/Launcher/ualwrapper.cpp 2016-12-12 16:49:02 +0000 |
594 | @@ -0,0 +1,73 @@ |
595 | +/* |
596 | + * Copyright (C) 2016 Canonical, Ltd. |
597 | + * |
598 | + * This program is free software; you can redistribute it and/or modify |
599 | + * it under the terms of the GNU General Public License as published by |
600 | + * the Free Software Foundation; version 3. |
601 | + * |
602 | + * This program is distributed in the hope that it will be useful, |
603 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
604 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
605 | + * GNU General Public License for more details. |
606 | + * |
607 | + * You should have received a copy of the GNU General Public License |
608 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
609 | + */ |
610 | + |
611 | +#include <QDebug> |
612 | + |
613 | +#include "ualwrapper.h" |
614 | + |
615 | +#include <ubuntu-app-launch/registry.h> |
616 | +using namespace ubuntu::app_launch; |
617 | + |
618 | +UalWrapper::UalWrapper(QObject *parent): |
619 | + QObject(parent) |
620 | +{ |
621 | + |
622 | +} |
623 | + |
624 | +QStringList UalWrapper::installedApps() |
625 | +{ |
626 | + QStringList appIds; |
627 | + try { |
628 | + for (const std::shared_ptr<Application> &app : Registry::installedApps()) { |
629 | + if (!app->appId().package.value().empty()) { |
630 | + appIds << QString::fromStdString(app->appId().package.value() + "_" + app->appId().appname.value()); |
631 | + } else { |
632 | + appIds << QString::fromStdString(app->appId().appname); |
633 | + } |
634 | + } |
635 | + } catch (const std::runtime_error &e) { |
636 | + qWarning() << "ubuntu-all-launch threw an exception listing apps:" << e.what(); |
637 | + } |
638 | + |
639 | + return appIds; |
640 | +} |
641 | + |
642 | +UalWrapper::AppInfo UalWrapper::getApplicationInfo(const QString &appId) |
643 | +{ |
644 | + AppInfo info; |
645 | + |
646 | + try { |
647 | + AppID ualAppId = AppID::find(appId.toStdString()); |
648 | + if (ualAppId.empty()) { |
649 | + qWarning() << "Empty ualAppId result for" << appId; |
650 | + return info; |
651 | + } |
652 | + |
653 | + std::shared_ptr<Application> ualApp; |
654 | + ualApp = Application::create(ualAppId, Registry::getDefault()); |
655 | + |
656 | + info.name = QString::fromStdString(ualApp->info()->name()); |
657 | + info.icon = QString::fromStdString(ualApp->info()->iconPath()); |
658 | + for (const std::string &keyword : ualApp->info()->keywords().value()) { |
659 | + info.keywords << QString::fromStdString(keyword); |
660 | + } |
661 | + info.valid = true; |
662 | + } catch (const std::runtime_error &e) { |
663 | + qWarning() << "ubuntu-app-launch threw an exception getting app info for appId:" << appId << ":" << e.what(); |
664 | + } |
665 | + |
666 | + return info; |
667 | +} |
668 | |
669 | === added file 'plugins/Unity/Launcher/ualwrapper.h' |
670 | --- plugins/Unity/Launcher/ualwrapper.h 1970-01-01 00:00:00 +0000 |
671 | +++ plugins/Unity/Launcher/ualwrapper.h 2016-12-12 16:49:02 +0000 |
672 | @@ -0,0 +1,35 @@ |
673 | +/* |
674 | + * Copyright (C) 2016 Canonical, Ltd. |
675 | + * |
676 | + * This program is free software; you can redistribute it and/or modify |
677 | + * it under the terms of the GNU General Public License as published by |
678 | + * the Free Software Foundation; version 3. |
679 | + * |
680 | + * This program is distributed in the hope that it will be useful, |
681 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
682 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
683 | + * GNU General Public License for more details. |
684 | + * |
685 | + * You should have received a copy of the GNU General Public License |
686 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
687 | + */ |
688 | + |
689 | +#include <QObject> |
690 | + |
691 | +class UalWrapper: public QObject |
692 | +{ |
693 | + Q_OBJECT |
694 | +public: |
695 | + struct AppInfo { |
696 | + bool valid = false; |
697 | + QString name; |
698 | + QString icon; |
699 | + QStringList keywords; |
700 | + }; |
701 | + |
702 | + UalWrapper(QObject* parent = nullptr); |
703 | + |
704 | + static QStringList installedApps(); |
705 | + static AppInfo getApplicationInfo(const QString &appId); |
706 | + |
707 | +}; |
708 | |
709 | === modified file 'plugins/Utils/CMakeLists.txt' |
710 | --- plugins/Utils/CMakeLists.txt 2016-06-29 18:05:44 +0000 |
711 | +++ plugins/Utils/CMakeLists.txt 2016-12-12 16:49:02 +0000 |
712 | @@ -15,6 +15,10 @@ |
713 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
714 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
715 | applicationsfiltermodel.cpp |
716 | + ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/AppDrawerModelInterface.h |
717 | + ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h |
718 | + ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h |
719 | + appdrawerproxymodel.cpp |
720 | constants.cpp |
721 | WindowInputMonitor.cpp |
722 | inputwatcher.cpp |
723 | |
724 | === modified file 'plugins/Utils/ElapsedTimer.h' |
725 | --- plugins/Utils/ElapsedTimer.h 2015-04-22 10:40:54 +0000 |
726 | +++ plugins/Utils/ElapsedTimer.h 2016-12-12 16:49:02 +0000 |
727 | @@ -41,7 +41,7 @@ |
728 | public: |
729 | void start() override { m_timer.start(); } |
730 | qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); } |
731 | - qint64 elapsed() const override { return m_timer.elapsed(); } |
732 | + qint64 elapsed() const override { return m_timer.isValid() ? m_timer.elapsed() : 0; } |
733 | private: |
734 | QElapsedTimer m_timer; |
735 | }; |
736 | |
737 | === modified file 'plugins/Utils/Timer.cpp' |
738 | --- plugins/Utils/Timer.cpp 2015-04-22 10:40:54 +0000 |
739 | +++ plugins/Utils/Timer.cpp 2016-12-12 16:49:02 +0000 |
740 | @@ -37,13 +37,16 @@ |
741 | void Timer::start() |
742 | { |
743 | m_timer.start(); |
744 | - AbstractTimer::start(); |
745 | } |
746 | |
747 | void Timer::stop() |
748 | { |
749 | m_timer.stop(); |
750 | - AbstractTimer::stop(); |
751 | +} |
752 | + |
753 | +bool Timer::isRunning() const |
754 | +{ |
755 | + return m_timer.isActive(); |
756 | } |
757 | |
758 | bool Timer::isSingleShot() const |
759 | |
760 | === modified file 'plugins/Utils/Timer.h' |
761 | --- plugins/Utils/Timer.h 2015-04-22 10:40:54 +0000 |
762 | +++ plugins/Utils/Timer.h 2016-12-12 16:49:02 +0000 |
763 | @@ -30,18 +30,16 @@ |
764 | { |
765 | Q_OBJECT |
766 | public: |
767 | - AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {} |
768 | + AbstractTimer(QObject *parent) : QObject(parent) {} |
769 | virtual int interval() const = 0; |
770 | virtual void setInterval(int msecs) = 0; |
771 | - virtual void start() { m_isRunning = true; } |
772 | - virtual void stop() { m_isRunning = false; } |
773 | - bool isRunning() const { return m_isRunning; } |
774 | + virtual void start() = 0; |
775 | + virtual void stop() = 0; |
776 | + virtual bool isRunning() const = 0; |
777 | virtual bool isSingleShot() const = 0; |
778 | virtual void setSingleShot(bool value) = 0; |
779 | Q_SIGNALS: |
780 | void timeout(); |
781 | -private: |
782 | - bool m_isRunning; |
783 | }; |
784 | |
785 | /** A QTimer wrapper */ |
786 | @@ -55,6 +53,7 @@ |
787 | void setInterval(int msecs) override; |
788 | void start() override; |
789 | void stop() override; |
790 | + bool isRunning() const override; |
791 | bool isSingleShot() const override; |
792 | void setSingleShot(bool value) override; |
793 | private: |
794 | |
795 | === added file 'plugins/Utils/appdrawerproxymodel.cpp' |
796 | --- plugins/Utils/appdrawerproxymodel.cpp 1970-01-01 00:00:00 +0000 |
797 | +++ plugins/Utils/appdrawerproxymodel.cpp 2016-12-12 16:49:02 +0000 |
798 | @@ -0,0 +1,189 @@ |
799 | +/* |
800 | + * Copyright (C) 2016 Canonical, Ltd. |
801 | + * |
802 | + * This program is free software; you can redistribute it and/or modify |
803 | + * it under the terms of the GNU General Public License as published by |
804 | + * the Free Software Foundation; version 3. |
805 | + * |
806 | + * This program is distributed in the hope that it will be useful, |
807 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
808 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
809 | + * GNU General Public License for more details. |
810 | + * |
811 | + * You should have received a copy of the GNU General Public License |
812 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
813 | + */ |
814 | + |
815 | +#include "appdrawerproxymodel.h" |
816 | + |
817 | +#include <unity/shell/launcher/LauncherItemInterface.h> |
818 | + |
819 | +#include <QDebug> |
820 | + |
821 | +AppDrawerProxyModel::AppDrawerProxyModel(QObject *parent): |
822 | + QSortFilterProxyModel(parent) |
823 | +{ |
824 | + setSortRole(AppDrawerModelInterface::RoleName); |
825 | + setSortLocaleAware(true); |
826 | + sort(0); |
827 | + |
828 | + connect(this, &QAbstractListModel::rowsInserted, this, &AppDrawerProxyModel::countChanged); |
829 | + connect(this, &QAbstractListModel::rowsRemoved, this, &AppDrawerProxyModel::countChanged); |
830 | + connect(this, &QAbstractListModel::layoutChanged, this, &AppDrawerProxyModel::countChanged); |
831 | +} |
832 | + |
833 | +QAbstractItemModel *AppDrawerProxyModel::source() const |
834 | +{ |
835 | + return m_source; |
836 | +} |
837 | + |
838 | +void AppDrawerProxyModel::setSource(QAbstractItemModel *source) |
839 | +{ |
840 | + if (m_source != source) { |
841 | + m_source = source; |
842 | + setSourceModel(m_source); |
843 | + setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage); |
844 | + connect(m_source, &QAbstractItemModel::rowsRemoved, this, &AppDrawerProxyModel::invalidateFilter); |
845 | + connect(m_source, &QAbstractItemModel::rowsInserted, this, &AppDrawerProxyModel::invalidateFilter); |
846 | + Q_EMIT sourceChanged(); |
847 | + } |
848 | +} |
849 | + |
850 | +AppDrawerProxyModel::GroupBy AppDrawerProxyModel::group() const |
851 | +{ |
852 | + return m_group; |
853 | +} |
854 | + |
855 | +void AppDrawerProxyModel::setGroup(AppDrawerProxyModel::GroupBy group) |
856 | +{ |
857 | + if (m_group != group) { |
858 | + m_group = group; |
859 | + Q_EMIT groupChanged(); |
860 | + invalidateFilter(); |
861 | + } |
862 | +} |
863 | + |
864 | +QString AppDrawerProxyModel::filterLetter() const |
865 | +{ |
866 | + return m_filterLetter; |
867 | +} |
868 | + |
869 | +void AppDrawerProxyModel::setFilterLetter(const QString &filterLetter) |
870 | +{ |
871 | + if (m_filterLetter != filterLetter) { |
872 | + m_filterLetter = filterLetter; |
873 | + Q_EMIT filterLetterChanged(); |
874 | + invalidateFilter(); |
875 | + } |
876 | +} |
877 | + |
878 | +QString AppDrawerProxyModel::filterString() const |
879 | +{ |
880 | + return m_filterString; |
881 | +} |
882 | + |
883 | +void AppDrawerProxyModel::setFilterString(const QString &filterString) |
884 | +{ |
885 | + if (m_filterString != filterString) { |
886 | + m_filterString = filterString; |
887 | + Q_EMIT filterStringChanged(); |
888 | + invalidateFilter(); |
889 | + } |
890 | +} |
891 | + |
892 | +AppDrawerProxyModel::SortBy AppDrawerProxyModel::sortBy() const |
893 | +{ |
894 | + return m_sortBy; |
895 | +} |
896 | + |
897 | +void AppDrawerProxyModel::setSortBy(AppDrawerProxyModel::SortBy sortBy) |
898 | +{ |
899 | + if (m_sortBy != sortBy) { |
900 | + m_sortBy = sortBy; |
901 | + Q_EMIT sortByChanged(); |
902 | + setSortRole(m_sortBy == SortByAToZ ? AppDrawerModelInterface::RoleName : AppDrawerModelInterface::RoleUsage); |
903 | + sort(0); |
904 | + } |
905 | +} |
906 | + |
907 | +int AppDrawerProxyModel::count() const |
908 | +{ |
909 | + return rowCount(); |
910 | +} |
911 | + |
912 | +QVariant AppDrawerProxyModel::data(const QModelIndex &index, int role) const |
913 | +{ |
914 | + QModelIndex idx = mapToSource(index); |
915 | + if (role == Qt::UserRole) { |
916 | + QString name = m_source->data(idx, AppDrawerModelInterface::RoleName).toString(); |
917 | + return name.length() > 0 ? QString(name.at(0)).toUpper() : QChar(); |
918 | + } |
919 | + return m_source->data(idx, role); |
920 | +} |
921 | + |
922 | +QHash<int, QByteArray> AppDrawerProxyModel::roleNames() const |
923 | +{ |
924 | + if (m_source) { |
925 | + QHash<int, QByteArray> roles = m_source->roleNames(); |
926 | + roles.insert(Qt::UserRole, "letter"); |
927 | + return roles; |
928 | + } |
929 | + return QHash<int, QByteArray>(); |
930 | +} |
931 | + |
932 | +bool AppDrawerProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const |
933 | +{ |
934 | + Q_UNUSED(source_parent) |
935 | + |
936 | + if (m_group == GroupByAToZ && source_row > 0) { |
937 | + QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString(); |
938 | + QChar currentLetter = currentName.length() > 0 ? currentName.at(0) : QChar(); |
939 | + QString previousName = m_source->data(m_source->index(source_row - 1,0 ), AppDrawerModelInterface::RoleName).toString(); |
940 | + QChar previousLetter = previousName.length() > 0 ? previousName.at(0) : QChar(); |
941 | + if (currentLetter.toLower() == previousLetter.toLower()) { |
942 | + return false; |
943 | + } |
944 | + } else if(m_group == GroupByAll && source_row > 0) { |
945 | + return false; |
946 | + } |
947 | + if (!m_filterLetter.isEmpty()) { |
948 | + QString currentName = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString(); |
949 | + QString currentLetter = currentName.length() > 0 ? QString(currentName.at(0)) : QString(); |
950 | + if (currentLetter.toLower() != m_filterLetter.toLower()) { |
951 | + return false; |
952 | + } |
953 | + } |
954 | + if (!m_filterString.isEmpty()) { |
955 | + QStringList allWords = m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleKeywords).toStringList(); |
956 | + allWords.prepend(m_source->data(m_source->index(source_row, 0), AppDrawerModelInterface::RoleName).toString()); |
957 | + bool found = false; |
958 | + Q_FOREACH (const QString currentWord, allWords) { |
959 | + if (currentWord.toLower().startsWith(m_filterString.toLower())) { |
960 | + found = true; |
961 | + break; |
962 | + } |
963 | + } |
964 | + if (!found) { |
965 | + return false; |
966 | + } |
967 | + } |
968 | + return true; |
969 | +} |
970 | + |
971 | +QString AppDrawerProxyModel::appId(int index) const |
972 | +{ |
973 | + if (index >= 0 && index < rowCount()) { |
974 | + QModelIndex sourceIndex = mapToSource(this->index(index, 0)); |
975 | + |
976 | + AppDrawerModelInterface* adm = dynamic_cast<AppDrawerModelInterface*>(m_source); |
977 | + if (adm) { |
978 | + return adm->data(sourceIndex, AppDrawerModelInterface::RoleAppId).toString(); |
979 | + } |
980 | + |
981 | + AppDrawerProxyModel* adpm = qobject_cast<AppDrawerProxyModel*>(m_source); |
982 | + if (adpm) { |
983 | + return adpm->appId(sourceIndex.row()); |
984 | + } |
985 | + } |
986 | + return nullptr; |
987 | +} |
988 | |
989 | === added file 'plugins/Utils/appdrawerproxymodel.h' |
990 | --- plugins/Utils/appdrawerproxymodel.h 1970-01-01 00:00:00 +0000 |
991 | +++ plugins/Utils/appdrawerproxymodel.h 2016-12-12 16:49:02 +0000 |
992 | @@ -0,0 +1,87 @@ |
993 | +/* |
994 | + * Copyright (C) 2016 Canonical, Ltd. |
995 | + * |
996 | + * This program is free software; you can redistribute it and/or modify |
997 | + * it under the terms of the GNU General Public License as published by |
998 | + * the Free Software Foundation; version 3. |
999 | + * |
1000 | + * This program is distributed in the hope that it will be useful, |
1001 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1002 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1003 | + * GNU General Public License for more details. |
1004 | + * |
1005 | + * You should have received a copy of the GNU General Public License |
1006 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1007 | + */ |
1008 | + |
1009 | +#include <QSortFilterProxyModel> |
1010 | + |
1011 | +#include <unity/shell/launcher/AppDrawerModelInterface.h> |
1012 | + |
1013 | +using namespace unity::shell::launcher; |
1014 | + |
1015 | +class AppDrawerProxyModel: public QSortFilterProxyModel |
1016 | +{ |
1017 | + Q_OBJECT |
1018 | + Q_ENUMS(GroupBy) |
1019 | + Q_ENUMS(SortBy) |
1020 | + Q_PROPERTY(QAbstractItemModel* source READ source WRITE setSource NOTIFY sourceChanged) |
1021 | + Q_PROPERTY(GroupBy group READ group WRITE setGroup NOTIFY groupChanged) |
1022 | + Q_PROPERTY(QString filterLetter READ filterLetter WRITE setFilterLetter NOTIFY filterLetterChanged) |
1023 | + Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged) |
1024 | + Q_PROPERTY(SortBy sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged) |
1025 | + Q_PROPERTY(int count READ count NOTIFY countChanged) |
1026 | + |
1027 | +public: |
1028 | + enum GroupBy { |
1029 | + GroupByNone, |
1030 | + GroupByAll, |
1031 | + GroupByAToZ |
1032 | + }; |
1033 | + enum SortBy { |
1034 | + SortByAToZ, |
1035 | + SortByUsage |
1036 | + }; |
1037 | + |
1038 | + AppDrawerProxyModel(QObject* parent = nullptr); |
1039 | + |
1040 | + QAbstractItemModel* source() const; |
1041 | + void setSource(QAbstractItemModel* source); |
1042 | + |
1043 | + GroupBy group() const; |
1044 | + void setGroup(GroupBy group); |
1045 | + |
1046 | + QString filterLetter() const; |
1047 | + void setFilterLetter(const QString &filterLetter); |
1048 | + |
1049 | + QString filterString() const; |
1050 | + void setFilterString(const QString &filterString); |
1051 | + |
1052 | + SortBy sortBy() const; |
1053 | + void setSortBy(SortBy sortBy); |
1054 | + |
1055 | + int count() const; |
1056 | + |
1057 | + QVariant data(const QModelIndex &index, int role) const override; |
1058 | + QHash<int, QByteArray> roleNames() const override; |
1059 | + |
1060 | + Q_INVOKABLE QString appId(int index) const; |
1061 | + |
1062 | +protected: |
1063 | + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; |
1064 | + |
1065 | +Q_SIGNALS: |
1066 | + void sourceChanged(); |
1067 | + void groupChanged(); |
1068 | + void filterLetterChanged(); |
1069 | + void filterStringChanged(); |
1070 | + void sortByChanged(); |
1071 | + void countChanged(); |
1072 | + |
1073 | +private: |
1074 | + QAbstractItemModel* m_source = nullptr; |
1075 | + GroupBy m_group = GroupByNone; |
1076 | + QString m_filterLetter; |
1077 | + QString m_filterString; |
1078 | + SortBy m_sortBy = SortByAToZ; |
1079 | +}; |
1080 | |
1081 | === modified file 'plugins/Utils/globalfunctions.cpp' |
1082 | --- plugins/Utils/globalfunctions.cpp 2016-06-02 09:32:33 +0000 |
1083 | +++ plugins/Utils/globalfunctions.cpp 2016-12-12 16:49:02 +0000 |
1084 | @@ -51,3 +51,8 @@ |
1085 | } |
1086 | return nullptr; |
1087 | } |
1088 | + |
1089 | +bool GlobalFunctions::itemUnderMouse(QQuickItem* item) |
1090 | +{ |
1091 | + return item && item->isUnderMouse(); |
1092 | +} |
1093 | |
1094 | === modified file 'plugins/Utils/globalfunctions.h' |
1095 | --- plugins/Utils/globalfunctions.h 2016-03-11 20:18:12 +0000 |
1096 | +++ plugins/Utils/globalfunctions.h 2016-12-12 16:49:02 +0000 |
1097 | @@ -37,6 +37,8 @@ |
1098 | int x, |
1099 | int y, |
1100 | QJSValue matcher); |
1101 | + |
1102 | + static Q_INVOKABLE bool itemUnderMouse(QQuickItem* item); |
1103 | }; |
1104 | |
1105 | #endif // GLOBALFUNCTIONS_H |
1106 | |
1107 | === modified file 'plugins/Utils/plugin.cpp' |
1108 | --- plugins/Utils/plugin.cpp 2016-06-28 20:38:00 +0000 |
1109 | +++ plugins/Utils/plugin.cpp 2016-12-12 16:49:02 +0000 |
1110 | @@ -39,6 +39,7 @@ |
1111 | #include "deviceconfigparser.h" |
1112 | #include "globalfunctions.h" |
1113 | #include "URLDispatcher.h" |
1114 | +#include "appdrawerproxymodel.h" |
1115 | |
1116 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
1117 | { |
1118 | @@ -82,4 +83,5 @@ |
1119 | qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser"); |
1120 | qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions); |
1121 | qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher"); |
1122 | + qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel"); |
1123 | } |
1124 | |
1125 | === modified file 'plugins/Utils/windowstatestorage.cpp' |
1126 | --- plugins/Utils/windowstatestorage.cpp 2016-08-23 13:53:07 +0000 |
1127 | +++ plugins/Utils/windowstatestorage.cpp 2016-12-12 16:49:02 +0000 |
1128 | @@ -189,3 +189,26 @@ |
1129 | } |
1130 | return query; |
1131 | } |
1132 | + |
1133 | +Mir::State WindowStateStorage::toMirState(WindowState state) const |
1134 | +{ |
1135 | + // assumes a single state (not an OR of several) |
1136 | + switch (state) { |
1137 | + case WindowStateMaximized: return Mir::MaximizedState; |
1138 | + case WindowStateMinimized: return Mir::MinimizedState; |
1139 | + case WindowStateFullscreen: return Mir::FullscreenState; |
1140 | + case WindowStateMaximizedLeft: return Mir::MaximizedLeftState; |
1141 | + case WindowStateMaximizedRight: return Mir::MaximizedRightState; |
1142 | + case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState; |
1143 | + case WindowStateMaximizedVertically: return Mir::VertMaximizedState; |
1144 | + case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState; |
1145 | + case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState; |
1146 | + case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState; |
1147 | + case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState; |
1148 | + |
1149 | + case WindowStateNormal: |
1150 | + case WindowStateRestored: |
1151 | + default: |
1152 | + return Mir::RestoredState; |
1153 | + } |
1154 | +} |
1155 | |
1156 | === modified file 'plugins/Utils/windowstatestorage.h' |
1157 | --- plugins/Utils/windowstatestorage.h 2016-08-23 13:53:07 +0000 |
1158 | +++ plugins/Utils/windowstatestorage.h 2016-12-12 16:49:02 +0000 |
1159 | @@ -19,6 +19,9 @@ |
1160 | #include <QMutex> |
1161 | #include <QFuture> |
1162 | |
1163 | +// unity-api |
1164 | +#include <unity/shell/application/Mir.h> |
1165 | + |
1166 | class WindowStateStorage: public QObject |
1167 | { |
1168 | Q_OBJECT |
1169 | @@ -56,6 +59,8 @@ |
1170 | Q_INVOKABLE void saveStage(const QString &appId, int stage); |
1171 | Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const; |
1172 | |
1173 | + Q_INVOKABLE Mir::State toMirState(WindowState state) const; |
1174 | + |
1175 | private: |
1176 | void initdb(); |
1177 | |
1178 | |
1179 | === modified file 'plugins/WindowManager/CMakeLists.txt' |
1180 | --- plugins/WindowManager/CMakeLists.txt 2016-06-20 09:43:38 +0000 |
1181 | +++ plugins/WindowManager/CMakeLists.txt 2016-12-12 16:49:02 +0000 |
1182 | @@ -1,10 +1,12 @@ |
1183 | set(WINDOWMANAGER_SRC |
1184 | - TopLevelSurfaceList.cpp |
1185 | + TopLevelWindowModel.cpp |
1186 | + Window.cpp |
1187 | WindowManagerPlugin.cpp |
1188 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
1189 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h |
1190 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h |
1191 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h |
1192 | + ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h |
1193 | ) |
1194 | |
1195 | add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC}) |
1196 | |
1197 | === removed file 'plugins/WindowManager/TopLevelSurfaceList.cpp' |
1198 | --- plugins/WindowManager/TopLevelSurfaceList.cpp 2016-09-14 15:02:01 +0000 |
1199 | +++ plugins/WindowManager/TopLevelSurfaceList.cpp 1970-01-01 00:00:00 +0000 |
1200 | @@ -1,481 +0,0 @@ |
1201 | -/* |
1202 | - * Copyright (C) 2016 Canonical, Ltd. |
1203 | - * |
1204 | - * This program is free software; you can redistribute it and/or modify |
1205 | - * it under the terms of the GNU General Public License as published by |
1206 | - * the Free Software Foundation; version 3. |
1207 | - * |
1208 | - * This program is distributed in the hope that it will be useful, |
1209 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1210 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1211 | - * GNU General Public License for more details. |
1212 | - * |
1213 | - * You should have received a copy of the GNU General Public License |
1214 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1215 | - */ |
1216 | - |
1217 | -#include "TopLevelSurfaceList.h" |
1218 | - |
1219 | -// unity-api |
1220 | -#include <unity/shell/application/ApplicationInfoInterface.h> |
1221 | -#include <unity/shell/application/MirSurfaceInterface.h> |
1222 | -#include <unity/shell/application/MirSurfaceListInterface.h> |
1223 | - |
1224 | -#include <QMetaObject> |
1225 | - |
1226 | -Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist", QtDebugMsg) |
1227 | - |
1228 | -#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__ |
1229 | - |
1230 | -using namespace unity::shell::application; |
1231 | - |
1232 | -TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) : |
1233 | - QAbstractListModel(parent) |
1234 | -{ |
1235 | - DEBUG_MSG << "()"; |
1236 | -} |
1237 | - |
1238 | -TopLevelSurfaceList::~TopLevelSurfaceList() |
1239 | -{ |
1240 | - DEBUG_MSG << "()"; |
1241 | -} |
1242 | - |
1243 | -int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const |
1244 | -{ |
1245 | - return !parent.isValid() ? m_surfaceList.size() : 0; |
1246 | -} |
1247 | - |
1248 | -QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const |
1249 | -{ |
1250 | - if (index.row() < 0 || index.row() >= m_surfaceList.size()) |
1251 | - return QVariant(); |
1252 | - |
1253 | - if (role == SurfaceRole) { |
1254 | - MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface; |
1255 | - return QVariant::fromValue(surface); |
1256 | - } else if (role == ApplicationRole) { |
1257 | - return QVariant::fromValue(m_surfaceList.at(index.row()).application); |
1258 | - } else if (role == IdRole) { |
1259 | - return QVariant::fromValue(m_surfaceList.at(index.row()).id); |
1260 | - } else { |
1261 | - return QVariant(); |
1262 | - } |
1263 | -} |
1264 | - |
1265 | -void TopLevelSurfaceList::raise(MirSurfaceInterface *surface) |
1266 | -{ |
1267 | - if (!surface) |
1268 | - return; |
1269 | - |
1270 | - DEBUG_MSG << "(MirSurface[" << (void*)surface << "])"; |
1271 | - |
1272 | - int i = indexOf(surface); |
1273 | - if (i != -1) { |
1274 | - raiseId(m_surfaceList.at(i).id); |
1275 | - } |
1276 | -} |
1277 | - |
1278 | -void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application) |
1279 | -{ |
1280 | - DEBUG_MSG << "(" << application->appId() << ")"; |
1281 | - |
1282 | - appendSurfaceHelper(nullptr, application); |
1283 | -} |
1284 | - |
1285 | -void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application) |
1286 | -{ |
1287 | - Q_ASSERT(surface != nullptr); |
1288 | - |
1289 | - bool filledPlaceholder = false; |
1290 | - for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) { |
1291 | - ModelEntry &entry = m_surfaceList[i]; |
1292 | - if (entry.application == application && entry.surface == nullptr) { |
1293 | - entry.surface = surface; |
1294 | - connectSurface(surface); |
1295 | - DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface |
1296 | - << ", filling out placeholder. after: " << toString(); |
1297 | - Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole); |
1298 | - filledPlaceholder = true; |
1299 | - } |
1300 | - } |
1301 | - |
1302 | - if (!filledPlaceholder) { |
1303 | - DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row"; |
1304 | - appendSurfaceHelper(surface, application); |
1305 | - } |
1306 | -} |
1307 | - |
1308 | -void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application) |
1309 | -{ |
1310 | - if (m_modelState == IdleState) { |
1311 | - m_modelState = InsertingState; |
1312 | - beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/); |
1313 | - } else { |
1314 | - Q_ASSERT(m_modelState == ResettingState); |
1315 | - // No point in signaling anything if we're resetting the whole model |
1316 | - } |
1317 | - |
1318 | - int id = generateId(); |
1319 | - m_surfaceList.append(ModelEntry(surface, application, id)); |
1320 | - if (surface) { |
1321 | - connectSurface(surface); |
1322 | - } |
1323 | - |
1324 | - if (m_modelState == InsertingState) { |
1325 | - endInsertRows(); |
1326 | - Q_EMIT countChanged(); |
1327 | - Q_EMIT listChanged(); |
1328 | - m_modelState = IdleState; |
1329 | - } |
1330 | - |
1331 | - DEBUG_MSG << " after " << toString(); |
1332 | -} |
1333 | - |
1334 | -void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface) |
1335 | -{ |
1336 | - connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){ |
1337 | - if (!live) { |
1338 | - onSurfaceDied(surface); |
1339 | - } |
1340 | - }); |
1341 | - connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); }); |
1342 | -} |
1343 | - |
1344 | -void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface) |
1345 | -{ |
1346 | - int i = indexOf(surface); |
1347 | - if (i == -1) { |
1348 | - return; |
1349 | - } |
1350 | - |
1351 | - auto application = m_surfaceList[i].application; |
1352 | - |
1353 | - // can't be starting if it already has a surface |
1354 | - Q_ASSERT(application->state() != ApplicationInfoInterface::Starting); |
1355 | - |
1356 | - if (application->state() == ApplicationInfoInterface::Running) { |
1357 | - m_surfaceList[i].removeOnceSurfaceDestroyed = true; |
1358 | - } else { |
1359 | - // assume it got killed by the out-of-memory daemon. |
1360 | - // |
1361 | - // So leave entry in the model and only remove its surface, so shell can display a screenshot |
1362 | - // in its place. |
1363 | - m_surfaceList[i].removeOnceSurfaceDestroyed = false; |
1364 | - } |
1365 | -} |
1366 | - |
1367 | -void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface) |
1368 | -{ |
1369 | - int i = indexOf(surface); |
1370 | - if (i == -1) { |
1371 | - return; |
1372 | - } |
1373 | - |
1374 | - if (m_surfaceList[i].removeOnceSurfaceDestroyed) { |
1375 | - removeAt(i); |
1376 | - } else { |
1377 | - m_surfaceList[i].surface = nullptr; |
1378 | - Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole); |
1379 | - DEBUG_MSG << " Removed surface from entry. After: " << toString(); |
1380 | - } |
1381 | -} |
1382 | - |
1383 | -void TopLevelSurfaceList::removeAt(int index) |
1384 | -{ |
1385 | - if (m_modelState == IdleState) { |
1386 | - beginRemoveRows(QModelIndex(), index, index); |
1387 | - m_modelState = RemovingState; |
1388 | - } else { |
1389 | - Q_ASSERT(m_modelState == ResettingState); |
1390 | - // No point in signaling anything if we're resetting the whole model |
1391 | - } |
1392 | - |
1393 | - m_surfaceList.removeAt(index); |
1394 | - |
1395 | - if (m_modelState == RemovingState) { |
1396 | - endRemoveRows(); |
1397 | - Q_EMIT countChanged(); |
1398 | - Q_EMIT listChanged(); |
1399 | - m_modelState = IdleState; |
1400 | - } |
1401 | - |
1402 | - DEBUG_MSG << " after " << toString(); |
1403 | -} |
1404 | - |
1405 | -int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface) |
1406 | -{ |
1407 | - for (int i = 0; i < m_surfaceList.count(); ++i) { |
1408 | - if (m_surfaceList.at(i).surface == surface) { |
1409 | - return i; |
1410 | - } |
1411 | - } |
1412 | - return -1; |
1413 | -} |
1414 | - |
1415 | -void TopLevelSurfaceList::move(int from, int to) |
1416 | -{ |
1417 | - if (from == to) return; |
1418 | - DEBUG_MSG << " from=" << from << " to=" << to; |
1419 | - |
1420 | - if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) { |
1421 | - QModelIndex parent; |
1422 | - /* When moving an item down, the destination index needs to be incremented |
1423 | - by one, as explained in the documentation: |
1424 | - http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ |
1425 | - |
1426 | - Q_ASSERT(m_modelState == IdleState); |
1427 | - m_modelState = MovingState; |
1428 | - |
1429 | - beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); |
1430 | - m_surfaceList.move(from, to); |
1431 | - endMoveRows(); |
1432 | - Q_EMIT listChanged(); |
1433 | - |
1434 | - m_modelState = IdleState; |
1435 | - |
1436 | - DEBUG_MSG << " after " << toString(); |
1437 | - } |
1438 | -} |
1439 | - |
1440 | -MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const |
1441 | -{ |
1442 | - if (index >=0 && index < m_surfaceList.count()) { |
1443 | - return m_surfaceList[index].surface; |
1444 | - } else { |
1445 | - return nullptr; |
1446 | - } |
1447 | -} |
1448 | - |
1449 | -ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const |
1450 | -{ |
1451 | - if (index >=0 && index < m_surfaceList.count()) { |
1452 | - return m_surfaceList[index].application; |
1453 | - } else { |
1454 | - return nullptr; |
1455 | - } |
1456 | -} |
1457 | - |
1458 | -int TopLevelSurfaceList::idAt(int index) const |
1459 | -{ |
1460 | - if (index >=0 && index < m_surfaceList.count()) { |
1461 | - return m_surfaceList[index].id; |
1462 | - } else { |
1463 | - return 0; |
1464 | - } |
1465 | -} |
1466 | - |
1467 | -int TopLevelSurfaceList::indexForId(int id) const |
1468 | -{ |
1469 | - for (int i = 0; i < m_surfaceList.count(); ++i) { |
1470 | - if (m_surfaceList[i].id == id) { |
1471 | - return i; |
1472 | - } |
1473 | - } |
1474 | - return -1; |
1475 | -} |
1476 | - |
1477 | -void TopLevelSurfaceList::doRaiseId(int id) |
1478 | -{ |
1479 | - int fromIndex = indexForId(id); |
1480 | - if (fromIndex != -1) { |
1481 | - move(fromIndex, 0 /* toIndex */); |
1482 | - } |
1483 | -} |
1484 | - |
1485 | -void TopLevelSurfaceList::raiseId(int id) |
1486 | -{ |
1487 | - if (m_modelState == IdleState) { |
1488 | - DEBUG_MSG << "(id=" << id << ") - do it now."; |
1489 | - doRaiseId(id); |
1490 | - } else { |
1491 | - DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop."; |
1492 | - // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts |
1493 | - // if we perform yet another model change straight away. |
1494 | - // |
1495 | - // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says |
1496 | - // the index is definitely within bounds. |
1497 | - QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id)); |
1498 | - } |
1499 | -} |
1500 | - |
1501 | -int TopLevelSurfaceList::generateId() |
1502 | -{ |
1503 | - int id = m_nextId; |
1504 | - m_nextId = nextFreeId(m_nextId + 1); |
1505 | - Q_EMIT nextIdChanged(); |
1506 | - return id; |
1507 | -} |
1508 | - |
1509 | -int TopLevelSurfaceList::nextFreeId(int candidateId) |
1510 | -{ |
1511 | - if (candidateId > m_maxId) { |
1512 | - return nextFreeId(1); |
1513 | - } else { |
1514 | - if (indexForId(candidateId) == -1) { |
1515 | - // it's indeed free |
1516 | - return candidateId; |
1517 | - } else { |
1518 | - return nextFreeId(candidateId + 1); |
1519 | - } |
1520 | - } |
1521 | -} |
1522 | - |
1523 | -QString TopLevelSurfaceList::toString() |
1524 | -{ |
1525 | - QString str; |
1526 | - for (int i = 0; i < m_surfaceList.count(); ++i) { |
1527 | - auto item = m_surfaceList.at(i); |
1528 | - |
1529 | - QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)") |
1530 | - .arg(i) |
1531 | - .arg(item.application->appId()) |
1532 | - .arg((qintptr)item.surface, 0, 16) |
1533 | - .arg(item.id); |
1534 | - |
1535 | - if (i > 0) { |
1536 | - str.append(","); |
1537 | - } |
1538 | - str.append(itemStr); |
1539 | - } |
1540 | - return str; |
1541 | -} |
1542 | - |
1543 | -void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application) |
1544 | -{ |
1545 | - DEBUG_MSG << "(" << application->appId() << ")"; |
1546 | - Q_ASSERT(!m_applications.contains(application)); |
1547 | - m_applications.append(application); |
1548 | - |
1549 | - MirSurfaceListInterface *surfaceList = application->surfaceList(); |
1550 | - |
1551 | - if (application->state() != ApplicationInfoInterface::Stopped) { |
1552 | - if (surfaceList->count() == 0) { |
1553 | - appendPlaceholder(application); |
1554 | - } else { |
1555 | - for (int i = 0; i < surfaceList->count(); ++i) { |
1556 | - appendSurface(surfaceList->get(i), application); |
1557 | - } |
1558 | - } |
1559 | - } |
1560 | - |
1561 | - connect(surfaceList, &QAbstractItemModel::rowsInserted, this, |
1562 | - [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last) |
1563 | - { |
1564 | - for (int i = last; i >= first; --i) { |
1565 | - this->appendSurface(surfaceList->get(i), application); |
1566 | - } |
1567 | - }); |
1568 | -} |
1569 | - |
1570 | -void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application) |
1571 | -{ |
1572 | - DEBUG_MSG << "(" << application->appId() << ")"; |
1573 | - Q_ASSERT(m_applications.contains(application)); |
1574 | - |
1575 | - MirSurfaceListInterface *surfaceList = application->surfaceList(); |
1576 | - |
1577 | - disconnect(surfaceList, 0, this, 0); |
1578 | - |
1579 | - Q_ASSERT(m_modelState == IdleState); |
1580 | - m_modelState = RemovingState; |
1581 | - |
1582 | - int i = 0; |
1583 | - while (i < m_surfaceList.count()) { |
1584 | - if (m_surfaceList.at(i).application == application) { |
1585 | - beginRemoveRows(QModelIndex(), i, i); |
1586 | - m_surfaceList.removeAt(i); |
1587 | - endRemoveRows(); |
1588 | - Q_EMIT countChanged(); |
1589 | - Q_EMIT listChanged(); |
1590 | - } else { |
1591 | - ++i; |
1592 | - } |
1593 | - } |
1594 | - |
1595 | - m_modelState = IdleState; |
1596 | - |
1597 | - DEBUG_MSG << " after " << toString(); |
1598 | - |
1599 | - m_applications.removeAll(application); |
1600 | -} |
1601 | - |
1602 | -QAbstractListModel *TopLevelSurfaceList::applicationsModel() const |
1603 | -{ |
1604 | - return m_applicationsModel; |
1605 | -} |
1606 | - |
1607 | -void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value) |
1608 | -{ |
1609 | - if (m_applicationsModel == value) { |
1610 | - return; |
1611 | - } |
1612 | - |
1613 | - DEBUG_MSG << "(" << value << ")"; |
1614 | - |
1615 | - Q_ASSERT(m_modelState == IdleState); |
1616 | - m_modelState = ResettingState; |
1617 | - |
1618 | - beginResetModel(); |
1619 | - |
1620 | - if (m_applicationsModel) { |
1621 | - m_surfaceList.clear(); |
1622 | - m_applications.clear(); |
1623 | - disconnect(m_applicationsModel, 0, this, 0); |
1624 | - } |
1625 | - |
1626 | - m_applicationsModel = value; |
1627 | - |
1628 | - if (m_applicationsModel) { |
1629 | - findApplicationRole(); |
1630 | - |
1631 | - connect(m_applicationsModel, &QAbstractItemModel::rowsInserted, |
1632 | - this, [this](const QModelIndex &/*parent*/, int first, int last) { |
1633 | - for (int i = first; i <= last; ++i) { |
1634 | - auto application = getApplicationFromModelAt(i); |
1635 | - addApplication(application); |
1636 | - } |
1637 | - }); |
1638 | - |
1639 | - connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved, |
1640 | - this, [this](const QModelIndex &/*parent*/, int first, int last) { |
1641 | - for (int i = first; i <= last; ++i) { |
1642 | - auto application = getApplicationFromModelAt(i); |
1643 | - removeApplication(application); |
1644 | - } |
1645 | - }); |
1646 | - |
1647 | - for (int i = 0; i < m_applicationsModel->rowCount(); ++i) { |
1648 | - auto application = getApplicationFromModelAt(i); |
1649 | - addApplication(application); |
1650 | - } |
1651 | - } |
1652 | - |
1653 | - endResetModel(); |
1654 | - m_modelState = IdleState; |
1655 | -} |
1656 | - |
1657 | -ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index) |
1658 | -{ |
1659 | - QModelIndex modelIndex = m_applicationsModel->index(index); |
1660 | - |
1661 | - QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole); |
1662 | - |
1663 | - // variant.value<ApplicationInfoInterface*>() returns null for some reason. |
1664 | - return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>()); |
1665 | -} |
1666 | - |
1667 | -void TopLevelSurfaceList::findApplicationRole() |
1668 | -{ |
1669 | - QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames(); |
1670 | - |
1671 | - m_applicationRole = -1; |
1672 | - for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) { |
1673 | - if (i.value() == "application") { |
1674 | - m_applicationRole = i.key(); |
1675 | - } |
1676 | - } |
1677 | - |
1678 | - if (m_applicationRole == -1) { |
1679 | - qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role."); |
1680 | - } |
1681 | -} |
1682 | |
1683 | === removed file 'plugins/WindowManager/TopLevelSurfaceList.h' |
1684 | --- plugins/WindowManager/TopLevelSurfaceList.h 2016-04-04 13:39:44 +0000 |
1685 | +++ plugins/WindowManager/TopLevelSurfaceList.h 1970-01-01 00:00:00 +0000 |
1686 | @@ -1,223 +0,0 @@ |
1687 | -/* |
1688 | - * Copyright (C) 2016 Canonical, Ltd. |
1689 | - * |
1690 | - * This program is free software; you can redistribute it and/or modify |
1691 | - * it under the terms of the GNU General Public License as published by |
1692 | - * the Free Software Foundation; version 3. |
1693 | - * |
1694 | - * This program is distributed in the hope that it will be useful, |
1695 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1696 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1697 | - * GNU General Public License for more details. |
1698 | - * |
1699 | - * You should have received a copy of the GNU General Public License |
1700 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1701 | - */ |
1702 | - |
1703 | -#ifndef TOPLEVELSURFACELIST_H |
1704 | -#define TOPLEVELSURFACELIST_H |
1705 | - |
1706 | -#include <QAbstractListModel> |
1707 | -#include <QList> |
1708 | -#include <QLoggingCategory> |
1709 | - |
1710 | -Q_DECLARE_LOGGING_CATEGORY(UNITY_TOPSURFACELIST) |
1711 | - |
1712 | -namespace unity { |
1713 | - namespace shell { |
1714 | - namespace application { |
1715 | - class ApplicationInfoInterface; |
1716 | - class MirSurfaceInterface; |
1717 | - } |
1718 | - } |
1719 | -} |
1720 | - |
1721 | -/** |
1722 | - * @brief A model of top-level surfaces |
1723 | - * |
1724 | - * It's an abstraction of top-level application windows. |
1725 | - * |
1726 | - * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is |
1727 | - * still starting up. A shell should then display a splash screen or saved screenshot of the application |
1728 | - * until its surface comes up. |
1729 | - * |
1730 | - * As applications can have multiple surfaces and you can also have entries without surfaces at all, |
1731 | - * the only way to unambiguously refer to an entry in this model is through its id. |
1732 | - */ |
1733 | -class TopLevelSurfaceList : public QAbstractListModel |
1734 | -{ |
1735 | - |
1736 | - Q_OBJECT |
1737 | - |
1738 | - /** |
1739 | - * @brief A list model of applications. |
1740 | - * |
1741 | - * It's expected to have a role called "application" which returns a ApplicationInfoInterface |
1742 | - */ |
1743 | - Q_PROPERTY(QAbstractListModel* applicationsModel READ applicationsModel |
1744 | - WRITE setApplicationsModel |
1745 | - NOTIFY applicationsModelChanged) |
1746 | - |
1747 | - /** |
1748 | - * @brief Number of top-level surfaces in this model |
1749 | - * |
1750 | - * This is the same as rowCount, added in order to keep compatibility with QML ListModels. |
1751 | - */ |
1752 | - Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
1753 | - |
1754 | - /** |
1755 | - The id to be used on the next entry created |
1756 | - Useful for tests |
1757 | - */ |
1758 | - Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged) |
1759 | -public: |
1760 | - |
1761 | - /** |
1762 | - * @brief The Roles supported by the model |
1763 | - * |
1764 | - * SurfaceRole - A MirSurfaceInterface. It will be null if the application is still starting up |
1765 | - * ApplicationRole - An ApplicationInfoInterface |
1766 | - * IdRole - A unique identifier for this entry. Useful to unambiguosly track elements as they move around in the list |
1767 | - */ |
1768 | - enum Roles { |
1769 | - SurfaceRole = Qt::UserRole, |
1770 | - ApplicationRole = Qt::UserRole + 1, |
1771 | - IdRole = Qt::UserRole + 2, |
1772 | - }; |
1773 | - |
1774 | - explicit TopLevelSurfaceList(QObject *parent = nullptr); |
1775 | - virtual ~TopLevelSurfaceList(); |
1776 | - |
1777 | - // QAbstractItemModel methods |
1778 | - int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
1779 | - QVariant data(const QModelIndex& index, int role) const override; |
1780 | - QHash<int, QByteArray> roleNames() const override { |
1781 | - QHash<int, QByteArray> roleNames { {SurfaceRole, "surface"}, |
1782 | - {ApplicationRole, "application"}, |
1783 | - {IdRole, "id"} }; |
1784 | - return roleNames; |
1785 | - } |
1786 | - |
1787 | - int nextId() const { return m_nextId; } |
1788 | - |
1789 | - QAbstractListModel *applicationsModel() const; |
1790 | - void setApplicationsModel(QAbstractListModel*); |
1791 | - |
1792 | -public Q_SLOTS: |
1793 | - /** |
1794 | - * @brief Returns the surface at the given index |
1795 | - * |
1796 | - * It will be a nullptr if the application is still starting up and thus hasn't yet created |
1797 | - * and drawn into a surface. |
1798 | - */ |
1799 | - unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const; |
1800 | - |
1801 | - /** |
1802 | - * @brief Returns the application at the given index |
1803 | - */ |
1804 | - unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const; |
1805 | - |
1806 | - /** |
1807 | - * @brief Returns the unique id of the element at the given index |
1808 | - */ |
1809 | - int idAt(int index) const; |
1810 | - |
1811 | - /** |
1812 | - * @brief Returns the index where the row with the given id is located |
1813 | - * |
1814 | - * Returns -1 if there's no row with the given id. |
1815 | - */ |
1816 | - int indexForId(int id) const; |
1817 | - |
1818 | - /** |
1819 | - * @brief Raises the row with the given id to index 0 |
1820 | - */ |
1821 | - void raiseId(int id); |
1822 | - |
1823 | - void doRaiseId(int id); |
1824 | - |
1825 | -Q_SIGNALS: |
1826 | - void countChanged(); |
1827 | - |
1828 | - /** |
1829 | - * @brief Emitted when the list changes |
1830 | - * |
1831 | - * Emitted when model gains an element, loses an element or when elements exchange positions. |
1832 | - */ |
1833 | - void listChanged(); |
1834 | - |
1835 | - void nextIdChanged(); |
1836 | - |
1837 | - void applicationsModelChanged(); |
1838 | - |
1839 | -private: |
1840 | - void addApplication(unity::shell::application::ApplicationInfoInterface *application); |
1841 | - void removeApplication(unity::shell::application::ApplicationInfoInterface *application); |
1842 | - |
1843 | - int indexOf(unity::shell::application::MirSurfaceInterface *surface); |
1844 | - void raise(unity::shell::application::MirSurfaceInterface *surface); |
1845 | - void move(int from, int to); |
1846 | - void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface, |
1847 | - unity::shell::application::ApplicationInfoInterface *application); |
1848 | - void connectSurface(unity::shell::application::MirSurfaceInterface *surface); |
1849 | - int generateId(); |
1850 | - int nextFreeId(int candidateId); |
1851 | - QString toString(); |
1852 | - void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface); |
1853 | - void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface); |
1854 | - void removeAt(int index); |
1855 | - void findApplicationRole(); |
1856 | - |
1857 | - unity::shell::application::ApplicationInfoInterface *getApplicationFromModelAt(int index); |
1858 | - |
1859 | - /* |
1860 | - Placeholder for a future surface from a starting or running application. |
1861 | - Enables shell to give immediate feedback to the user by showing, eg, |
1862 | - a splash screen. |
1863 | - |
1864 | - It's a model row containing a null surface and the given application. |
1865 | - */ |
1866 | - void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application); |
1867 | - |
1868 | - /* |
1869 | - Adds a model row with the given surface and application |
1870 | - |
1871 | - Alternatively, if a placeholder exists for the given application it's |
1872 | - filled with the given surface instead. |
1873 | - */ |
1874 | - void appendSurface(unity::shell::application::MirSurfaceInterface *surface, |
1875 | - unity::shell::application::ApplicationInfoInterface *application); |
1876 | - |
1877 | - struct ModelEntry { |
1878 | - ModelEntry(unity::shell::application::MirSurfaceInterface *surface, unity::shell::application::ApplicationInfoInterface *application, int id) |
1879 | - : surface(surface), application(application), id(id) {} |
1880 | - unity::shell::application::MirSurfaceInterface *surface; |
1881 | - unity::shell::application::ApplicationInfoInterface *application; |
1882 | - int id; |
1883 | - bool removeOnceSurfaceDestroyed{false}; |
1884 | - }; |
1885 | - |
1886 | - QList<ModelEntry> m_surfaceList; |
1887 | - int m_nextId{1}; |
1888 | - static const int m_maxId{1000000}; |
1889 | - |
1890 | - // applications that are being monitored |
1891 | - QList<unity::shell::application::ApplicationInfoInterface *> m_applications; |
1892 | - |
1893 | - QAbstractListModel* m_applicationsModel{nullptr}; |
1894 | - int m_applicationRole{-1}; |
1895 | - |
1896 | - enum ModelState { |
1897 | - IdleState, |
1898 | - InsertingState, |
1899 | - RemovingState, |
1900 | - MovingState, |
1901 | - ResettingState |
1902 | - }; |
1903 | - ModelState m_modelState{IdleState}; |
1904 | -}; |
1905 | - |
1906 | -Q_DECLARE_METATYPE(TopLevelSurfaceList*) |
1907 | -Q_DECLARE_METATYPE(QAbstractListModel*) |
1908 | - |
1909 | -#endif // TOPLEVELSURFACELIST_H |
1910 | |
1911 | === added file 'plugins/WindowManager/TopLevelWindowModel.cpp' |
1912 | --- plugins/WindowManager/TopLevelWindowModel.cpp 1970-01-01 00:00:00 +0000 |
1913 | +++ plugins/WindowManager/TopLevelWindowModel.cpp 2016-12-12 16:49:02 +0000 |
1914 | @@ -0,0 +1,668 @@ |
1915 | +/* |
1916 | + * Copyright (C) 2016 Canonical, Ltd. |
1917 | + * |
1918 | + * This program is free software: you can redistribute it and/or modify it under |
1919 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1920 | + * the Free Software Foundation. |
1921 | + * |
1922 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1923 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1924 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1925 | + * Lesser General Public License for more details. |
1926 | + * |
1927 | + * You should have received a copy of the GNU Lesser General Public License |
1928 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1929 | + */ |
1930 | + |
1931 | +#include "TopLevelWindowModel.h" |
1932 | + |
1933 | +// unity-api |
1934 | +#include <unity/shell/application/ApplicationInfoInterface.h> |
1935 | +#include <unity/shell/application/ApplicationManagerInterface.h> |
1936 | +#include <unity/shell/application/MirSurfaceInterface.h> |
1937 | +#include <unity/shell/application/MirSurfaceListInterface.h> |
1938 | +#include <unity/shell/application/SurfaceManagerInterface.h> |
1939 | + |
1940 | +// Qt |
1941 | +#include <QGuiApplication> |
1942 | +#include <QDebug> |
1943 | + |
1944 | +// local |
1945 | +#include "Window.h" |
1946 | + |
1947 | +Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg) |
1948 | + |
1949 | +#define DEBUG_MSG qCDebug(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__ |
1950 | +#define INFO_MSG qCInfo(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__ |
1951 | + |
1952 | +namespace unityapi = unity::shell::application; |
1953 | + |
1954 | +TopLevelWindowModel::TopLevelWindowModel() |
1955 | +{ |
1956 | +} |
1957 | + |
1958 | +void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInterface* value) |
1959 | +{ |
1960 | + if (m_applicationManager == value) { |
1961 | + return; |
1962 | + } |
1963 | + |
1964 | + DEBUG_MSG << "(" << value << ")"; |
1965 | + |
1966 | + Q_ASSERT(m_modelState == IdleState); |
1967 | + m_modelState = ResettingState; |
1968 | + |
1969 | + beginResetModel(); |
1970 | + |
1971 | + if (m_applicationManager) { |
1972 | + m_windowModel.clear(); |
1973 | + disconnect(m_applicationManager, 0, this, 0); |
1974 | + } |
1975 | + |
1976 | + m_applicationManager = value; |
1977 | + |
1978 | + if (m_applicationManager) { |
1979 | + connect(m_applicationManager, &QAbstractItemModel::rowsInserted, |
1980 | + this, [this](const QModelIndex &/*parent*/, int first, int last) { |
1981 | + for (int i = first; i <= last; ++i) { |
1982 | + auto application = m_applicationManager->get(i); |
1983 | + addApplication(application); |
1984 | + } |
1985 | + }); |
1986 | + |
1987 | + connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved, |
1988 | + this, [this](const QModelIndex &/*parent*/, int first, int last) { |
1989 | + for (int i = first; i <= last; ++i) { |
1990 | + auto application = m_applicationManager->get(i); |
1991 | + removeApplication(application); |
1992 | + } |
1993 | + }); |
1994 | + |
1995 | + for (int i = 0; i < m_applicationManager->rowCount(); ++i) { |
1996 | + auto application = m_applicationManager->get(i); |
1997 | + addApplication(application); |
1998 | + } |
1999 | + } |
2000 | + |
2001 | + endResetModel(); |
2002 | + m_modelState = IdleState; |
2003 | +} |
2004 | + |
2005 | +void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *surfaceManager) |
2006 | +{ |
2007 | + if (surfaceManager == m_surfaceManager) { |
2008 | + return; |
2009 | + } |
2010 | + |
2011 | + DEBUG_MSG << "(" << surfaceManager << ")"; |
2012 | + |
2013 | + if (m_surfaceManager) { |
2014 | + disconnect(m_surfaceManager, 0, this, 0); |
2015 | + } |
2016 | + |
2017 | + m_surfaceManager = surfaceManager; |
2018 | + |
2019 | + if (m_surfaceManager) { |
2020 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfaceCreated, this, &TopLevelWindowModel::onSurfaceCreated); |
2021 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised); |
2022 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted); |
2023 | + connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded); |
2024 | + } |
2025 | + |
2026 | + Q_EMIT surfaceManagerChanged(m_surfaceManager); |
2027 | +} |
2028 | + |
2029 | +void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application) |
2030 | +{ |
2031 | + DEBUG_MSG << "(" << application->appId() << ")"; |
2032 | + |
2033 | + if (application->state() != unityapi::ApplicationInfoInterface::Stopped && application->surfaceList()->count() == 0) { |
2034 | + prependPlaceholder(application); |
2035 | + } |
2036 | +} |
2037 | + |
2038 | +void TopLevelWindowModel::removeApplication(unityapi::ApplicationInfoInterface *application) |
2039 | +{ |
2040 | + DEBUG_MSG << "(" << application->appId() << ")"; |
2041 | + |
2042 | + Q_ASSERT(m_modelState == IdleState); |
2043 | + |
2044 | + int i = 0; |
2045 | + while (i < m_windowModel.count()) { |
2046 | + if (m_windowModel.at(i).application == application) { |
2047 | + removeAt(i); |
2048 | + } else { |
2049 | + ++i; |
2050 | + } |
2051 | + } |
2052 | +} |
2053 | + |
2054 | +void TopLevelWindowModel::prependPlaceholder(unityapi::ApplicationInfoInterface *application) |
2055 | +{ |
2056 | + INFO_MSG << "(" << application->appId() << ")"; |
2057 | + |
2058 | + prependSurfaceHelper(nullptr, application); |
2059 | +} |
2060 | + |
2061 | +void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application) |
2062 | +{ |
2063 | + Q_ASSERT(surface != nullptr); |
2064 | + |
2065 | + bool filledPlaceholder = false; |
2066 | + for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) { |
2067 | + ModelEntry &entry = m_windowModel[i]; |
2068 | + if (entry.application == application && entry.window->surface() == nullptr) { |
2069 | + entry.window->setSurface(surface); |
2070 | + connectSurface(surface); |
2071 | + INFO_MSG << " appId=" << application->appId() << " surface=" << surface |
2072 | + << ", filling out placeholder. after: " << toString(); |
2073 | + filledPlaceholder = true; |
2074 | + } |
2075 | + } |
2076 | + |
2077 | + if (!filledPlaceholder) { |
2078 | + INFO_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row"; |
2079 | + prependSurfaceHelper(surface, application); |
2080 | + } |
2081 | +} |
2082 | + |
2083 | +void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application) |
2084 | +{ |
2085 | + if (m_modelState == IdleState) { |
2086 | + m_modelState = InsertingState; |
2087 | + beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/); |
2088 | + } else { |
2089 | + Q_ASSERT(m_modelState == ResettingState); |
2090 | + // No point in signaling anything if we're resetting the whole model |
2091 | + } |
2092 | + |
2093 | + int id = generateId(); |
2094 | + Window *window = new Window(id, this); |
2095 | + if (surface) { |
2096 | + window->setSurface(surface); |
2097 | + } |
2098 | + m_windowModel.prepend(ModelEntry(window, application)); |
2099 | + if (surface) { |
2100 | + connectSurface(surface); |
2101 | + } |
2102 | + |
2103 | + connectWindow(window); |
2104 | + |
2105 | + if (m_modelState == InsertingState) { |
2106 | + endInsertRows(); |
2107 | + Q_EMIT countChanged(); |
2108 | + Q_EMIT listChanged(); |
2109 | + m_modelState = IdleState; |
2110 | + } |
2111 | + |
2112 | + if (!surface) { |
2113 | + activateEmptyWindow(window); |
2114 | + } |
2115 | + |
2116 | + INFO_MSG << " after " << toString(); |
2117 | +} |
2118 | + |
2119 | +void TopLevelWindowModel::connectWindow(Window *window) |
2120 | +{ |
2121 | + connect(window, &Window::focusRequested, this, [this, window]() { |
2122 | + if (!window->surface()) { |
2123 | + activateEmptyWindow(window); |
2124 | + } |
2125 | + }); |
2126 | + |
2127 | + connect(window, &Window::focusedChanged, this, [this, window](bool focused) { |
2128 | + if (window->surface()) { |
2129 | + // Condense changes to the focused window |
2130 | + // eg: Do focusedWindow=A to focusedWindow=B instead of |
2131 | + // focusedWindow=A to focusedWindow=null to focusedWindow=B |
2132 | + if (focused) { |
2133 | + Q_ASSERT(m_newlyFocusedWindow == nullptr); |
2134 | + m_focusedWindowChanged = true; |
2135 | + m_newlyFocusedWindow = window; |
2136 | + } else if (m_focusedWindow == window) { |
2137 | + m_focusedWindowChanged = true; |
2138 | + } else { |
2139 | + // don't clear the focused window if you were not there in the first place |
2140 | + // happens when a filled window gets replaced with an empty one (no surface) as the focused window. |
2141 | + } |
2142 | + } |
2143 | + }); |
2144 | + |
2145 | + connect(window, &Window::closeRequested, this, [this, window]() { |
2146 | + if (!window->surface()) { |
2147 | + // do things ourselves as miral doesn't know about this window |
2148 | + int id = window->id(); |
2149 | + int index = indexForId(id); |
2150 | + bool focusOther = false; |
2151 | + Q_ASSERT(index >= 0); |
2152 | + if (window->focused()) { |
2153 | + focusOther = true; |
2154 | + } |
2155 | + m_windowModel[index].application->close(); |
2156 | + if (focusOther) { |
2157 | + activateTopMostWindowWithoutId(id); |
2158 | + } |
2159 | + } |
2160 | + }); |
2161 | + |
2162 | + connect(window, &Window::emptyWindowActivated, this, [this, window]() { |
2163 | + activateEmptyWindow(window); |
2164 | + }); |
2165 | +} |
2166 | + |
2167 | +void TopLevelWindowModel::activateEmptyWindow(Window *window) |
2168 | +{ |
2169 | + Q_ASSERT(!window->surface()); |
2170 | + DEBUG_MSG << "(" << window << ")"; |
2171 | + |
2172 | + // miral doesn't know about empty windows (ie, windows that are not backed up by MirSurfaces) |
2173 | + // So we have to activate them ourselves (instead of asking SurfaceManager to do it for us). |
2174 | + |
2175 | + window->setFocused(true); |
2176 | + raiseId(window->id()); |
2177 | + Window *previousWindow = m_focusedWindow; |
2178 | + setFocusedWindow(window); |
2179 | + if (previousWindow && previousWindow->surface() && previousWindow->surface()->focused()) { |
2180 | + m_surfaceManager->activate(nullptr); |
2181 | + } |
2182 | +} |
2183 | + |
2184 | +void TopLevelWindowModel::connectSurface(unityapi::MirSurfaceInterface *surface) |
2185 | +{ |
2186 | + connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, [this, surface](bool live){ |
2187 | + if (!live) { |
2188 | + onSurfaceDied(surface); |
2189 | + } |
2190 | + }); |
2191 | + connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); }); |
2192 | +} |
2193 | + |
2194 | +void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface) |
2195 | +{ |
2196 | + if (surface->type() == Mir::InputMethodType) { |
2197 | + removeInputMethodWindow(); |
2198 | + return; |
2199 | + } |
2200 | + |
2201 | + int i = indexOf(surface); |
2202 | + if (i == -1) { |
2203 | + return; |
2204 | + } |
2205 | + |
2206 | + auto application = m_windowModel[i].application; |
2207 | + |
2208 | + // can't be starting if it already has a surface |
2209 | + Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting); |
2210 | + |
2211 | + if (application->state() == unityapi::ApplicationInfoInterface::Running) { |
2212 | + m_windowModel[i].removeOnceSurfaceDestroyed = true; |
2213 | + } else { |
2214 | + // assume it got killed by the out-of-memory daemon. |
2215 | + // |
2216 | + // So leave entry in the model and only remove its surface, so shell can display a screenshot |
2217 | + // in its place. |
2218 | + m_windowModel[i].removeOnceSurfaceDestroyed = false; |
2219 | + } |
2220 | +} |
2221 | + |
2222 | +void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surface) |
2223 | +{ |
2224 | + int i = indexOf(surface); |
2225 | + if (i == -1) { |
2226 | + return; |
2227 | + } |
2228 | + |
2229 | + if (m_windowModel[i].removeOnceSurfaceDestroyed) { |
2230 | + removeAt(i); |
2231 | + } else { |
2232 | + auto window = m_windowModel[i].window; |
2233 | + window->setSurface(nullptr); |
2234 | + window->setFocused(false); |
2235 | + INFO_MSG << " Removed surface from entry. After: " << toString(); |
2236 | + } |
2237 | +} |
2238 | + |
2239 | +void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface) |
2240 | +{ |
2241 | + DEBUG_MSG << "(" << surface << ")"; |
2242 | + if (surface->type() == Mir::InputMethodType) { |
2243 | + int id = generateId(); |
2244 | + Window *qmlWindow = new Window(id, this); |
2245 | + connectWindow(qmlWindow); |
2246 | + qmlWindow->setSurface(surface); |
2247 | + setInputMethodWindow(qmlWindow); |
2248 | + } else { |
2249 | + auto application = m_applicationManager->findApplicationWithSurface(surface); |
2250 | + if (application) { |
2251 | + prependSurface(surface, application); |
2252 | + } else { |
2253 | + // Must be a prompt session. No need to do add it as a prompt surface is not top-level. |
2254 | + // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application. |
2255 | + // Still wrap it in a Window though, so that we keep focusedWindow() up to date. |
2256 | + int id = generateId(); |
2257 | + Window *promptWindow = new Window(id, this); |
2258 | + connectWindow(promptWindow); |
2259 | + promptWindow->setSurface(surface); |
2260 | + connect(surface, &QObject::destroyed, promptWindow, [=](){ |
2261 | + promptWindow->setSurface(nullptr); |
2262 | + promptWindow->deleteLater(); |
2263 | + }); |
2264 | + } |
2265 | + } |
2266 | + // TODO: handle surfaces that are neither top-level windows nor input method. eg: child dialogs, popups, menus |
2267 | +} |
2268 | + |
2269 | +void TopLevelWindowModel::removeAt(int index) |
2270 | +{ |
2271 | + if (m_modelState == IdleState) { |
2272 | + beginRemoveRows(QModelIndex(), index, index); |
2273 | + m_modelState = RemovingState; |
2274 | + } else { |
2275 | + Q_ASSERT(m_modelState == ResettingState); |
2276 | + // No point in signaling anything if we're resetting the whole model |
2277 | + } |
2278 | + |
2279 | + auto window = m_windowModel[index].window; |
2280 | + |
2281 | + window->setSurface(nullptr); |
2282 | + window->setFocused(false); |
2283 | + |
2284 | + m_windowModel.removeAt(index); |
2285 | + |
2286 | + if (m_modelState == RemovingState) { |
2287 | + endRemoveRows(); |
2288 | + Q_EMIT countChanged(); |
2289 | + Q_EMIT listChanged(); |
2290 | + m_modelState = IdleState; |
2291 | + } |
2292 | + |
2293 | + disconnect(window, 0, this, 0); |
2294 | + if (m_focusedWindow == window) { |
2295 | + setFocusedWindow(nullptr); |
2296 | + } |
2297 | + delete window; |
2298 | + |
2299 | + INFO_MSG << " after " << toString(); |
2300 | +} |
2301 | + |
2302 | +void TopLevelWindowModel::setInputMethodWindow(Window *window) |
2303 | +{ |
2304 | + if (m_inputMethodWindow) { |
2305 | + qWarning("Multiple Input Method Surfaces created, removing the old one!"); |
2306 | + delete m_inputMethodWindow; |
2307 | + } |
2308 | + m_inputMethodWindow = window; |
2309 | + Q_EMIT inputMethodSurfaceChanged(m_inputMethodWindow->surface()); |
2310 | +} |
2311 | + |
2312 | +void TopLevelWindowModel::removeInputMethodWindow() |
2313 | +{ |
2314 | + if (m_inputMethodWindow) { |
2315 | + delete m_inputMethodWindow; |
2316 | + m_inputMethodWindow = nullptr; |
2317 | + Q_EMIT inputMethodSurfaceChanged(nullptr); |
2318 | + } |
2319 | +} |
2320 | + |
2321 | +void TopLevelWindowModel::onSurfacesRaised(const QVector<unityapi::MirSurfaceInterface*> &surfaces) |
2322 | +{ |
2323 | + DEBUG_MSG << "(" << surfaces << ")"; |
2324 | + const int raiseCount = surfaces.size(); |
2325 | + for (int i = 0; i < raiseCount; i++) { |
2326 | + int fromIndex = findIndexOf(surfaces[i]); |
2327 | + if (fromIndex != -1) { |
2328 | + move(fromIndex, 0); |
2329 | + } |
2330 | + } |
2331 | +} |
2332 | + |
2333 | +int TopLevelWindowModel::rowCount(const QModelIndex &/*parent*/) const |
2334 | +{ |
2335 | + return m_windowModel.count(); |
2336 | +} |
2337 | + |
2338 | +QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const |
2339 | +{ |
2340 | + if (index.row() < 0 || index.row() >= m_windowModel.size()) |
2341 | + return QVariant(); |
2342 | + |
2343 | + if (role == WindowRole) { |
2344 | + Window *window = m_windowModel.at(index.row()).window; |
2345 | + return QVariant::fromValue(window); |
2346 | + } else if (role == ApplicationRole) { |
2347 | + return QVariant::fromValue(m_windowModel.at(index.row()).application); |
2348 | + } else { |
2349 | + return QVariant(); |
2350 | + } |
2351 | +} |
2352 | + |
2353 | +int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const |
2354 | +{ |
2355 | + for (int i=0; i<m_windowModel.count(); i++) { |
2356 | + if (m_windowModel[i].window->surface() == surface) { |
2357 | + return i; |
2358 | + } |
2359 | + } |
2360 | + return -1; |
2361 | +} |
2362 | + |
2363 | +int TopLevelWindowModel::generateId() |
2364 | +{ |
2365 | + int id = m_nextId; |
2366 | + m_nextId = nextFreeId(nextId(id), id); |
2367 | + return id; |
2368 | +} |
2369 | + |
2370 | +int TopLevelWindowModel::nextId(int id) const |
2371 | +{ |
2372 | + if (id == m_maxId) { |
2373 | + return id = 1; |
2374 | + } else { |
2375 | + return id + 1; |
2376 | + } |
2377 | +} |
2378 | + |
2379 | +int TopLevelWindowModel::nextFreeId(int candidateId, const int latestId) |
2380 | +{ |
2381 | + int firstCandidateId = candidateId; |
2382 | + |
2383 | + while (indexForId(candidateId) != -1 || candidateId == latestId) { |
2384 | + candidateId = nextId(candidateId); |
2385 | + |
2386 | + if (candidateId == firstCandidateId) { |
2387 | + qFatal("TopLevelWindowModel: run out of window ids."); |
2388 | + } |
2389 | + } |
2390 | + |
2391 | + return candidateId; |
2392 | +} |
2393 | + |
2394 | +QString TopLevelWindowModel::toString() |
2395 | +{ |
2396 | + QString str; |
2397 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
2398 | + auto item = m_windowModel.at(i); |
2399 | + |
2400 | + QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)") |
2401 | + .arg(QString::number(i), |
2402 | + item.application->appId(), |
2403 | + QString::number((qintptr)item.window->surface(), 16), |
2404 | + QString::number(item.window->id())); |
2405 | + |
2406 | + if (i > 0) { |
2407 | + str.append(","); |
2408 | + } |
2409 | + str.append(itemStr); |
2410 | + } |
2411 | + return str; |
2412 | +} |
2413 | + |
2414 | +int TopLevelWindowModel::indexOf(unityapi::MirSurfaceInterface *surface) |
2415 | +{ |
2416 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
2417 | + if (m_windowModel.at(i).window->surface() == surface) { |
2418 | + return i; |
2419 | + } |
2420 | + } |
2421 | + return -1; |
2422 | +} |
2423 | + |
2424 | +int TopLevelWindowModel::indexForId(int id) const |
2425 | +{ |
2426 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
2427 | + if (m_windowModel[i].window->id() == id) { |
2428 | + return i; |
2429 | + } |
2430 | + } |
2431 | + return -1; |
2432 | +} |
2433 | + |
2434 | +Window *TopLevelWindowModel::windowAt(int index) const |
2435 | +{ |
2436 | + if (index >=0 && index < m_windowModel.count()) { |
2437 | + return m_windowModel[index].window; |
2438 | + } else { |
2439 | + return nullptr; |
2440 | + } |
2441 | +} |
2442 | + |
2443 | +unityapi::MirSurfaceInterface *TopLevelWindowModel::surfaceAt(int index) const |
2444 | +{ |
2445 | + if (index >=0 && index < m_windowModel.count()) { |
2446 | + return m_windowModel[index].window->surface(); |
2447 | + } else { |
2448 | + return nullptr; |
2449 | + } |
2450 | +} |
2451 | + |
2452 | +unityapi::ApplicationInfoInterface *TopLevelWindowModel::applicationAt(int index) const |
2453 | +{ |
2454 | + if (index >=0 && index < m_windowModel.count()) { |
2455 | + return m_windowModel[index].application; |
2456 | + } else { |
2457 | + return nullptr; |
2458 | + } |
2459 | +} |
2460 | + |
2461 | +int TopLevelWindowModel::idAt(int index) const |
2462 | +{ |
2463 | + if (index >=0 && index < m_windowModel.count()) { |
2464 | + return m_windowModel[index].window->id(); |
2465 | + } else { |
2466 | + return 0; |
2467 | + } |
2468 | +} |
2469 | + |
2470 | +void TopLevelWindowModel::raiseId(int id) |
2471 | +{ |
2472 | + if (m_modelState == IdleState) { |
2473 | + DEBUG_MSG << "(id=" << id << ") - do it now."; |
2474 | + doRaiseId(id); |
2475 | + } else { |
2476 | + DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop."; |
2477 | + // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts |
2478 | + // if we perform yet another model change straight away. |
2479 | + // |
2480 | + // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says |
2481 | + // the index is definitely within bounds. |
2482 | + QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id)); |
2483 | + } |
2484 | +} |
2485 | + |
2486 | +void TopLevelWindowModel::doRaiseId(int id) |
2487 | +{ |
2488 | + int fromIndex = indexForId(id); |
2489 | + // can't raise something that doesn't exist or that it's already on top |
2490 | + if (fromIndex != -1 && fromIndex != 0) { |
2491 | + auto surface = m_windowModel[fromIndex].window->surface(); |
2492 | + if (surface) { |
2493 | + m_surfaceManager->raise(surface); |
2494 | + } else { |
2495 | + // move it ourselves. Since there's no mir::scene::Surface/miral::Window, there's nothing |
2496 | + // miral can do about it. |
2497 | + move(fromIndex, 0); |
2498 | + } |
2499 | + } |
2500 | +} |
2501 | + |
2502 | +void TopLevelWindowModel::setFocusedWindow(Window *window) |
2503 | +{ |
2504 | + if (window != m_focusedWindow) { |
2505 | + INFO_MSG << "(" << window << ")"; |
2506 | + |
2507 | + Window* previousWindow = m_focusedWindow; |
2508 | + |
2509 | + m_focusedWindow = window; |
2510 | + Q_EMIT focusedWindowChanged(m_focusedWindow); |
2511 | + |
2512 | + if (previousWindow && previousWindow->focused() && !previousWindow->surface()) { |
2513 | + // do it ourselves. miral doesn't know about this window |
2514 | + previousWindow->setFocused(false); |
2515 | + } |
2516 | + } |
2517 | +} |
2518 | + |
2519 | +unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const |
2520 | +{ |
2521 | + return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr; |
2522 | +} |
2523 | + |
2524 | +Window* TopLevelWindowModel::focusedWindow() const |
2525 | +{ |
2526 | + return m_focusedWindow; |
2527 | +} |
2528 | + |
2529 | +void TopLevelWindowModel::move(int from, int to) |
2530 | +{ |
2531 | + if (from == to) return; |
2532 | + DEBUG_MSG << " from=" << from << " to=" << to; |
2533 | + |
2534 | + if (from >= 0 && from < m_windowModel.size() && to >= 0 && to < m_windowModel.size()) { |
2535 | + QModelIndex parent; |
2536 | + /* When moving an item down, the destination index needs to be incremented |
2537 | + by one, as explained in the documentation: |
2538 | + http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */ |
2539 | + |
2540 | + Q_ASSERT(m_modelState == IdleState); |
2541 | + m_modelState = MovingState; |
2542 | + |
2543 | + beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0)); |
2544 | +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) |
2545 | + const auto &window = m_windowModel.takeAt(from); |
2546 | + m_windowModel.insert(to, window); |
2547 | +#else |
2548 | + m_windowModel.move(from, to); |
2549 | +#endif |
2550 | + endMoveRows(); |
2551 | + |
2552 | + Q_EMIT listChanged(); |
2553 | + m_modelState = IdleState; |
2554 | + |
2555 | + INFO_MSG << " after " << toString(); |
2556 | + } |
2557 | +} |
2558 | +void TopLevelWindowModel::onModificationsStarted() |
2559 | +{ |
2560 | +} |
2561 | + |
2562 | +void TopLevelWindowModel::onModificationsEnded() |
2563 | +{ |
2564 | + if (m_focusedWindowChanged) { |
2565 | + setFocusedWindow(m_newlyFocusedWindow); |
2566 | + } |
2567 | + // reset |
2568 | + m_focusedWindowChanged = false; |
2569 | + m_newlyFocusedWindow = nullptr; |
2570 | +} |
2571 | + |
2572 | +void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId) |
2573 | +{ |
2574 | + DEBUG_MSG << "(" << forbiddenId << ")"; |
2575 | + |
2576 | + for (int i = 0; i < m_windowModel.count(); ++i) { |
2577 | + Window *window = m_windowModel[i].window; |
2578 | + if (window->id() != forbiddenId) { |
2579 | + window->activate(); |
2580 | + } |
2581 | + } |
2582 | +} |
2583 | |
2584 | === added file 'plugins/WindowManager/TopLevelWindowModel.h' |
2585 | --- plugins/WindowManager/TopLevelWindowModel.h 1970-01-01 00:00:00 +0000 |
2586 | +++ plugins/WindowManager/TopLevelWindowModel.h 2016-12-12 16:49:02 +0000 |
2587 | @@ -0,0 +1,260 @@ |
2588 | +/* |
2589 | + * Copyright (C) 2016 Canonical, Ltd. |
2590 | + * |
2591 | + * This program is free software: you can redistribute it and/or modify it under |
2592 | + * the terms of the GNU Lesser General Public License version 3, as published by |
2593 | + * the Free Software Foundation. |
2594 | + * |
2595 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
2596 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
2597 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2598 | + * Lesser General Public License for more details. |
2599 | + * |
2600 | + * You should have received a copy of the GNU Lesser General Public License |
2601 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2602 | + */ |
2603 | + |
2604 | +#ifndef TOPLEVELWINDOWMODEL_H |
2605 | +#define TOPLEVELWINDOWMODEL_H |
2606 | + |
2607 | +#include <QAbstractListModel> |
2608 | +#include <QLoggingCategory> |
2609 | + |
2610 | +Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL) |
2611 | + |
2612 | +class Window; |
2613 | + |
2614 | +namespace unity { |
2615 | + namespace shell { |
2616 | + namespace application { |
2617 | + class ApplicationInfoInterface; |
2618 | + class ApplicationManagerInterface; |
2619 | + class MirSurfaceInterface; |
2620 | + class SurfaceManagerInterface; |
2621 | + } |
2622 | + } |
2623 | +} |
2624 | + |
2625 | +/** |
2626 | + * @brief A model of top-level surfaces |
2627 | + * |
2628 | + * It's an abstraction of top-level application windows. |
2629 | + * |
2630 | + * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is |
2631 | + * still starting up. A shell should then display a splash screen or saved screenshot of the application |
2632 | + * until its surface comes up. |
2633 | + * |
2634 | + * As applications can have multiple surfaces and you can also have entries without surfaces at all, |
2635 | + * the only way to unambiguously refer to an entry in this model is through its id. |
2636 | + */ |
2637 | +class TopLevelWindowModel : public QAbstractListModel |
2638 | +{ |
2639 | + Q_OBJECT |
2640 | + |
2641 | + /** |
2642 | + * @brief Number of top-level surfaces in this model |
2643 | + * |
2644 | + * This is the same as rowCount, added in order to keep compatibility with QML ListModels. |
2645 | + */ |
2646 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
2647 | + |
2648 | + /** |
2649 | + * @brief The input method surface, if any |
2650 | + * |
2651 | + * The surface of a onscreen keyboard (akak "virtual keyboard") would be kept here and not in the model itself. |
2652 | + */ |
2653 | + Q_PROPERTY(unity::shell::application::MirSurfaceInterface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged) |
2654 | + |
2655 | + /** |
2656 | + * @brief The currently focused window, if any |
2657 | + */ |
2658 | + Q_PROPERTY(Window* focusedWindow READ focusedWindow NOTIFY focusedWindowChanged) |
2659 | + |
2660 | + Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager |
2661 | + READ surfaceManager |
2662 | + WRITE setSurfaceManager |
2663 | + NOTIFY surfaceManagerChanged) |
2664 | + |
2665 | + Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager |
2666 | + READ applicationManager |
2667 | + WRITE setApplicationManager |
2668 | + NOTIFY applicationManagerChanged) |
2669 | + |
2670 | + /** |
2671 | + The id to be used on the next entry created |
2672 | + Useful for tests |
2673 | + */ |
2674 | + Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged) |
2675 | + |
2676 | +public: |
2677 | + /** |
2678 | + * @brief The Roles supported by the model |
2679 | + * |
2680 | + * WindowRole - A Window. |
2681 | + * ApplicationRole - An ApplicationInfoInterface |
2682 | + */ |
2683 | + enum Roles { |
2684 | + WindowRole = Qt::UserRole, |
2685 | + ApplicationRole = Qt::UserRole + 1, |
2686 | + }; |
2687 | + |
2688 | + TopLevelWindowModel(); |
2689 | + |
2690 | + // From QAbstractItemModel |
2691 | + int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
2692 | + QVariant data(const QModelIndex& index, int role) const override; |
2693 | + QHash<int, QByteArray> roleNames() const override { |
2694 | + QHash<int, QByteArray> roleNames { {WindowRole, "window"}, |
2695 | + {ApplicationRole, "application"} }; |
2696 | + return roleNames; |
2697 | + } |
2698 | + |
2699 | + // Own API |
2700 | + |
2701 | + unity::shell::application::MirSurfaceInterface* inputMethodSurface() const; |
2702 | + Window* focusedWindow() const; |
2703 | + |
2704 | + unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; } |
2705 | + void setApplicationManager(unity::shell::application::ApplicationManagerInterface*); |
2706 | + |
2707 | + unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; } |
2708 | + void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*); |
2709 | + |
2710 | + int nextId() const { return m_nextId; } |
2711 | + |
2712 | +public: |
2713 | + /** |
2714 | + * @brief Returns the surface at the given index |
2715 | + * |
2716 | + * It will be a nullptr if the application is still starting up and thus hasn't yet created |
2717 | + * and drawn into a surface. |
2718 | + * |
2719 | + * Same as windowAt(index).surface() |
2720 | + */ |
2721 | + Q_INVOKABLE unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const; |
2722 | + |
2723 | + /** |
2724 | + * @brief Returns the window at the given index |
2725 | + * |
2726 | + * Will always be valid |
2727 | + */ |
2728 | + Q_INVOKABLE Window *windowAt(int index) const; |
2729 | + |
2730 | + /** |
2731 | + * @brief Returns the application at the given index |
2732 | + */ |
2733 | + Q_INVOKABLE unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const; |
2734 | + |
2735 | + /** |
2736 | + * @brief Returns the unique id of the element at the given index |
2737 | + */ |
2738 | + Q_INVOKABLE int idAt(int index) const; |
2739 | + |
2740 | + /** |
2741 | + * @brief Returns the index where the row with the given id is located |
2742 | + * |
2743 | + * Returns -1 if there's no row with the given id. |
2744 | + */ |
2745 | + Q_INVOKABLE int indexForId(int id) const; |
2746 | + |
2747 | + /** |
2748 | + * @brief Raises the row with the given id to the top of the window stack (index == count-1) |
2749 | + */ |
2750 | + Q_INVOKABLE void raiseId(int id); |
2751 | + |
2752 | +Q_SIGNALS: |
2753 | + void countChanged(); |
2754 | + void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface); |
2755 | + void focusedWindowChanged(Window *focusedWindow); |
2756 | + void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*); |
2757 | + void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*); |
2758 | + |
2759 | + /** |
2760 | + * @brief Emitted when the list changes |
2761 | + * |
2762 | + * Emitted when model gains an element, loses an element or when elements exchange positions. |
2763 | + */ |
2764 | + void listChanged(); |
2765 | + |
2766 | + void nextIdChanged(); |
2767 | + |
2768 | +private Q_SLOTS: |
2769 | + void onSurfaceCreated(unity::shell::application::MirSurfaceInterface *surface); |
2770 | + void onSurfacesRaised(const QVector<unity::shell::application::MirSurfaceInterface*> &surfaces); |
2771 | + |
2772 | + void onModificationsStarted(); |
2773 | + void onModificationsEnded(); |
2774 | + |
2775 | +private: |
2776 | + void doRaiseId(int id); |
2777 | + int generateId(); |
2778 | + int nextFreeId(int candidateId, const int latestId); |
2779 | + int nextId(int id) const; |
2780 | + QString toString(); |
2781 | + int indexOf(unity::shell::application::MirSurfaceInterface *surface); |
2782 | + |
2783 | + void setInputMethodWindow(Window *window); |
2784 | + void setFocusedWindow(Window *window); |
2785 | + void removeInputMethodWindow(); |
2786 | + int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const; |
2787 | + void removeAt(int index); |
2788 | + |
2789 | + void addApplication(unity::shell::application::ApplicationInfoInterface *application); |
2790 | + void removeApplication(unity::shell::application::ApplicationInfoInterface *application); |
2791 | + |
2792 | + void prependPlaceholder(unity::shell::application::ApplicationInfoInterface *application); |
2793 | + void prependSurface(unity::shell::application::MirSurfaceInterface *surface, |
2794 | + unity::shell::application::ApplicationInfoInterface *application); |
2795 | + void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface, |
2796 | + unity::shell::application::ApplicationInfoInterface *application); |
2797 | + |
2798 | + void connectWindow(Window *window); |
2799 | + void connectSurface(unity::shell::application::MirSurfaceInterface *surface); |
2800 | + |
2801 | + void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface); |
2802 | + void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface); |
2803 | + |
2804 | + void move(int from, int to); |
2805 | + |
2806 | + void activateEmptyWindow(Window *window); |
2807 | + |
2808 | + void activateTopMostWindowWithoutId(int forbiddenId); |
2809 | + |
2810 | + struct ModelEntry { |
2811 | + ModelEntry() {} |
2812 | + ModelEntry(Window *window, |
2813 | + unity::shell::application::ApplicationInfoInterface *application) |
2814 | + : window(window), application(application) {} |
2815 | + Window *window{nullptr}; |
2816 | + unity::shell::application::ApplicationInfoInterface *application{nullptr}; |
2817 | + bool removeOnceSurfaceDestroyed{false}; |
2818 | + }; |
2819 | + |
2820 | + QVector<ModelEntry> m_windowModel; |
2821 | + Window* m_inputMethodWindow{nullptr}; |
2822 | + Window* m_focusedWindow{nullptr}; |
2823 | + |
2824 | + int m_nextId{1}; |
2825 | + // Just something big enough that we don't risk running out of unused id numbers. |
2826 | + // Not sure if QML int type supports something close to std::numeric_limits<int>::max() and |
2827 | + // there's no reason to try out its limits. |
2828 | + static const int m_maxId{1000000}; |
2829 | + |
2830 | + unity::shell::application::ApplicationManagerInterface* m_applicationManager{nullptr}; |
2831 | + unity::shell::application::SurfaceManagerInterface *m_surfaceManager{nullptr}; |
2832 | + |
2833 | + enum ModelState { |
2834 | + IdleState, |
2835 | + InsertingState, |
2836 | + RemovingState, |
2837 | + MovingState, |
2838 | + ResettingState |
2839 | + }; |
2840 | + ModelState m_modelState{IdleState}; |
2841 | + |
2842 | + // Valid between modificationsStarted and modificationsEnded |
2843 | + bool m_focusedWindowChanged{false}; |
2844 | + Window *m_newlyFocusedWindow{nullptr}; |
2845 | +}; |
2846 | + |
2847 | +#endif // TOPLEVELWINDOWMODEL_H |
2848 | |
2849 | === added file 'plugins/WindowManager/Window.cpp' |
2850 | --- plugins/WindowManager/Window.cpp 1970-01-01 00:00:00 +0000 |
2851 | +++ plugins/WindowManager/Window.cpp 2016-12-12 16:49:02 +0000 |
2852 | @@ -0,0 +1,237 @@ |
2853 | +/* |
2854 | + * Copyright (C) 2016 Canonical, Ltd. |
2855 | + * |
2856 | + * This program is free software; you can redistribute it and/or modify |
2857 | + * it under the terms of the GNU General Public License as published by |
2858 | + * the Free Software Foundation; version 3. |
2859 | + * |
2860 | + * This program is distributed in the hope that it will be useful, |
2861 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2862 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2863 | + * GNU General Public License for more details. |
2864 | + * |
2865 | + * You should have received a copy of the GNU General Public License |
2866 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2867 | + */ |
2868 | + |
2869 | +#include "Window.h" |
2870 | + |
2871 | +// unity-api |
2872 | +#include <unity/shell/application/MirSurfaceInterface.h> |
2873 | + |
2874 | +#include <QQmlEngine> |
2875 | +#include <QTextStream> |
2876 | + |
2877 | +namespace unityapi = unity::shell::application; |
2878 | + |
2879 | +Q_LOGGING_CATEGORY(UNITY_WINDOW, "unity.window", QtWarningMsg) |
2880 | + |
2881 | +#define DEBUG_MSG qCDebug(UNITY_WINDOW).nospace() << qPrintable(toString()) << "::" << __func__ |
2882 | + |
2883 | +Window::Window(int id, QObject *parent) |
2884 | + : QObject(parent) |
2885 | + , m_id(id) |
2886 | +{ |
2887 | + DEBUG_MSG << "()"; |
2888 | + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); |
2889 | +} |
2890 | + |
2891 | +Window::~Window() |
2892 | +{ |
2893 | + DEBUG_MSG << "()"; |
2894 | +} |
2895 | + |
2896 | +QPoint Window::position() const |
2897 | +{ |
2898 | + return m_position; |
2899 | +} |
2900 | + |
2901 | +QPoint Window::requestedPosition() const |
2902 | +{ |
2903 | + return m_requestedPosition; |
2904 | +} |
2905 | + |
2906 | +void Window::setRequestedPosition(const QPoint &value) |
2907 | +{ |
2908 | + m_positionRequested = true; |
2909 | + if (value != m_requestedPosition) { |
2910 | + m_requestedPosition = value; |
2911 | + Q_EMIT requestedPositionChanged(m_requestedPosition); |
2912 | + if (m_surface) { |
2913 | + m_surface->setRequestedPosition(value); |
2914 | + } else { |
2915 | + // fake-miral: always comply |
2916 | + m_position = m_requestedPosition; |
2917 | + Q_EMIT positionChanged(m_position); |
2918 | + } |
2919 | + } |
2920 | +} |
2921 | + |
2922 | +Mir::State Window::state() const |
2923 | +{ |
2924 | + return m_state; |
2925 | +} |
2926 | + |
2927 | +bool Window::focused() const |
2928 | +{ |
2929 | + return m_focused; |
2930 | +} |
2931 | + |
2932 | +bool Window::confinesMousePointer() const |
2933 | +{ |
2934 | + if (m_surface) { |
2935 | + return m_surface->confinesMousePointer(); |
2936 | + } else { |
2937 | + return false; |
2938 | + } |
2939 | +} |
2940 | + |
2941 | +int Window::id() const |
2942 | +{ |
2943 | + return m_id; |
2944 | +} |
2945 | + |
2946 | +unityapi::MirSurfaceInterface* Window::surface() const |
2947 | +{ |
2948 | + return m_surface; |
2949 | +} |
2950 | + |
2951 | +void Window::requestState(Mir::State state) |
2952 | +{ |
2953 | + m_stateRequested = true; |
2954 | + if (m_surface) { |
2955 | + m_surface->requestState(state); |
2956 | + } else if (m_state != state) { |
2957 | + m_state = state; |
2958 | + Q_EMIT stateChanged(m_state); |
2959 | + } |
2960 | +} |
2961 | + |
2962 | +void Window::close() |
2963 | +{ |
2964 | + if (m_surface) { |
2965 | + m_surface->close(); |
2966 | + } else { |
2967 | + Q_EMIT closeRequested(); |
2968 | + } |
2969 | +} |
2970 | + |
2971 | +void Window::activate() |
2972 | +{ |
2973 | + DEBUG_MSG << "()"; |
2974 | + if (m_surface) { |
2975 | + m_surface->activate(); |
2976 | + } else { |
2977 | + Q_EMIT emptyWindowActivated(); |
2978 | + } |
2979 | +} |
2980 | + |
2981 | +void Window::setSurface(unityapi::MirSurfaceInterface *surface) |
2982 | +{ |
2983 | + DEBUG_MSG << "(" << surface << ")"; |
2984 | + if (m_surface) { |
2985 | + disconnect(m_surface, 0, this, 0); |
2986 | + } |
2987 | + |
2988 | + m_surface = surface; |
2989 | + |
2990 | + if (m_surface) { |
2991 | + connect(surface, &unityapi::MirSurfaceInterface::focusRequested, this, [this]() { |
2992 | + Q_EMIT focusRequested(); |
2993 | + }); |
2994 | + |
2995 | + connect(surface, &unityapi::MirSurfaceInterface::closeRequested, this, &Window::closeRequested); |
2996 | + |
2997 | + connect(surface, &unityapi::MirSurfaceInterface::positionChanged, this, [this]() { |
2998 | + updatePosition(); |
2999 | + }); |
3000 | + |
3001 | + connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [this]() { |
3002 | + updateState(); |
3003 | + }); |
3004 | + |
3005 | + connect(surface, &unityapi::MirSurfaceInterface::focusedChanged, this, [this]() { |
3006 | + updateFocused(); |
3007 | + }); |
3008 | + |
3009 | + // bring it up to speed |
3010 | + if (m_positionRequested) { |
3011 | + m_surface->setRequestedPosition(m_requestedPosition); |
3012 | + } |
3013 | + if (m_stateRequested && m_surface->state() == Mir::RestoredState) { |
3014 | + m_surface->requestState(m_state); |
3015 | + } |
3016 | + |
3017 | + // and sync with surface |
3018 | + updatePosition(); |
3019 | + updateState(); |
3020 | + updateFocused(); |
3021 | + } |
3022 | + |
3023 | + Q_EMIT surfaceChanged(surface); |
3024 | +} |
3025 | + |
3026 | +void Window::updatePosition() |
3027 | +{ |
3028 | + if (m_surface->position() != m_position) { |
3029 | + m_position = m_surface->position(); |
3030 | + Q_EMIT positionChanged(m_position); |
3031 | + } |
3032 | +} |
3033 | + |
3034 | +void Window::updateState() |
3035 | +{ |
3036 | + if (m_surface->state() != m_state) { |
3037 | + m_state = m_surface->state(); |
3038 | + Q_EMIT stateChanged(m_state); |
3039 | + } |
3040 | +} |
3041 | + |
3042 | +void Window::updateFocused() |
3043 | +{ |
3044 | + if (m_surface->focused() != m_focused) { |
3045 | + m_focused = m_surface->focused(); |
3046 | + Q_EMIT focusedChanged(m_focused); |
3047 | + } |
3048 | +} |
3049 | + |
3050 | +void Window::setFocused(bool value) |
3051 | +{ |
3052 | + if (value != m_focused) { |
3053 | + DEBUG_MSG << "(" << value << ")"; |
3054 | + m_focused = value; |
3055 | + Q_EMIT focusedChanged(m_focused); |
3056 | + // when we have a surface we get focus changes from updateFocused() instead |
3057 | + Q_ASSERT(!m_surface); |
3058 | + } |
3059 | +} |
3060 | + |
3061 | +QString Window::toString() const |
3062 | +{ |
3063 | + QString result; |
3064 | + { |
3065 | + QTextStream stream(&result); |
3066 | + stream << "Window["<<(void*)this<<", id="<<id()<<", "; |
3067 | + if (surface()) { |
3068 | + stream << "MirSurface["<<(void*)surface()<<",\""<<surface()->name()<<"\"]"; |
3069 | + } else { |
3070 | + stream << "null"; |
3071 | + } |
3072 | + stream << "]"; |
3073 | + } |
3074 | + return result; |
3075 | +} |
3076 | + |
3077 | +QDebug operator<<(QDebug dbg, const Window *window) |
3078 | +{ |
3079 | + QDebugStateSaver saver(dbg); |
3080 | + dbg.nospace(); |
3081 | + |
3082 | + if (window) { |
3083 | + dbg << qPrintable(window->toString()); |
3084 | + } else { |
3085 | + dbg << (void*)(window); |
3086 | + } |
3087 | + |
3088 | + return dbg; |
3089 | +} |
3090 | |
3091 | === added file 'plugins/WindowManager/Window.h' |
3092 | --- plugins/WindowManager/Window.h 1970-01-01 00:00:00 +0000 |
3093 | +++ plugins/WindowManager/Window.h 2016-12-12 16:49:02 +0000 |
3094 | @@ -0,0 +1,161 @@ |
3095 | +/* |
3096 | + * Copyright (C) 2016 Canonical, Ltd. |
3097 | + * |
3098 | + * This program is free software; you can redistribute it and/or modify |
3099 | + * it under the terms of the GNU General Public License as published by |
3100 | + * the Free Software Foundation; version 3. |
3101 | + * |
3102 | + * This program is distributed in the hope that it will be useful, |
3103 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3104 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3105 | + * GNU General Public License for more details. |
3106 | + * |
3107 | + * You should have received a copy of the GNU General Public License |
3108 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3109 | + */ |
3110 | + |
3111 | +#ifndef UNITY_WINDOW_H |
3112 | +#define UNITY_WINDOW_H |
3113 | + |
3114 | +#include <QLoggingCategory> |
3115 | +#include <QObject> |
3116 | +#include <QPoint> |
3117 | + |
3118 | +// Unity API |
3119 | +#include <unity/shell/application/Mir.h> |
3120 | + |
3121 | +namespace unity { |
3122 | + namespace shell { |
3123 | + namespace application { |
3124 | + class MirSurfaceInterface; |
3125 | + } |
3126 | + } |
3127 | +} |
3128 | + |
3129 | + |
3130 | +Q_DECLARE_LOGGING_CATEGORY(UNITY_WINDOW) |
3131 | + |
3132 | +/** |
3133 | + @brief A slightly higher concept than MirSurface |
3134 | + |
3135 | + A Window exists before its MirSurface gets created (for splashscreen purposes) |
3136 | + and might also hang around after the backing surface is gone (In case the application |
3137 | + was killed to free up memory, as it should still remain in the window list since the user |
3138 | + did not explicitly close it). |
3139 | + */ |
3140 | +class Window : public QObject |
3141 | +{ |
3142 | + Q_OBJECT |
3143 | + |
3144 | + /** |
3145 | + * @brief Position of the current surface buffer, in pixels. |
3146 | + */ |
3147 | + Q_PROPERTY(QPoint position READ position NOTIFY positionChanged) |
3148 | + |
3149 | + /** |
3150 | + * @brief Requested position of the current surface buffer, in pixels. |
3151 | + */ |
3152 | + Q_PROPERTY(QPoint requestedPosition READ requestedPosition WRITE setRequestedPosition NOTIFY requestedPositionChanged) |
3153 | + |
3154 | + /** |
3155 | + * @brief State of the surface |
3156 | + */ |
3157 | + Q_PROPERTY(Mir::State state READ state NOTIFY stateChanged) |
3158 | + |
3159 | + /** |
3160 | + * @brief Whether the surface is focused |
3161 | + * |
3162 | + * It will be true if this surface is MirFocusControllerInterface::focusedSurface |
3163 | + */ |
3164 | + Q_PROPERTY(bool focused READ focused NOTIFY focusedChanged) |
3165 | + |
3166 | + /** |
3167 | + * @brief Whether the surface wants to confine the mouse pointer within its boundaries |
3168 | + * |
3169 | + * If true, the surface doesn't want the mouse pointer to leave its boundaries while it's focused. |
3170 | + */ |
3171 | + Q_PROPERTY(bool confinesMousePointer READ confinesMousePointer NOTIFY confinesMousePointerChanged) |
3172 | + |
3173 | + /** |
3174 | + * @brief A unique identifier for this window. |
3175 | + * Useful for telling windows apart in a list model as they get moved around |
3176 | + */ |
3177 | + Q_PROPERTY(int id READ id CONSTANT) |
3178 | + |
3179 | + /** |
3180 | + * @brief Surface backing up this window |
3181 | + * It might be null if a surface hasn't been created yet (application is starting up) or if |
3182 | + * the corresponding application has been killed (but can still get restarted to continue from |
3183 | + * where it left) |
3184 | + */ |
3185 | + Q_PROPERTY(unity::shell::application::MirSurfaceInterface* surface READ surface NOTIFY surfaceChanged) |
3186 | + |
3187 | +public: |
3188 | + Window(int id, QObject *parent = nullptr); |
3189 | + virtual ~Window(); |
3190 | + QPoint position() const; |
3191 | + QPoint requestedPosition() const; |
3192 | + void setRequestedPosition(const QPoint &); |
3193 | + Mir::State state() const; |
3194 | + bool focused() const; |
3195 | + bool confinesMousePointer() const; |
3196 | + int id() const; |
3197 | + unity::shell::application::MirSurfaceInterface* surface() const; |
3198 | + |
3199 | + void setSurface(unity::shell::application::MirSurfaceInterface *surface); |
3200 | + void setFocused(bool value); |
3201 | + |
3202 | + QString toString() const; |
3203 | + |
3204 | +public Q_SLOTS: |
3205 | + /** |
3206 | + * @brief Requests a change to the specified state |
3207 | + */ |
3208 | + void requestState(Mir::State state); |
3209 | + |
3210 | + /** |
3211 | + * @brief Sends a close request |
3212 | + * |
3213 | + */ |
3214 | + void close(); |
3215 | + |
3216 | + /** |
3217 | + * @brief Focuses and raises the window |
3218 | + */ |
3219 | + void activate(); |
3220 | + |
3221 | +Q_SIGNALS: |
3222 | + void closeRequested(); |
3223 | + void emptyWindowActivated(); |
3224 | + |
3225 | + void positionChanged(QPoint position); |
3226 | + void requestedPositionChanged(QPoint position); |
3227 | + void stateChanged(Mir::State value); |
3228 | + void focusedChanged(bool value); |
3229 | + void confinesMousePointerChanged(bool value); |
3230 | + void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface); |
3231 | + |
3232 | + /** |
3233 | + * @brief Emitted when focus for this window is requested by an external party |
3234 | + */ |
3235 | + void focusRequested(); |
3236 | + |
3237 | +private: |
3238 | + void updatePosition(); |
3239 | + void updateState(); |
3240 | + void updateFocused(); |
3241 | + |
3242 | + QPoint m_position; |
3243 | + QPoint m_requestedPosition; |
3244 | + bool m_positionRequested{false}; |
3245 | + bool m_focused{false}; |
3246 | + int m_id; |
3247 | + Mir::State m_state{Mir::RestoredState}; |
3248 | + bool m_stateRequested{false}; |
3249 | + unity::shell::application::MirSurfaceInterface *m_surface{nullptr}; |
3250 | +}; |
3251 | + |
3252 | +QDebug operator<<(QDebug dbg, const Window *window); |
3253 | + |
3254 | +Q_DECLARE_METATYPE(Window*) |
3255 | +#endif // UNITY_WINDOW_H |
3256 | |
3257 | === modified file 'plugins/WindowManager/WindowManagerPlugin.cpp' |
3258 | --- plugins/WindowManager/WindowManagerPlugin.cpp 2016-04-04 13:37:49 +0000 |
3259 | +++ plugins/WindowManager/WindowManagerPlugin.cpp 2016-12-12 16:49:02 +0000 |
3260 | @@ -16,13 +16,16 @@ |
3261 | |
3262 | #include "WindowManagerPlugin.h" |
3263 | |
3264 | -#include "TopLevelSurfaceList.h" |
3265 | +#include "TopLevelWindowModel.h" |
3266 | +#include "Window.h" |
3267 | |
3268 | #include <QtQml> |
3269 | |
3270 | void WindowManagerPlugin::registerTypes(const char *uri) |
3271 | { |
3272 | - qmlRegisterType<TopLevelSurfaceList>(uri, 0, 1, "TopLevelSurfaceList"); |
3273 | + qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel"); |
3274 | + |
3275 | + qRegisterMetaType<Window*>("Window*"); |
3276 | |
3277 | qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*"); |
3278 | } |
3279 | |
3280 | === modified file 'po/unity8.pot' |
3281 | --- po/unity8.pot 2016-11-29 09:43:33 +0000 |
3282 | +++ po/unity8.pot 2016-12-12 16:49:02 +0000 |
3283 | @@ -8,7 +8,7 @@ |
3284 | msgstr "" |
3285 | "Project-Id-Version: unity8\n" |
3286 | "Report-Msgid-Bugs-To: \n" |
3287 | -"POT-Creation-Date: 2016-11-29 09:43+0000\n" |
3288 | +"POT-Creation-Date: 2016-12-07 13:49+0000\n" |
3289 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
3290 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
3291 | "Language-Team: LANGUAGE <LL@li.org>\n" |
3292 | @@ -23,7 +23,7 @@ |
3293 | msgstr "" |
3294 | |
3295 | #: plugins/Unity/Launcher/launcheritem.cpp:50 |
3296 | -#: plugins/Unity/Launcher/launcheritem.cpp:109 |
3297 | +#: plugins/Unity/Launcher/launcheritem.cpp:122 |
3298 | msgid "Pin shortcut" |
3299 | msgstr "" |
3300 | |
3301 | @@ -31,7 +31,7 @@ |
3302 | msgid "Quit" |
3303 | msgstr "" |
3304 | |
3305 | -#: plugins/Unity/Launcher/launcheritem.cpp:109 |
3306 | +#: plugins/Unity/Launcher/launcheritem.cpp:122 |
3307 | msgid "Unpin shortcut" |
3308 | msgstr "" |
3309 | |
3310 | @@ -175,87 +175,95 @@ |
3311 | msgid "Same as clicking on a launcher icon." |
3312 | msgstr "" |
3313 | |
3314 | -#: qml/Components/KeyboardShortcutsOverlay.qml:181 |
3315 | +#: qml/Components/KeyboardShortcutsOverlay.qml:177 |
3316 | +msgid "Super + A" |
3317 | +msgstr "" |
3318 | + |
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:/