Merge lp:~nick-dedekind/unity8/application.menus into lp:unity8
- application.menus
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~nick-dedekind/unity8/application.menus |
Merge into: | lp:unity8 |
Prerequisite: | lp:~nick-dedekind/unity8/menu.registrar |
Diff against target: |
6365 lines (+3524/-865) 60 files modified
debian/control (+3/-3) debian/unity8.install (+1/-0) plugins/GlobalShortcut/globalshortcut.cpp (+12/-4) plugins/GlobalShortcut/globalshortcut.h (+5/-0) plugins/GlobalShortcut/globalshortcutregistry.cpp (+14/-4) plugins/Unity/Indicators/sharedunitymenumodel.cpp (+4/-0) qml/ApplicationMenus/ApplicationMenuItemFactory.qml (+135/-0) qml/ApplicationMenus/MenuBar.qml (+304/-0) qml/ApplicationMenus/MenuItem.qml (+150/-0) qml/ApplicationMenus/MenuNavigator.qml (+78/-0) qml/ApplicationMenus/MenuPopup.qml (+361/-0) qml/ApplicationMenus/RegisteredApplicationMenuModel.qml (+56/-0) qml/CMakeLists.txt (+1/-0) qml/Components/DragHandle.qml (+1/-0) qml/Components/PanelState/PanelState.qml (+6/-3) qml/Components/ScrollCalculator.qml (+21/-2) qml/Components/WindowControlButtons.qml (+21/-0) qml/OrientedShell.qml (+6/-0) qml/Panel/FakePanelMenu.qml (+26/-0) qml/Panel/Indicators/IndicatorItem.qml (+2/-1) qml/Panel/Indicators/IndicatorMenuItemFactory.qml (+7/-10) qml/Panel/MenuContent.qml (+7/-10) qml/Panel/Panel.qml (+314/-110) qml/Panel/PanelBar.qml (+70/-37) qml/Panel/PanelItemRow.qml (+54/-103) qml/Panel/PanelMenu.qml (+31/-19) qml/Panel/PanelMenuPage.qml (+194/-157) qml/Rotation/RotationStates.qml (+2/-2) qml/Shell.qml (+21/-11) qml/Stage/DecoratedWindow.qml (+77/-35) qml/Stage/Stage.qml (+17/-3) qml/Stage/WindowDecoration.qml (+80/-19) src/main.cpp (+2/-2) tests/mocks/QMenuModel/unitymenumodel.cpp (+47/-6) tests/mocks/QMenuModel/unitymenumodel.h (+3/-1) tests/mocks/Unity/Application/SurfaceManager.cpp (+5/-2) tests/mocks/Unity/Application/SurfaceManager.h (+3/-1) tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cpp (+5/-0) tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.h (+3/-0) tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js (+4/-4) tests/qmltests/ApplicationMenuDataLoader.qml (+75/-0) tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+183/-0) tests/qmltests/ApplicationMenus/tst_MenuPopup.qml (+207/-0) tests/qmltests/CMakeLists.txt (+11/-7) tests/qmltests/Panel/Indicators/tst_IndicatorItem.qml (+1/-1) tests/qmltests/Panel/Indicators/tst_IndicatorMenuItemFactory.qml (+2/-2) tests/qmltests/Panel/PanelTest.qml (+0/-5) tests/qmltests/Panel/tst_MenuContent.qml (+14/-2) tests/qmltests/Panel/tst_Panel.qml (+386/-109) tests/qmltests/Panel/tst_PanelBar.qml (+25/-34) tests/qmltests/Panel/tst_PanelItemRow.qml (+36/-32) tests/qmltests/Panel/tst_PanelMenu.qml (+32/-14) tests/qmltests/Panel/tst_PanelMenuPage.qml (+60/-54) tests/qmltests/Stage/tst_DecoratedWindow.qml (+40/-5) tests/qmltests/Stage/tst_DesktopStage.qml (+9/-45) tests/qmltests/Stage/tst_PhoneStage.qml (+5/-0) tests/qmltests/Stage/tst_TabletStage.qml (+6/-0) tests/qmltests/Stage/tst_WindowDecoration.qml (+242/-0) tests/qmltests/tst_Shell.qml (+34/-5) tests/qmltests/tst_ShellWithPin.qml (+3/-1) |
To merge this branch: | bzr merge lp:~nick-dedekind/unity8/application.menus |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Michael Zanetti (community) | Approve | ||
Review via email: mp+307143@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-01-10.
Commit message
Application menus
Description of the change
Prereq-archive: ppa:ci-
Application menus
* Are there any related MPs required for this MP to build/function as expected? Please list.
https:/
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?
N/A
* If you changed the UI, has there been a design review?
Not yet
Daniel d'Andrada (dandrader) wrote : | # |
Omer Akram (om26er) wrote : | # |
Does this also implement application menus in titlebar (as in unity7) if not, are there plans to implement that as well ?
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2463
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: 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:/
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:2464
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:/
Nick Dedekind (nick-dedekind) wrote : | # |
> Does this also implement application menus in titlebar (as in unity7) if not,
> are there plans to implement that as well ?
In windowed mode, the menus are in title bars for "restored windows" and the panel for "maximized windows".
In touch mode, they are in the panel, exposed by a left side of panel swipe (on app title).
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2474
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2474
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: 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:/
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:2476
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: 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:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michael Zanetti (mzanetti) wrote : | # |
looks like the switch for windowed mode has disappeared from the system indicator
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2477
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: 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:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michael Zanetti (mzanetti) wrote : | # |
The reason why it doesn't work for me is that I tried with a confined click app. Seems we need to open up the D-Bus interface in apparmor.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2478
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: 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:/
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:2483
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: 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:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2484
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2486
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: 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:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2487
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: 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:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2488
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: 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:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Michael Zanetti (mzanetti) wrote : | # |
Some inline comments so far.
Michael Zanetti (mzanetti) wrote : | # |
A question on the renaming of the "Indicators" to "Panel". Makes sense in general. Is it now that the "Panel" means the complete top panel thing, from edge to edge, "Indicators" mean everything that is painted on the indicator area and comes out when you pull down, and "Menu" everything that is painted on the left side as menus and comes out when pulling down there?
Michael Zanetti (mzanetti) wrote : | # |
Some notes on testing (mainly as a checklist for testing after reading the code changes):
* Check mouse hover states in panel. Lukas had a hard time to get this right, this changes it. Verify for corner cases.
* Check shortcuts. GlobalShortcut component is changed to copy the event. Looks like it makes sense, needs to be tested for other cases than just menus if all is still as expected.
* Test how it behaves on a 40/50 gu wide screen.
* Test window decoration transition in right edge gesture when going to spread... There's funky magic which has been shifted around a bit.
Nick Dedekind (nick-dedekind) wrote : | # |
Commented on some of yours. Will address the rest.
Nick Dedekind (nick-dedekind) wrote : | # |
>
> A question on the renaming of the "Indicators" to "Panel". Makes sense in
> general. Is it now that the "Panel" means the complete top panel thing, from
> edge to edge, "Indicators" mean everything that is painted on the indicator
> area and comes out when you pull down, and "Menu" everything that is painted
> on the left side as menus and comes out when pulling down there?
Panel is the bar at the top of the screen, which includes "indicators" (on right) and the "applicationMenu" (on left)
Nick Dedekind (nick-dedekind) wrote : | # |
>
> A question on the renaming of the "Indicators" to "Panel". Makes sense in
> general. Is it now that the "Panel" means the complete top panel thing, from
> edge to edge, "Indicators" mean everything that is painted on the indicator
> area and comes out when you pull down, and "Menu" everything that is painted
> on the left side as menus and comes out when pulling down there?
Panel is the bar at the top of the screen, which includes "indicators" (on right) and the "applicationMenu" (on left)
Nick Dedekind (nick-dedekind) wrote : | # |
Other comments addressed
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2491
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:/
Albert Astals Cid (aacid) wrote : | # |
Text conflict in qml/Shell.qml
Text conflict in qml/Stage/
Text conflict in qml/Stage/
3 conflicts encountered.
Nick Dedekind (nick-dedekind) wrote : | # |
> The reason why it doesn't work for me is that I tried with a confined click
> app. Seems we need to open up the D-Bus interface in apparmor.
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2494
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:/
Nick Dedekind (nick-dedekind) wrote : | # |
We're no longer blocking on the confined app issue. The dbus access will be opened up by snap interfaces now rather than apparmor-easyprof
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2494
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:/
Nick Dedekind (nick-dedekind) wrote : | # |
There is one small bug that i don't want to block on regarding submenu mnemonics. All mnemonics are Alt prefixed; but this can't be fixed without SDK changes.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2496
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:2497
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:/
Michael Zanetti (mzanetti) wrote : | # |
thanks for the fixes, one more inline question
Nick Dedekind (nick-dedekind) : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2498
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:2500
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:2501
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:2501
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:/
Michael Zanetti (mzanetti) wrote : | # |
Testing this on a large screen in staged mode with mouse, it doesn't seem to reveal the appmenus by mouse hover and even with mouse, it reveals the touch based mode of the menus.
The size of the ui elements in menus and indicators should not be based on the usage mode, but rather on the interaction that started the journey. This means, even in staged mode, if the screen space allows it, it should be possible to see the menus on mouse hover and access the mouse version of them.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2502
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:/
Michael Zanetti (mzanetti) wrote : | # |
tested the latest commits. appmenus in staged mode but with mouse now work as expected, however, in windowed mode we now have LIM and gobal menus at the same time... I guess they should be disabled when there is a LIM and the app is not fullscreen...
Michael Zanetti (mzanetti) wrote : | # |
also, staged mode, fullscreen, mouse usage, if you hower the panel with the mouse, the menu is shown correctly, however, if you move the cursor to the very left, there are a few pixels where the hower on the menu goes away and the app title appears again, clicking with the mouse now, will open the touch version of the menu.
Michael Zanetti (mzanetti) wrote : | # |
windowed mode, application with menu windowed, press Alt+F to open the File menu, now try to use the arrow keys to navigate, doesn't work
Michael Zanetti (mzanetti) wrote : | # |
Pressing quickly alt+f doesn't seem to work either, one needs to hold Alt for some 200ms and only when the menu then appears, the f key would open the menu. alt+f should react immediately though, like it does in unity7
Michael Zanetti (mzanetti) wrote : | # |
I cannot get it to work on the M10 but I'm not 100% sure if it's this branch's fault or not. Using ubuntu-app-test the app will start correctly, but I cannot see any menus exposed. trying to run qmlscene directly with --desktop_
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2504
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:/
Albert Astals Cid (aacid) wrote : | # |
Text conflict in debian/control
Text conflict in tests/mocks/
Text conflict in tests/mocks/
Text conflict in tests/qmltests/
Text conflict in tests/qmltests/
Text conflict in tests/qmltests/
Text conflict in tests/qmltests/
Text conflict in tests/qmltests/
8 conflicts encountered.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2506
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:2507
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:2500
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2508
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:2509
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: 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 : | # |
PASSED: Continuous integration, rev:2510
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:/
Michael Zanetti (mzanetti) wrote : | # |
Menus are painted outside of the window decoration if the window is too small.
Michael Zanetti (mzanetti) wrote : | # |
Also, there is a focus issue. If you open an app with menus, then press alt+f and try to navigate with the arrow keys, it doesn't work. Only after you hover the menu with mouse or otherwise force focus on the menu it starts working.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2512
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:/
Michael Zanetti (mzanetti) wrote : | # |
seems the test failure on zesty is related to the changed code. perhaps some flakyness we need to stabilize?
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2514
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 : | # |
PASSED: Continuous integration, rev:2515
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:/
Michael Zanetti (mzanetti) wrote : | # |
Ok, seems to work rather well now. The last test revealed this one: http://
But I have not really an idea how to deal with this atm... seems a combination of things and also requires design input so lets leave that for later.
ci is happy too now
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2518
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 : | # |
PASSED: Continuous integration, rev:2518
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:2520
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 : | # |
FAILED: Continuous integration, rev:2520
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2518
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
Unmerged revisions
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2017-01-09 15:26:30 +0000 |
3 | +++ debian/control 2017-01-09 15:26:31 +0000 |
4 | @@ -29,7 +29,7 @@ |
5 | libpam0g-dev, |
6 | libpay2-dev, |
7 | libpulse-dev, |
8 | - libqmenumodel-dev (>= 0.2.10), |
9 | + libqmenumodel-dev (>= 0.2.11), |
10 | libqt5svg5-dev, |
11 | libqt5xmlpatterns5-dev, |
12 | libsystemsettings-dev, |
13 | @@ -83,7 +83,7 @@ |
14 | |
15 | Package: indicators-client |
16 | Architecture: any |
17 | -Depends: qmenumodel-qml (>= 0.2.10), |
18 | +Depends: qmenumodel-qml (>= 0.2.11), |
19 | qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030), |
20 | unity8 (= ${binary:Version}), |
21 | ${misc:Depends}, |
22 | @@ -122,7 +122,7 @@ |
23 | gsettings-desktop-schemas, |
24 | libcap2-bin, |
25 | libglib2.0-bin, |
26 | - qmenumodel-qml (>= 0.2.10), |
27 | + qmenumodel-qml (>= 0.2.11), |
28 | qml-module-biometryd, |
29 | qml-module-qt-labs-folderlistmodel, |
30 | qml-module-qt-labs-settings, |
31 | |
32 | === modified file 'debian/unity8.install' |
33 | --- debian/unity8.install 2016-09-12 17:05:43 +0000 |
34 | +++ debian/unity8.install 2017-01-09 15:26:31 +0000 |
35 | @@ -8,6 +8,7 @@ |
36 | usr/bin/unity8-dash |
37 | usr/share/applications/unity8-dash.desktop |
38 | usr/share/applications/unity8.desktop |
39 | +usr/share/unity8/ApplicationMenus |
40 | usr/share/unity8/Greeter |
41 | usr/share/unity8/Launcher |
42 | usr/share/unity8/Panel |
43 | |
44 | === modified file 'plugins/GlobalShortcut/globalshortcut.cpp' |
45 | --- plugins/GlobalShortcut/globalshortcut.cpp 2015-09-18 11:03:48 +0000 |
46 | +++ plugins/GlobalShortcut/globalshortcut.cpp 2017-01-09 15:26:31 +0000 |
47 | @@ -63,10 +63,18 @@ |
48 | |
49 | void GlobalShortcut::keyPressEvent(QKeyEvent * event) |
50 | { |
51 | - Q_UNUSED(event) |
52 | - if (m_active) { |
53 | - Q_EMIT triggered(m_shortcut.toString()); |
54 | - } |
55 | + if (!m_active) return; |
56 | + |
57 | + event->accept(); |
58 | + Q_EMIT triggered(m_shortcut.toString()); |
59 | +} |
60 | + |
61 | +void GlobalShortcut::keyReleaseEvent(QKeyEvent * event) |
62 | +{ |
63 | + if (!m_active) return; |
64 | + |
65 | + event->accept(); |
66 | + Q_EMIT released(m_shortcut.toString()); |
67 | } |
68 | |
69 | void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window) |
70 | |
71 | === modified file 'plugins/GlobalShortcut/globalshortcut.h' |
72 | --- plugins/GlobalShortcut/globalshortcut.h 2015-08-24 15:39:53 +0000 |
73 | +++ plugins/GlobalShortcut/globalshortcut.h 2017-01-09 15:26:31 +0000 |
74 | @@ -55,6 +55,7 @@ |
75 | protected: |
76 | void componentComplete() override; |
77 | void keyPressEvent(QKeyEvent * event) override; |
78 | + void keyReleaseEvent(QKeyEvent * event) override; |
79 | |
80 | Q_SIGNALS: |
81 | void shortcutChanged(const QVariant &shortcut); |
82 | @@ -62,6 +63,10 @@ |
83 | * Emitted when a global keypress of @p shortcut is detected |
84 | */ |
85 | void triggered(const QString &shortcut); |
86 | + /** |
87 | + * Emitted when a global keypress of @p shortcut is released |
88 | + */ |
89 | + void released(const QString &shortcut); |
90 | void activeChanged(bool active); |
91 | |
92 | private Q_SLOTS: |
93 | |
94 | === modified file 'plugins/GlobalShortcut/globalshortcutregistry.cpp' |
95 | --- plugins/GlobalShortcut/globalshortcutregistry.cpp 2016-02-19 15:28:38 +0000 |
96 | +++ plugins/GlobalShortcut/globalshortcutregistry.cpp 2017-01-09 15:26:31 +0000 |
97 | @@ -73,20 +73,30 @@ |
98 | Q_ASSERT(m_filteredWindow); |
99 | Q_ASSERT(obj == static_cast<QObject*>(m_filteredWindow.data())); |
100 | |
101 | - if (event->type() == QEvent::KeyPress) { |
102 | + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { |
103 | + |
104 | QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); |
105 | + |
106 | + // Make a copy of the event so we don't alter it for passing on. |
107 | + QKeyEvent eCopy(keyEvent->type(), |
108 | + keyEvent->key(), |
109 | + keyEvent->modifiers(), |
110 | + keyEvent->text(), |
111 | + keyEvent->isAutoRepeat(), |
112 | + keyEvent->count()); |
113 | + eCopy.ignore(); |
114 | + |
115 | int seq = keyEvent->key() + keyEvent->modifiers(); |
116 | if (m_shortcuts.contains(seq)) { |
117 | const auto shortcuts = m_shortcuts.value(seq); |
118 | Q_FOREACH(const auto &shortcut, shortcuts) { |
119 | if (shortcut) { |
120 | - qApp->sendEvent(shortcut, keyEvent); |
121 | - event->accept(); |
122 | + qApp->sendEvent(shortcut, &eCopy); |
123 | } |
124 | } |
125 | } |
126 | |
127 | - return event->isAccepted(); |
128 | + return eCopy.isAccepted(); |
129 | } |
130 | |
131 | return QObject::eventFilter(obj, event); |
132 | |
133 | === modified file 'plugins/Unity/Indicators/sharedunitymenumodel.cpp' |
134 | --- plugins/Unity/Indicators/sharedunitymenumodel.cpp 2014-10-17 14:55:35 +0000 |
135 | +++ plugins/Unity/Indicators/sharedunitymenumodel.cpp 2017-01-09 15:26:31 +0000 |
136 | @@ -81,12 +81,16 @@ |
137 | } |
138 | } else { |
139 | QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_menuObjectPath); |
140 | + |
141 | if (model != m_model) { |
142 | if (model->busName() != m_busName) model->setBusName(m_busName); |
143 | if (model->actions() != m_actions) model->setActions(m_actions); |
144 | |
145 | m_model = model; |
146 | Q_EMIT modelChanged(); |
147 | + } else if (m_model) { |
148 | + if (m_model->busName() != m_busName) m_model->setBusName(m_busName); |
149 | + if (m_model->actions() != m_actions) m_model->setActions(m_actions); |
150 | } |
151 | } |
152 | } |
153 | |
154 | === added directory 'qml/ApplicationMenus' |
155 | === added file 'qml/ApplicationMenus/ApplicationMenuItemFactory.qml' |
156 | --- qml/ApplicationMenus/ApplicationMenuItemFactory.qml 1970-01-01 00:00:00 +0000 |
157 | +++ qml/ApplicationMenus/ApplicationMenuItemFactory.qml 2017-01-09 15:26:31 +0000 |
158 | @@ -0,0 +1,135 @@ |
159 | +/* |
160 | + * Copyright 2016 Canonical Ltd. |
161 | + * |
162 | + * This program is free software; you can redistribute it and/or modify |
163 | + * it under the terms of the GNU Lesser General Public License as published by |
164 | + * the Free Software Foundation; version 3. |
165 | + * |
166 | + * This program is distributed in the hope that it will be useful, |
167 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
168 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
169 | + * GNU Lesser General Public License for more details. |
170 | + * |
171 | + * You should have received a copy of the GNU Lesser General Public License |
172 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
173 | + */ |
174 | + |
175 | +import QtQuick 2.4 |
176 | +import Ubuntu.Settings.Menus 0.1 as Menus |
177 | +import Ubuntu.Settings.Components 0.1 |
178 | +import QMenuModel 0.1 |
179 | +import Utils 0.1 as Utils |
180 | +import Ubuntu.Components 1.3 |
181 | + |
182 | +Object { |
183 | + id: menuFactory |
184 | + |
185 | + property string context |
186 | + property var rootModel: null |
187 | + property var menuModel: null |
188 | + |
189 | + Component { |
190 | + id: applicationMenu; |
191 | + |
192 | + ListItem { |
193 | + property QtObject menuData: null |
194 | + property int menuIndex: -1 |
195 | + |
196 | + height: layout.height |
197 | + enabled: menuData && menuData.sensitive || false |
198 | + divider.visible: false |
199 | + |
200 | + onClicked: { |
201 | + menuModel.activate(menuIndex); |
202 | + } |
203 | + |
204 | + Action { |
205 | + id: action |
206 | + text: menuData.label.replace("_", "&") |
207 | + } |
208 | + |
209 | + ListItemLayout { |
210 | + id: layout |
211 | + title.text: action.text |
212 | + |
213 | + Icon { |
214 | + source: menuData && menuData.icon || "" |
215 | + SlotsLayout.position: SlotsLayout.Leading |
216 | + height: units.gu(3) |
217 | + } |
218 | + |
219 | + Label { |
220 | + text: menuData.shortcut |
221 | + visible: menuData.shortcut && QuickUtils.keyboardAttached |
222 | + SlotsLayout.position: SlotsLayout.Trailing |
223 | + color: enabled ? theme.palette.normal.backgroundSecondaryText : |
224 | + theme.palette.disabled.backgroundSecondaryText |
225 | + } |
226 | + } |
227 | + } |
228 | + } |
229 | + |
230 | + Component { |
231 | + id: submenu |
232 | + |
233 | + ListItem { |
234 | + property QtObject menuData: null |
235 | + property int menuIndex: -1 |
236 | + |
237 | + height: layout.height |
238 | + enabled: menuData && menuData.sensitive || false |
239 | + divider.visible: false |
240 | + |
241 | + onClicked: { |
242 | + menuModel.activate(menuIndex); |
243 | + } |
244 | + |
245 | + Action { |
246 | + id: action |
247 | + text: menuData.label.replace("_", "&") |
248 | + } |
249 | + |
250 | + ListItemLayout { |
251 | + id: layout |
252 | + title.text: action.text |
253 | + |
254 | + Icon { |
255 | + source: menuData && menuData.icon || "" |
256 | + SlotsLayout.position: SlotsLayout.Leading |
257 | + height: units.gu(3) |
258 | + } |
259 | + |
260 | + Icon { |
261 | + name: "toolkit_chevron-ltr_1gu" |
262 | + SlotsLayout.position: SlotsLayout.Trailing |
263 | + width: units.gu(2) |
264 | + color: enabled ? theme.palette.normal.backgroundSecondaryText : |
265 | + theme.palette.disabled.backgroundSecondaryText |
266 | + } |
267 | + } |
268 | + } |
269 | + } |
270 | + |
271 | + Component { |
272 | + id: applicationMenuSeparator; |
273 | + |
274 | + Menus.SeparatorMenu { |
275 | + objectName: "separatorMenu" |
276 | + } |
277 | + } |
278 | + |
279 | + function load(modelData) { |
280 | + if (modelData.isSeparator) { |
281 | + return applicationMenuSeparator; |
282 | + } |
283 | + if (modelData.isRadio) { |
284 | + } |
285 | + if (modelData.isCheck) { |
286 | + |
287 | + } |
288 | + if (modelData.hasSubmenu) { |
289 | + return submenu; |
290 | + } |
291 | + return applicationMenu; |
292 | + } |
293 | +} |
294 | |
295 | === added file 'qml/ApplicationMenus/MenuBar.qml' |
296 | --- qml/ApplicationMenus/MenuBar.qml 1970-01-01 00:00:00 +0000 |
297 | +++ qml/ApplicationMenus/MenuBar.qml 2017-01-09 15:26:31 +0000 |
298 | @@ -0,0 +1,304 @@ |
299 | +/* |
300 | + * Copyright 2016 Canonical Ltd. |
301 | + * |
302 | + * This program is free software; you can redistribute it and/or modify |
303 | + * it under the terms of the GNU Lesser General Public License as published by |
304 | + * the Free Software Foundation; version 3. |
305 | + * |
306 | + * This program is distributed in the hope that it will be useful, |
307 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
308 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
309 | + * GNU Lesser General Public License for more details. |
310 | + * |
311 | + * You should have received a copy of the GNU Lesser General Public License |
312 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
313 | + */ |
314 | + |
315 | +import QtQuick 2.4 |
316 | +import QtQuick.Layouts 1.1 |
317 | +import Utils 0.1 |
318 | +import Ubuntu.Components 1.3 |
319 | +import GlobalShortcut 1.0 |
320 | + |
321 | +Item { |
322 | + id: root |
323 | + objectName: "menuBar" |
324 | + |
325 | + property alias unityMenuModel: rowRepeater.model |
326 | + |
327 | + readonly property bool valid: rowRepeater.count > 0 |
328 | + |
329 | + property bool enableKeyFilter: false |
330 | + |
331 | + readonly property bool showRequested: d.longAltPressed || d.currentItem != null |
332 | + |
333 | + implicitWidth: row.width |
334 | + height: parent.height |
335 | + |
336 | + function select(index) { |
337 | + d.select(index); |
338 | + } |
339 | + |
340 | + function dismiss() { |
341 | + d.dismissAll(); |
342 | + } |
343 | + |
344 | + GlobalShortcut { |
345 | + shortcut: Qt.Key_Alt|Qt.AltModifier |
346 | + active: enableKeyFilter |
347 | + onTriggered: d.startShortcutTimer() |
348 | + onReleased: d.stopSHortcutTimer() |
349 | + } |
350 | + // On an actual keyboard, the AltModifier is not supplied on release. |
351 | + GlobalShortcut { |
352 | + shortcut: Qt.Key_Alt |
353 | + active: enableKeyFilter |
354 | + onTriggered: d.startShortcutTimer() |
355 | + onReleased: d.stopSHortcutTimer() |
356 | + } |
357 | + |
358 | + InverseMouseArea { |
359 | + acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton |
360 | + anchors.fill: parent |
361 | + enabled: d.currentItem != null |
362 | + onPressed: d.dismissAll() |
363 | + } |
364 | + |
365 | + Item { |
366 | + id: clippingItem |
367 | + |
368 | + height: root.height |
369 | + width: root.width |
370 | + clip: true |
371 | + |
372 | + Row { |
373 | + id: row |
374 | + spacing: units.gu(2) |
375 | + height: parent.height |
376 | + |
377 | + ActionContext { |
378 | + id: menuBarContext |
379 | + objectName: "barContext" |
380 | + active: !d.currentItem && enableKeyFilter |
381 | + } |
382 | + |
383 | + Repeater { |
384 | + id: rowRepeater |
385 | + |
386 | + Item { |
387 | + id: visualItem |
388 | + objectName: root.objectName + "-item" + __ownIndex |
389 | + |
390 | + readonly property int __ownIndex: index |
391 | + property Item __popup: null; |
392 | + property bool popupVisible: __popup && __popup.visible |
393 | + |
394 | + implicitWidth: column.implicitWidth |
395 | + implicitHeight: row.height |
396 | + enabled: model.sensitive |
397 | + |
398 | + function show() { |
399 | + if (!__popup) { |
400 | + __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" }); |
401 | + // force the current item to be the newly popped up menu |
402 | + } else { |
403 | + __popup.show(); |
404 | + } |
405 | + d.currentItem = visualItem; |
406 | + } |
407 | + function hide() { |
408 | + if (__popup) { |
409 | + __popup.hide(); |
410 | + |
411 | + if (d.currentItem === visualItem) { |
412 | + d.currentItem = null; |
413 | + } |
414 | + } |
415 | + } |
416 | + function dismiss() { |
417 | + if (__popup) { |
418 | + __popup.destroy(); |
419 | + __popup = null; |
420 | + |
421 | + if (d.currentItem === visualItem) { |
422 | + d.currentItem = null; |
423 | + } |
424 | + } |
425 | + } |
426 | + |
427 | + Connections { |
428 | + target: d |
429 | + onDismissAll: visualItem.dismiss() |
430 | + } |
431 | + |
432 | + Component { |
433 | + id: menuComponent |
434 | + MenuPopup { |
435 | + x: visualItem.x - units.gu(1) |
436 | + anchors.top: parent.bottom |
437 | + unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex) |
438 | + |
439 | + Component.onCompleted: reset(); |
440 | + } |
441 | + } |
442 | + |
443 | + RowLayout { |
444 | + id: column |
445 | + spacing: units.gu(1) |
446 | + anchors { |
447 | + centerIn: parent |
448 | + } |
449 | + |
450 | + Icon { |
451 | + Layout.preferredWidth: units.gu(2) |
452 | + Layout.preferredHeight: units.gu(2) |
453 | + Layout.alignment: Qt.AlignVCenter |
454 | + |
455 | + visible: model.icon || false |
456 | + source: model.icon || "" |
457 | + } |
458 | + |
459 | + ActionItem { |
460 | + id: actionItem |
461 | + width: _title.width |
462 | + height: _title.height |
463 | + |
464 | + action: Action { |
465 | + // FIXME - SDK Action:text modifies menu text with html underline for mnemonic |
466 | + text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") |
467 | + |
468 | + onTriggered: { |
469 | + visualItem.show(); |
470 | + } |
471 | + } |
472 | + |
473 | + Label { |
474 | + id: _title |
475 | + text: actionItem.text |
476 | + horizontalAlignment: Text.AlignLeft |
477 | + color: enabled ? "white" : "#5d5d5d" |
478 | + } |
479 | + } |
480 | + } |
481 | + } // Item ( delegate ) |
482 | + } // Repeater |
483 | + } // Row |
484 | + |
485 | + MouseArea { |
486 | + anchors.fill: parent |
487 | + hoverEnabled: d.currentItem |
488 | + |
489 | + onEntered: { |
490 | + if (d.currentItem) { |
491 | + updateCurrentItemFromPosition(Qt.point(mouseX, mouseY)) |
492 | + } |
493 | + } |
494 | + onPositionChanged: { |
495 | + if (d.currentItem) { |
496 | + updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
497 | + } |
498 | + } |
499 | + onClicked: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
500 | + |
501 | + function updateCurrentItemFromPosition(point) { |
502 | + var pos = mapToItem(row, point.x, point.y); |
503 | + |
504 | + if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) { |
505 | + d.hoveredItem = row.childAt(pos.x, pos.y); |
506 | + if (!d.hoveredItem || !d.hoveredItem.enabled) |
507 | + return false; |
508 | + if (d.currentItem != d.hoveredItem) { |
509 | + d.currentItem = d.hoveredItem; |
510 | + } |
511 | + } |
512 | + return true; |
513 | + } |
514 | + } |
515 | + |
516 | + Rectangle { |
517 | + id: underline |
518 | + anchors { |
519 | + bottom: row.bottom |
520 | + } |
521 | + x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0 |
522 | + width: d.currentItem ? d.currentItem.width + units.gu(2) : 0 |
523 | + height: units.dp(4) |
524 | + color: UbuntuColors.orange |
525 | + visible: d.currentItem |
526 | + } |
527 | + } |
528 | + |
529 | + MenuNavigator { |
530 | + id: d |
531 | + objectName: "d" |
532 | + itemView: rowRepeater |
533 | + |
534 | + property Item currentItem: null |
535 | + property Item hoveredItem: null |
536 | + property Item prevCurrentItem: null |
537 | + |
538 | + readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1 |
539 | + |
540 | + property bool altPressed: false |
541 | + property bool longAltPressed: false |
542 | + |
543 | + signal dismissAll() |
544 | + |
545 | + onSelect: { |
546 | + var delegate = rowRepeater.itemAt(index); |
547 | + if (delegate) { |
548 | + d.currentItem = delegate; |
549 | + } |
550 | + } |
551 | + |
552 | + onCurrentItemChanged: { |
553 | + if (prevCurrentItem && prevCurrentItem != currentItem) { |
554 | + if (currentItem) { |
555 | + prevCurrentItem.hide(); |
556 | + } else { |
557 | + prevCurrentItem.dismiss(); |
558 | + } |
559 | + } |
560 | + |
561 | + if (currentItem) currentItem.show(); |
562 | + prevCurrentItem = currentItem; |
563 | + } |
564 | + |
565 | + function startShortcutTimer() { |
566 | + d.altPressed = true; |
567 | + menuBarShortcutTimer.start(); |
568 | + } |
569 | + |
570 | + function stopSHortcutTimer() { |
571 | + menuBarShortcutTimer.stop(); |
572 | + d.altPressed = false; |
573 | + d.longAltPressed = false; |
574 | + } |
575 | + } |
576 | + |
577 | + Timer { |
578 | + id: menuBarShortcutTimer |
579 | + interval: 200 |
580 | + repeat: false |
581 | + onTriggered: { |
582 | + d.longAltPressed = true; |
583 | + } |
584 | + } |
585 | + |
586 | + Keys.onEscapePressed: { |
587 | + d.dismissAll(); |
588 | + event.accepted = true; |
589 | + } |
590 | + |
591 | + Keys.onLeftPressed: { |
592 | + if (d.currentItem) { |
593 | + d.selectPrevious(d.currentIndex); |
594 | + } |
595 | + } |
596 | + |
597 | + Keys.onRightPressed: { |
598 | + if (d.currentItem) { |
599 | + d.selectNext(d.currentIndex); |
600 | + } |
601 | + } |
602 | +} |
603 | |
604 | === added file 'qml/ApplicationMenus/MenuItem.qml' |
605 | --- qml/ApplicationMenus/MenuItem.qml 1970-01-01 00:00:00 +0000 |
606 | +++ qml/ApplicationMenus/MenuItem.qml 2017-01-09 15:26:31 +0000 |
607 | @@ -0,0 +1,150 @@ |
608 | +/* |
609 | + * Copyright 2016 Canonical Ltd. |
610 | + * |
611 | + * This program is free software; you can redistribute it and/or modify |
612 | + * it under the terms of the GNU Lesser General Public License as published by |
613 | + * the Free Software Foundation; version 3. |
614 | + * |
615 | + * This program is distributed in the hope that it will be useful, |
616 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
617 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
618 | + * GNU Lesser General Public License for more details. |
619 | + * |
620 | + * You should have received a copy of the GNU Lesser General Public License |
621 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
622 | + */ |
623 | + |
624 | +import QtQuick 2.4 |
625 | +import QtQuick.Layouts 1.1 |
626 | +import Ubuntu.Components 1.3 |
627 | + |
628 | +ActionItem { |
629 | + id: root |
630 | + implicitHeight: units.gu(5) |
631 | + implicitWidth: requiredWidth |
632 | + |
633 | + property var menuData: undefined |
634 | + |
635 | + readonly property real requiredWidth: { |
636 | + var val = 0; |
637 | + val += units.gu(1) + flagGutter.width; |
638 | + if (iconSource != "") { |
639 | + val += units.gu(1) + icon.width |
640 | + } |
641 | + val += units.gu(1) + title.contentWidth; |
642 | + if (hasSubmenu) { |
643 | + val += units.gu(1) + chevronIcon.width; |
644 | + } else if (shortcut != undefined) { |
645 | + val += units.gu(3) + shortcutLabel.contentWidth; |
646 | + } |
647 | + return val + units.gu(1); |
648 | + } |
649 | + |
650 | + readonly property bool hasSubmenu: menuData ? menuData.hasSubmenu : false |
651 | + readonly property bool _checked : action && action.checkable ? action.checked : false |
652 | + |
653 | + enabled: menuData ? menuData.sensitive : false |
654 | + |
655 | + action: Action { |
656 | + // FIXME - SDK Action:text modifies menu text with html underline for mnemonic |
657 | + text: menuData.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") |
658 | + checkable: menuData.isCheck || menuData.isRadio |
659 | + checked: menuData.isToggled |
660 | + } |
661 | + |
662 | + width: { |
663 | + if (!parent) return implicitWidth; |
664 | + if (parent.width > implicitWidth) return parent.width; |
665 | + return implicitWidth; |
666 | + } |
667 | + |
668 | + Keys.onRightPressed: { |
669 | + if (hasSubmenu) { |
670 | + root.trigger(); |
671 | + } else { |
672 | + event.accepted = false; |
673 | + } |
674 | + } |
675 | + Keys.onReturnPressed: { |
676 | + root.trigger(); |
677 | + } |
678 | + Keys.onEnterPressed: { |
679 | + root.trigger(); |
680 | + } |
681 | + |
682 | + RowLayout { |
683 | + id: row |
684 | + spacing: units.gu(1) |
685 | + anchors.left: parent.left |
686 | + anchors.right: parent.right |
687 | + anchors.leftMargin: units.gu(1) |
688 | + anchors.rightMargin: units.gu(1) |
689 | + anchors.verticalCenter: parent.verticalCenter |
690 | + |
691 | + Item { |
692 | + Layout.minimumWidth: units.gu(1.5) |
693 | + Layout.minimumHeight: units.gu(1.5) |
694 | + |
695 | + Icon { |
696 | + id: flagGutter |
697 | + width: units.gu(1.5) |
698 | + height: units.gu(1.5) |
699 | + visible: _checked |
700 | + name: "tick" |
701 | + } |
702 | + } |
703 | + |
704 | + Icon { |
705 | + id: icon |
706 | + width: units.gu(2) |
707 | + height: units.gu(2) |
708 | + |
709 | + visible: root.iconSource != "" || false |
710 | + source: root.iconSource || "" |
711 | + } |
712 | + |
713 | + RowLayout { |
714 | + spacing: units.gu(3) |
715 | + |
716 | + Label { |
717 | + id: title |
718 | + elide: Text.ElideNone |
719 | + wrapMode: Text.NoWrap |
720 | + clip: true |
721 | + Layout.fillWidth: true |
722 | + |
723 | + text: root.text ? root.text : "" |
724 | + } |
725 | + |
726 | + Label { |
727 | + id: shortcutLabel |
728 | + elide: Text.ElideNone |
729 | + wrapMode: Text.NoWrap |
730 | + clip: true |
731 | + color: enabled ? theme.palette.normal.backgroundSecondaryText : |
732 | + theme.palette.disabled.backgroundSecondaryText |
733 | + |
734 | + visible: menuData.shortcut != undefined && !root.hasSubmenu && QuickUtils.keyboardAttached |
735 | + text: menuData.shortcut ? menuData.shortcut : "" |
736 | + } |
737 | + } |
738 | + |
739 | + Icon { |
740 | + id: chevronIcon |
741 | + width: units.gu(2) |
742 | + height: units.gu(2) |
743 | + color: enabled ? theme.palette.normal.backgroundSecondaryText : |
744 | + theme.palette.disabled.backgroundSecondaryText |
745 | + |
746 | + visible: root.hasSubmenu |
747 | + name: "chevron" |
748 | + } |
749 | + } |
750 | + |
751 | + MouseArea { |
752 | + anchors.fill: parent |
753 | + onClicked: { |
754 | + root.trigger(action && action.checkable ? !action.checked : undefined); |
755 | + } |
756 | + } |
757 | +} |
758 | |
759 | === added file 'qml/ApplicationMenus/MenuNavigator.qml' |
760 | --- qml/ApplicationMenus/MenuNavigator.qml 1970-01-01 00:00:00 +0000 |
761 | +++ qml/ApplicationMenus/MenuNavigator.qml 2017-01-09 15:26:31 +0000 |
762 | @@ -0,0 +1,78 @@ |
763 | +/* |
764 | + * Copyright 2016 Canonical Ltd. |
765 | + * |
766 | + * This program is free software; you can redistribute it and/or modify |
767 | + * it under the terms of the GNU Lesser General Public License as published by |
768 | + * the Free Software Foundation; version 3. |
769 | + * |
770 | + * This program is distributed in the hope that it will be useful, |
771 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
772 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
773 | + * GNU Lesser General Public License for more details. |
774 | + * |
775 | + * You should have received a copy of the GNU Lesser General Public License |
776 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
777 | + */ |
778 | + |
779 | +import QtQuick 2.4 |
780 | + |
781 | +QtObject { |
782 | + property Item itemView: null |
783 | + |
784 | + signal select(int index) |
785 | + |
786 | + function selectNext(currentIndex) { |
787 | + var menu; |
788 | + var newIndex = 0; |
789 | + if (currentIndex === -1 && itemView.count > 0) { |
790 | + while (itemView.count > newIndex) { |
791 | + menu = itemView.itemAt(newIndex); |
792 | + if (!!menu["enabled"]) { |
793 | + select(newIndex); |
794 | + break; |
795 | + } |
796 | + newIndex++; |
797 | + } |
798 | + } else if (currentIndex !== -1 && itemView.count > 1) { |
799 | + var startIndex = (currentIndex + 1) % itemView.count; |
800 | + newIndex = startIndex; |
801 | + do { |
802 | + menu = itemView.itemAt(newIndex); |
803 | + if (!!menu["enabled"]) { |
804 | + select(newIndex); |
805 | + break; |
806 | + } |
807 | + newIndex = (newIndex + 1) % itemView.count; |
808 | + } while (newIndex !== startIndex) |
809 | + } |
810 | + } |
811 | + |
812 | + function selectPrevious(currentIndex) { |
813 | + var menu; |
814 | + var newIndex = itemView.count-1; |
815 | + if (currentIndex === -1 && itemView.count > 0) { |
816 | + while (itemView.count > newIndex) { |
817 | + menu = itemView.itemAt(newIndex); |
818 | + if (!!menu["enabled"]) { |
819 | + select(newIndex); |
820 | + break; |
821 | + } |
822 | + newIndex--; |
823 | + } |
824 | + } else if (currentIndex !== -1 && itemView.count > 1) { |
825 | + var startIndex = currentIndex - 1; |
826 | + newIndex = startIndex; |
827 | + do { |
828 | + if (newIndex < 0) { |
829 | + newIndex = itemView.count - 1; |
830 | + } |
831 | + menu = itemView.itemAt(newIndex); |
832 | + if (!!menu["enabled"]) { |
833 | + select(newIndex); |
834 | + break; |
835 | + } |
836 | + newIndex--; |
837 | + } while (newIndex !== startIndex) |
838 | + } |
839 | + } |
840 | +} |
841 | |
842 | === added file 'qml/ApplicationMenus/MenuPopup.qml' |
843 | --- qml/ApplicationMenus/MenuPopup.qml 1970-01-01 00:00:00 +0000 |
844 | +++ qml/ApplicationMenus/MenuPopup.qml 2017-01-09 15:26:31 +0000 |
845 | @@ -0,0 +1,361 @@ |
846 | +/* |
847 | + * Copyright 2016 Canonical Ltd. |
848 | + * |
849 | + * This program is free software; you can redistribute it and/or modify |
850 | + * it under the terms of the GNU Lesser General Public License as published by |
851 | + * the Free Software Foundation; version 3. |
852 | + * |
853 | + * This program is distributed in the hope that it will be useful, |
854 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
855 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
856 | + * GNU Lesser General Public License for more details. |
857 | + * |
858 | + * You should have received a copy of the GNU Lesser General Public License |
859 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
860 | + */ |
861 | + |
862 | +import QtQuick 2.4 |
863 | +import QtQuick.Layouts 1.1 |
864 | +import QtQuick.Window 2.2 |
865 | +import Ubuntu.Components 1.3 |
866 | +import Ubuntu.Components.ListItems 1.3 as ListItems |
867 | +import "../Components" |
868 | + |
869 | +UbuntuShape { |
870 | + id: root |
871 | + objectName: "menu" |
872 | + backgroundColor: theme.palette.normal.overlay |
873 | + |
874 | + property alias unityMenuModel: listView.model |
875 | + |
876 | + readonly property real __ajustedMinimumHeight: { |
877 | + if (listView.contentHeight > __minimumHeight) { |
878 | + return units.gu(30); |
879 | + } |
880 | + return Math.max(listView.contentHeight, units.gu(2)); |
881 | + } |
882 | + |
883 | + readonly property real __minimumWidth: units.gu(20) |
884 | + readonly property real __minimumHeight: units.gu(30) |
885 | + readonly property real __maximumWidth: Screen.width * 0.7 |
886 | + readonly property real __maximumHeight: Screen.height * 0.7 |
887 | + |
888 | + function show() { |
889 | + visible = true; |
890 | + focusScope.forceActiveFocus(); |
891 | + } |
892 | + |
893 | + function hide() { |
894 | + visible = false; |
895 | + d.currentItem = null; |
896 | + } |
897 | + |
898 | + function select(index) { |
899 | + d.select(index) |
900 | + } |
901 | + |
902 | + function reset() { |
903 | + d.currentItem = null; |
904 | + dismiss(); |
905 | + } |
906 | + |
907 | + function dismiss() { |
908 | + d.dismissAll(); |
909 | + } |
910 | + |
911 | + implicitWidth: container.width |
912 | + implicitHeight: MathUtils.clamp(listView.contentHeight, __ajustedMinimumHeight, __maximumHeight) |
913 | + |
914 | + MenuNavigator { |
915 | + id: d |
916 | + objectName: "d" |
917 | + itemView: listView |
918 | + |
919 | + property Item currentItem: null |
920 | + property Item hoveredItem: null |
921 | + readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1 |
922 | + |
923 | + signal dismissAll() |
924 | + |
925 | + onCurrentItemChanged: { |
926 | + if (currentItem) { |
927 | + currentItem.item.forceActiveFocus(); |
928 | + } else { |
929 | + hoveredItem = null; |
930 | + } |
931 | + } |
932 | + |
933 | + onSelect: { |
934 | + currentItem = listView.itemAt(index); |
935 | + } |
936 | + } |
937 | + |
938 | + Item { |
939 | + id: focusScope |
940 | + anchors.fill: parent |
941 | + focus: visible |
942 | + |
943 | + Keys.onUpPressed: d.selectPrevious(d.currentIndex) |
944 | + Keys.onDownPressed: d.selectNext(d.currentIndex) |
945 | + Keys.onRightPressed: { |
946 | + // Don't let right keypresses fall through if the current item has a visible popup. |
947 | + if (!d.currentItem || !d.currentItem.popup || !d.currentItem.popup.visible) { |
948 | + event.accepted = false; |
949 | + } |
950 | + } |
951 | + |
952 | + ColumnLayout { |
953 | + id: container |
954 | + objectName: "container" |
955 | + |
956 | + width: listView.contentWidth |
957 | + height: parent.height |
958 | + spacing: 0 |
959 | + |
960 | + // FIXME use ListView.header - tried but was flaky with positionViewAtIndex. |
961 | + Item { |
962 | + Layout.fillWidth: true; |
963 | + Layout.maximumHeight: units.gu(3) |
964 | + Layout.minimumHeight: units.gu(3) |
965 | + visible: listView.contentHeight > root.height |
966 | + enabled: !listView.atYBeginning |
967 | + |
968 | + Rectangle { |
969 | + color: enabled ? theme.palette.normal.overlayText : |
970 | + theme.palette.disabled.overlayText |
971 | + height: units.dp(1) |
972 | + anchors { |
973 | + bottom: parent.bottom |
974 | + left: parent.left |
975 | + right: parent.right |
976 | + } |
977 | + } |
978 | + |
979 | + Icon { |
980 | + anchors.centerIn: parent |
981 | + width: units.gu(2) |
982 | + height: units.gu(2) |
983 | + name: "up" |
984 | + color: enabled ? theme.palette.normal.overlayText : |
985 | + theme.palette.disabled.overlayText |
986 | + } |
987 | + |
988 | + MouseArea { |
989 | + anchors.fill: parent |
990 | + onPressed: { |
991 | + var index = listView.indexAt(0, listView.contentY); |
992 | + listView.positionViewAtIndex(index-1, ListView.Beginning); |
993 | + } |
994 | + } |
995 | + } |
996 | + |
997 | + ListView { |
998 | + id: listView |
999 | + objectName: "listView" |
1000 | + Layout.fillHeight: true |
1001 | + Layout.fillWidth: true |
1002 | + contentWidth: MathUtils.clamp(contentItem.childrenRect.width, |
1003 | + __minimumWidth, |
1004 | + __maximumWidth) |
1005 | + |
1006 | + orientation: Qt.Vertical |
1007 | + interactive: contentHeight > height |
1008 | + clip: interactive |
1009 | + highlightFollowsCurrentItem: false |
1010 | + |
1011 | + highlight: Rectangle { |
1012 | + color: "transparent" |
1013 | + border.width: units.dp(1) |
1014 | + border.color: UbuntuColors.orange |
1015 | + z: 1 |
1016 | + |
1017 | + width: listView.width |
1018 | + height: d.currentItem ? d.currentItem.height : 0 |
1019 | + y: d.currentItem ? d.currentItem.y : 0 |
1020 | + visible: d.currentItem |
1021 | + } |
1022 | + |
1023 | + function itemAt(index) { |
1024 | + if (index > count || index < 0) return null; |
1025 | + currentIndex = index; |
1026 | + return currentItem; |
1027 | + } |
1028 | + |
1029 | + MouseArea { |
1030 | + id: menuMouseArea |
1031 | + anchors.fill: listView |
1032 | + hoverEnabled: true |
1033 | + propagateComposedEvents: true // propogate events so we send clicks to children. |
1034 | + z: 1 // on top so we override any other hovers |
1035 | + onEntered: updateCurrentItemFromPosition(Qt.point(mouseX, mouseY)) |
1036 | + onPositionChanged: updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
1037 | + |
1038 | + function updateCurrentItemFromPosition(point) { |
1039 | + var pos = mapToItem(listView.contentItem, point.x, point.y); |
1040 | + |
1041 | + if (!d.hoveredItem || !d.currentItem || |
1042 | + !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) { |
1043 | + d.hoveredItem = listView.itemAt(listView.indexAt(pos.x, pos.y)); |
1044 | + if (!d.hoveredItem || !d.hoveredItem.enabled) |
1045 | + return false; |
1046 | + d.currentItem = d.hoveredItem; |
1047 | + } |
1048 | + return true; |
1049 | + } |
1050 | + } |
1051 | + |
1052 | + ActionContext { |
1053 | + id: menuBarContext |
1054 | + objectName: "menuContext" |
1055 | + active: { |
1056 | + if (!root.visible) return false; |
1057 | + if (d.currentItem && d.currentItem.popup && d.currentItem.popup.visible) { |
1058 | + return false; |
1059 | + } |
1060 | + return true; |
1061 | + } |
1062 | + } |
1063 | + |
1064 | + delegate: Loader { |
1065 | + id: loader |
1066 | + objectName: root.objectName + "-item" + __ownIndex |
1067 | + |
1068 | + property int __ownIndex: index |
1069 | + |
1070 | + width: root.width |
1071 | + enabled: model.isSeparator ? false : model.sensitive |
1072 | + |
1073 | + sourceComponent: { |
1074 | + if (model.isSeparator) { |
1075 | + return separatorComponent; |
1076 | + } |
1077 | + return menuItemComponent; |
1078 | + } |
1079 | + |
1080 | + property Item popup: null |
1081 | + |
1082 | + Component { |
1083 | + id: menuItemComponent |
1084 | + MenuItem { |
1085 | + id: menuItem |
1086 | + menuData: model |
1087 | + objectName: loader.objectName + "-actionItem" |
1088 | + |
1089 | + action.onTriggered: { |
1090 | + d.currentItem = loader; |
1091 | + |
1092 | + if (hasSubmenu) { |
1093 | + if (!popup) { |
1094 | + var model = root.unityMenuModel.submenu(__ownIndex); |
1095 | + popup = submenuComponent.createObject(focusScope, { |
1096 | + objectName: loader.objectName + "-", |
1097 | + unityMenuModel: model, |
1098 | + x: Qt.binding(function() { return root.width }), |
1099 | + y: Qt.binding(function() { return loader.y }) |
1100 | + }); |
1101 | + } else if (popup) { |
1102 | + popup.visible = true; |
1103 | + } |
1104 | + popup.retreat.connect(function() { |
1105 | + popup.destroy(); |
1106 | + popup = null; |
1107 | + menuItem.forceActiveFocus(); |
1108 | + }) |
1109 | + } else { |
1110 | + root.unityMenuModel.activate(__ownIndex); |
1111 | + } |
1112 | + } |
1113 | + |
1114 | + Connections { |
1115 | + target: d |
1116 | + onCurrentIndexChanged: { |
1117 | + if (popup && d.currentIndex != __ownIndex) { |
1118 | + popup.visible = false; |
1119 | + } |
1120 | + } |
1121 | + onDismissAll: { |
1122 | + if (popup) { |
1123 | + popup.destroy(); |
1124 | + popup = null; |
1125 | + } |
1126 | + } |
1127 | + } |
1128 | + } |
1129 | + } |
1130 | + |
1131 | + Component { |
1132 | + id: separatorComponent |
1133 | + ListItems.ThinDivider { |
1134 | + objectName: loader.objectName + "-separator" |
1135 | + } |
1136 | + } |
1137 | + } |
1138 | + } // ListView |
1139 | + |
1140 | + // FIXME use ListView.footer - tried but was flaky with positionViewAtIndex. |
1141 | + Item { |
1142 | + Layout.fillWidth: true; |
1143 | + Layout.maximumHeight: units.gu(3) |
1144 | + Layout.minimumHeight: units.gu(3) |
1145 | + visible: listView.contentHeight > root.height |
1146 | + enabled: !listView.atYEnd |
1147 | + |
1148 | + Rectangle { |
1149 | + color: enabled ? theme.palette.normal.overlayText : |
1150 | + theme.palette.disabled.overlayText |
1151 | + height: units.dp(1) |
1152 | + anchors { |
1153 | + top: parent.top |
1154 | + left: parent.left |
1155 | + right: parent.right |
1156 | + } |
1157 | + } |
1158 | + |
1159 | + Icon { |
1160 | + anchors.centerIn: parent |
1161 | + width: units.gu(2) |
1162 | + height: units.gu(2) |
1163 | + name: "down" |
1164 | + color: enabled ? theme.palette.normal.overlayText : |
1165 | + theme.palette.disabled.overlayText |
1166 | + } |
1167 | + |
1168 | + MouseArea { |
1169 | + anchors.fill: parent |
1170 | + onPressed: { |
1171 | + var index = listView.indexAt(0, listView.contentY); |
1172 | + listView.positionViewAtIndex(index+1, ListView.Beginning); |
1173 | + } |
1174 | + } |
1175 | + } |
1176 | + } // Column |
1177 | + |
1178 | + Component { |
1179 | + id: submenuComponent |
1180 | + Loader { |
1181 | + id: submenuLoader |
1182 | + source: "MenuPopup.qml" |
1183 | + |
1184 | + property var unityMenuModel: null |
1185 | + signal retreat() |
1186 | + |
1187 | + Binding { |
1188 | + target: item |
1189 | + property: "unityMenuModel" |
1190 | + value: submenuLoader.unityMenuModel |
1191 | + } |
1192 | + |
1193 | + Binding { |
1194 | + target: item |
1195 | + property: "objectName" |
1196 | + value: submenuLoader.objectName + "menu" |
1197 | + } |
1198 | + |
1199 | + Keys.onLeftPressed: retreat() |
1200 | + |
1201 | + Component.onCompleted: item.select(0); |
1202 | + onVisibleChanged: if (visible) { item.select(0); } |
1203 | + } |
1204 | + } |
1205 | + } |
1206 | +} |
1207 | |
1208 | === added file 'qml/ApplicationMenus/RegisteredApplicationMenuModel.qml' |
1209 | --- qml/ApplicationMenus/RegisteredApplicationMenuModel.qml 1970-01-01 00:00:00 +0000 |
1210 | +++ qml/ApplicationMenus/RegisteredApplicationMenuModel.qml 2017-01-09 15:26:31 +0000 |
1211 | @@ -0,0 +1,56 @@ |
1212 | +/* |
1213 | + * Copyright 2016 Canonical Ltd. |
1214 | + * |
1215 | + * This program is free software; you can redistribute it and/or modify |
1216 | + * it under the terms of the GNU Lesser General Public License as published by |
1217 | + * the Free Software Foundation; version 3. |
1218 | + * |
1219 | + * This program is distributed in the hope that it will be useful, |
1220 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1221 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1222 | + * GNU Lesser General Public License for more details. |
1223 | + * |
1224 | + * You should have received a copy of the GNU Lesser General Public License |
1225 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1226 | + */ |
1227 | + |
1228 | +import QtQuick 2.4 |
1229 | +import Ubuntu.Components 1.3 |
1230 | +import Unity.Indicators 0.1 |
1231 | +import Unity.ApplicationMenu 0.1 |
1232 | +import Unity.Indicators 0.1 as Indicators |
1233 | + |
1234 | +Object { |
1235 | + property string persistentSurfaceId |
1236 | + readonly property alias model: sharedAppModel.model |
1237 | + |
1238 | + Indicators.SharedUnityMenuModel { |
1239 | + id: sharedAppModel |
1240 | + property var menus: ApplicationMenuRegistry.getMenusForSurface(persistentSurfaceId) |
1241 | + property var menuService: menus.length > 0 ? menus[0] : null |
1242 | + |
1243 | + busName: menuService ? menuService.service : "" |
1244 | + menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : "" |
1245 | + actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {} |
1246 | + } |
1247 | + |
1248 | + onPersistentSurfaceIdChanged: update() |
1249 | + |
1250 | + function update() { |
1251 | + sharedAppModel.menus = Qt.binding(function() { return ApplicationMenuRegistry.getMenusForSurface(persistentSurfaceId); }); |
1252 | + } |
1253 | + |
1254 | + Connections { |
1255 | + target: ApplicationMenuRegistry |
1256 | + onSurfaceMenuRegistered: { |
1257 | + if (surfaceId === persistentSurfaceId) { |
1258 | + update(); |
1259 | + } |
1260 | + } |
1261 | + onSurfaceMenuUnregistered: { |
1262 | + if (surfaceId === persistentSurfaceId) { |
1263 | + update(); |
1264 | + } |
1265 | + } |
1266 | + } |
1267 | +} |
1268 | |
1269 | === modified file 'qml/CMakeLists.txt' |
1270 | --- qml/CMakeLists.txt 2016-08-04 14:27:38 +0000 |
1271 | +++ qml/CMakeLists.txt 2017-01-09 15:26:31 +0000 |
1272 | @@ -5,6 +5,7 @@ |
1273 | ) |
1274 | |
1275 | set(QML_DIRS |
1276 | + ApplicationMenus |
1277 | Components |
1278 | Dash |
1279 | graphics |
1280 | |
1281 | === modified file 'qml/Components/DragHandle.qml' |
1282 | --- qml/Components/DragHandle.qml 2016-05-18 21:30:25 +0000 |
1283 | +++ qml/Components/DragHandle.qml 2017-01-09 15:26:31 +0000 |
1284 | @@ -67,6 +67,7 @@ |
1285 | SmoothedAnimation { |
1286 | id: hintingAnimation |
1287 | target: hintingAnimation |
1288 | + objectName: "hintingAnimation" |
1289 | property: "targetValue" |
1290 | duration: 150 |
1291 | velocity: -1 |
1292 | |
1293 | === modified file 'qml/Components/PanelState/PanelState.qml' |
1294 | --- qml/Components/PanelState/PanelState.qml 2016-08-31 19:11:43 +0000 |
1295 | +++ qml/Components/PanelState/PanelState.qml 2017-01-09 15:26:31 +0000 |
1296 | @@ -1,5 +1,5 @@ |
1297 | /* |
1298 | - * Copyright (C) 2014 Canonical, Ltd. |
1299 | + * Copyright (C) 2014-2016 Canonical, Ltd. |
1300 | * |
1301 | * This program is free software; you can redistribute it and/or modify |
1302 | * it under the terms of the GNU General Public License as published by |
1303 | @@ -21,8 +21,8 @@ |
1304 | id: root |
1305 | |
1306 | property string title: "" |
1307 | - property bool buttonsVisible: false |
1308 | - property bool buttonsAlwaysVisible: false |
1309 | + property bool decorationsVisible: false |
1310 | + property bool decorationsAlwaysVisible: false |
1311 | property bool closeButtonShown: true |
1312 | property bool dropShadow: false |
1313 | property int panelHeight: 0 |
1314 | @@ -30,4 +30,7 @@ |
1315 | signal closeClicked() |
1316 | signal minimizeClicked() |
1317 | signal restoreClicked() |
1318 | + |
1319 | + property string focusedPersistentSurfaceId: "" |
1320 | + property bool focusedSurfaceMaximized: false |
1321 | } |
1322 | |
1323 | === modified file 'qml/Components/ScrollCalculator.qml' |
1324 | --- qml/Components/ScrollCalculator.qml 2015-07-15 15:07:19 +0000 |
1325 | +++ qml/Components/ScrollCalculator.qml 2017-01-09 15:26:31 +0000 |
1326 | @@ -22,6 +22,7 @@ |
1327 | |
1328 | readonly property bool areaActive: lateralPosition >= 0 |
1329 | property real stopScrollThreshold: units.gu(2) |
1330 | + property real progressThreshold: units.dp(4) |
1331 | property int direction: Qt.LeftToRight |
1332 | property real baseScrollAmount: units.dp(3) |
1333 | property real maximumScrollAmount: units.dp(8) |
1334 | @@ -38,6 +39,11 @@ |
1335 | function handleEnter() { |
1336 | d.thresholdAreaX = -scrollArea.stopScrollThreshold; |
1337 | scrollTimer.restart(); |
1338 | + d.init = true; |
1339 | + d.passedProgressThreshold = false; |
1340 | + |
1341 | + d.progression = 0; |
1342 | + d.startingProgression = 0; |
1343 | } |
1344 | |
1345 | function handleExit() { |
1346 | @@ -59,6 +65,10 @@ |
1347 | } |
1348 | |
1349 | d.progression = lateralPosition / width; |
1350 | + if (d.init) { |
1351 | + d.startingProgression = d.progression; |
1352 | + d.init = false; |
1353 | + } |
1354 | } |
1355 | } |
1356 | |
1357 | @@ -68,14 +78,23 @@ |
1358 | repeat: true |
1359 | |
1360 | onTriggered: { |
1361 | - var scrollAmount = scrollArea.baseScrollAmount + scrollArea.maximumScrollAmount * d.progression; |
1362 | - scrollArea.scroll(scrollAmount); |
1363 | + if (d.passedProgressThreshold || |
1364 | + Math.abs(d.progression - d.startingProgression) * scrollArea.width > scrollArea.progressThreshold) { |
1365 | + d.passedProgressThreshold = true; |
1366 | + |
1367 | + var scrollAmount = scrollArea.baseScrollAmount + scrollArea.maximumScrollAmount * d.progression; |
1368 | + scrollArea.scroll(scrollAmount); |
1369 | + } |
1370 | } |
1371 | } |
1372 | |
1373 | QtObject { |
1374 | id: d |
1375 | + property real startingProgression: 0 |
1376 | + property bool init: true |
1377 | + |
1378 | property real progression: 0 |
1379 | property real thresholdAreaX: -scrollArea.stopScrollThreshold |
1380 | + property bool passedProgressThreshold: false |
1381 | } |
1382 | } |
1383 | |
1384 | === modified file 'qml/Components/WindowControlButtons.qml' |
1385 | --- qml/Components/WindowControlButtons.qml 2016-09-22 10:33:39 +0000 |
1386 | +++ qml/Components/WindowControlButtons.qml 2017-01-09 15:26:31 +0000 |
1387 | @@ -46,6 +46,13 @@ |
1388 | onClicked: root.closeClicked() |
1389 | visible: root.closeButtonShown |
1390 | |
1391 | + // We dont want touch events to fall through to parent, |
1392 | + // otherwise the containsMouse will not work. |
1393 | + MouseArea { |
1394 | + anchors.fill: parent |
1395 | + propagateComposedEvents: true |
1396 | + } |
1397 | + |
1398 | Rectangle { |
1399 | anchors.fill: parent |
1400 | anchors.margins: windowIsMaximized ? units.dp(3) : 0 |
1401 | @@ -69,6 +76,13 @@ |
1402 | width: height |
1403 | onClicked: root.minimizeClicked() |
1404 | |
1405 | + // We dont want touch events to fall through to parent, |
1406 | + // otherwise the containsMouse will not work. |
1407 | + MouseArea { |
1408 | + anchors.fill: parent |
1409 | + propagateComposedEvents: true |
1410 | + } |
1411 | + |
1412 | Rectangle { |
1413 | anchors.fill: parent |
1414 | anchors.margins: windowIsMaximized ? units.dp(3) : 0 |
1415 | @@ -102,6 +116,13 @@ |
1416 | } |
1417 | } |
1418 | |
1419 | + // We dont want touch events to fall through to parent, |
1420 | + // otherwise the containsMouse will not work. |
1421 | + MouseArea { |
1422 | + anchors.fill: parent |
1423 | + propagateComposedEvents: true |
1424 | + } |
1425 | + |
1426 | Rectangle { |
1427 | anchors.fill: parent |
1428 | anchors.margins: windowIsMaximized ? units.dp(3) : 0 |
1429 | |
1430 | === modified file 'qml/OrientedShell.qml' |
1431 | --- qml/OrientedShell.qml 2016-09-01 07:40:06 +0000 |
1432 | +++ qml/OrientedShell.qml 2017-01-09 15:26:31 +0000 |
1433 | @@ -98,6 +98,12 @@ |
1434 | deviceFilter: InputInfo.TouchScreen |
1435 | } |
1436 | |
1437 | + Binding { |
1438 | + target: QuickUtils |
1439 | + property: "keyboardAttached" |
1440 | + value: keyboardsModel.count > 0 |
1441 | + } |
1442 | + |
1443 | readonly property int pointerInputDevices: miceModel.count + touchPadModel.count |
1444 | onPointerInputDevicesChanged: calculateUsageMode() |
1445 | |
1446 | |
1447 | === added file 'qml/Panel/FakePanelMenu.qml' |
1448 | --- qml/Panel/FakePanelMenu.qml 1970-01-01 00:00:00 +0000 |
1449 | +++ qml/Panel/FakePanelMenu.qml 2017-01-09 15:26:31 +0000 |
1450 | @@ -0,0 +1,26 @@ |
1451 | +/* |
1452 | + * Copyright (C) 2013-2015 Canonical, Ltd. |
1453 | + * |
1454 | + * This program is free software; you can redistribute it and/or modify |
1455 | + * it under the terms of the GNU General Public License as published by |
1456 | + * the Free Software Foundation; version 3. |
1457 | + * |
1458 | + * This program is distributed in the hope that it will be useful, |
1459 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1460 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1461 | + * GNU General Public License for more details. |
1462 | + * |
1463 | + * You should have received a copy of the GNU General Public License |
1464 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1465 | + */ |
1466 | + |
1467 | +import QtQuick 2.4 |
1468 | + |
1469 | +QtObject { |
1470 | + property list<QtObject> hides |
1471 | + property QtObject model: null |
1472 | + property bool expanded: false |
1473 | + |
1474 | + signal hide() |
1475 | + signal show() |
1476 | +} |
1477 | |
1478 | === renamed file 'qml/Panel/IndicatorItem.qml' => 'qml/Panel/Indicators/IndicatorItem.qml' |
1479 | --- qml/Panel/IndicatorItem.qml 2016-03-29 03:47:39 +0000 |
1480 | +++ qml/Panel/Indicators/IndicatorItem.qml 2017-01-09 15:26:31 +0000 |
1481 | @@ -18,7 +18,6 @@ |
1482 | import Ubuntu.Components 1.3 |
1483 | import Ubuntu.Settings.Components 0.1 |
1484 | import QMenuModel 0.1 |
1485 | -import "Indicators" |
1486 | |
1487 | IndicatorDelegate { |
1488 | id: root |
1489 | @@ -157,6 +156,8 @@ |
1490 | } |
1491 | |
1492 | StateGroup { |
1493 | + objectName: "indicatorItemState" |
1494 | + |
1495 | states: [ |
1496 | State { |
1497 | name: "minimised" |
1498 | |
1499 | === renamed file 'qml/Panel/Indicators/MenuItemFactory.qml' => 'qml/Panel/Indicators/IndicatorMenuItemFactory.qml' |
1500 | --- qml/Panel/Indicators/MenuItemFactory.qml 2016-11-30 12:16:26 +0000 |
1501 | +++ qml/Panel/Indicators/IndicatorMenuItemFactory.qml 2017-01-09 15:26:31 +0000 |
1502 | @@ -25,9 +25,10 @@ |
1503 | import Unity.Session 0.1 |
1504 | import Unity.Platform 1.0 |
1505 | |
1506 | -Item { |
1507 | +Object { |
1508 | id: menuFactory |
1509 | |
1510 | + property string indicator |
1511 | property var rootModel: null |
1512 | property var menuModel: null |
1513 | |
1514 | @@ -88,7 +89,7 @@ |
1515 | } |
1516 | } |
1517 | |
1518 | - function getComponentForIndicatorEntryType(indicator, type) { |
1519 | + function getComponentForIndicatorEntryType(type) { |
1520 | var component = undefined; |
1521 | var map = _userMap || _typeToComponent |
1522 | var indicatorComponents = map[indicator]; |
1523 | @@ -112,7 +113,7 @@ |
1524 | return component |
1525 | } |
1526 | |
1527 | - function getComponentForIndicatorEntryAction(indicator, action) { |
1528 | + function getComponentForIndicatorEntryAction(action) { |
1529 | var component = undefined; |
1530 | var indicatorFilter = _action_filter_map[indicator] |
1531 | |
1532 | @@ -1082,17 +1083,13 @@ |
1533 | } |
1534 | } |
1535 | |
1536 | - function load(modelData, context) { |
1537 | - if (context && context.indexOf("fake-") == 0) { |
1538 | - context = context.substring("fake-".length) |
1539 | - } |
1540 | - |
1541 | - var component = getComponentForIndicatorEntryAction(context, modelData.action) |
1542 | + function load(modelData) { |
1543 | + var component = getComponentForIndicatorEntryAction(modelData.action) |
1544 | if (component !== undefined) { |
1545 | return component |
1546 | } |
1547 | |
1548 | - component = getComponentForIndicatorEntryType(context, modelData.type) |
1549 | + component = getComponentForIndicatorEntryType(modelData.type) |
1550 | if (component !== undefined) { |
1551 | return component; |
1552 | } |
1553 | |
1554 | === modified file 'qml/Panel/MenuContent.qml' |
1555 | --- qml/Panel/MenuContent.qml 2016-03-23 21:03:33 +0000 |
1556 | +++ qml/Panel/MenuContent.qml 2017-01-09 15:26:31 +0000 |
1557 | @@ -25,8 +25,10 @@ |
1558 | Rectangle { |
1559 | id: content |
1560 | |
1561 | - property QtObject indicatorsModel: null |
1562 | + property QtObject model: null |
1563 | property int currentMenuIndex: -1 |
1564 | + property Component pageDelegate |
1565 | + |
1566 | color: theme.palette.normal.background |
1567 | |
1568 | width: units.gu(40) |
1569 | @@ -40,7 +42,7 @@ |
1570 | id: listViewContent |
1571 | objectName: "indicatorsContentListView" |
1572 | anchors.fill: parent |
1573 | - model: content.indicatorsModel |
1574 | + model: content.model |
1575 | |
1576 | highlightFollowsCurrentItem: true |
1577 | interactive: false |
1578 | @@ -59,18 +61,13 @@ |
1579 | |
1580 | width: ListView.view.width |
1581 | height: ListView.view.height |
1582 | - objectName: identifier |
1583 | asynchronous: true |
1584 | visible: ListView.isCurrentItem |
1585 | |
1586 | - sourceComponent: IndicatorPage { |
1587 | - objectName: identifier + "-page" |
1588 | + property var modelData: model |
1589 | + property var modelIndex: index |
1590 | |
1591 | - identifier: model.identifier |
1592 | - busName: indicatorProperties.busName |
1593 | - actionsObjectPath: indicatorProperties.actionsObjectPath |
1594 | - menuObjectPath: indicatorProperties.menuObjectPath |
1595 | - } |
1596 | + sourceComponent: pageDelegate |
1597 | |
1598 | onVisibleChanged: { |
1599 | // Reset the indicator states |
1600 | |
1601 | === modified file 'qml/Panel/Panel.qml' |
1602 | --- qml/Panel/Panel.qml 2016-09-22 10:33:39 +0000 |
1603 | +++ qml/Panel/Panel.qml 2017-01-09 15:26:31 +0000 |
1604 | @@ -16,91 +16,160 @@ |
1605 | |
1606 | import QtQuick 2.4 |
1607 | import Ubuntu.Components 1.3 |
1608 | +import Ubuntu.Layouts 1.0 |
1609 | import Unity.Application 0.1 |
1610 | +import Unity.Indicators 0.1 |
1611 | +import Utils 0.1 |
1612 | +import Unity.ApplicationMenu 0.1 |
1613 | + |
1614 | +import QtQuick.Window 2.2 |
1615 | +// for indicator-keyboard |
1616 | +import AccountsService 0.1 |
1617 | +import Unity.InputInfo 0.1 |
1618 | + |
1619 | +import "../ApplicationMenus" |
1620 | import "../Components" |
1621 | import "../Components/PanelState" |
1622 | import ".." |
1623 | +import "Indicators" |
1624 | |
1625 | Item { |
1626 | id: root |
1627 | - readonly property real panelHeight: indicatorArea.y + d.indicatorHeight |
1628 | + readonly property real panelHeight: panelArea.y + minimizedPanelHeight |
1629 | + |
1630 | + property real minimizedPanelHeight: units.gu(3) |
1631 | + property real expandedPanelHeight: units.gu(7) |
1632 | + property real indicatorMenuWidth: width |
1633 | + property real applicationMenuWidth: width |
1634 | + |
1635 | + property alias applicationMenus: __applicationMenus |
1636 | property alias indicators: __indicators |
1637 | - property alias callHint: __callHint |
1638 | property bool fullscreenMode: false |
1639 | - property real indicatorAreaShowProgress: 1.0 |
1640 | - property bool locked: false |
1641 | + property real panelAreaShowProgress: 1.0 |
1642 | + property bool greeterShown: false |
1643 | + |
1644 | + property string mode: "staged" |
1645 | |
1646 | MouseArea { |
1647 | + id: backMouseEater |
1648 | anchors.fill: parent |
1649 | anchors.topMargin: panelHeight |
1650 | - visible: !indicators.fullyClosed |
1651 | + visible: !indicators.fullyClosed || !applicationMenus.fullyClosed |
1652 | enabled: visible |
1653 | - onClicked: if (indicators.fullyOpened) indicators.hide(); |
1654 | hoverEnabled: true // should also eat hover events, otherwise they will pass through |
1655 | + |
1656 | + onClicked: { |
1657 | + __applicationMenus.hide(); |
1658 | + __indicators.hide(); |
1659 | + } |
1660 | } |
1661 | |
1662 | Binding { |
1663 | target: PanelState |
1664 | property: "panelHeight" |
1665 | - value: indicators.minimizedPanelHeight |
1666 | + value: minimizedPanelHeight |
1667 | + } |
1668 | + |
1669 | + RegisteredApplicationMenuModel { |
1670 | + id: registeredMenuModel |
1671 | + persistentSurfaceId: PanelState.focusedPersistentSurfaceId |
1672 | + } |
1673 | + |
1674 | + QtObject { |
1675 | + id: d |
1676 | + |
1677 | + property bool revealControls: !greeterShown && |
1678 | + !applicationMenus.shown && |
1679 | + !indicators.shown && |
1680 | + (decorationMouseArea.containsMouse || menuBarLoader.menusRequested) |
1681 | + |
1682 | + property bool showWindowDecorationControls: (revealControls && PanelState.decorationsVisible) || |
1683 | + PanelState.decorationsAlwaysVisible |
1684 | + |
1685 | + property bool showPointerMenu: revealControls && |
1686 | + (PanelState.decorationsVisible || mode == "staged") |
1687 | + |
1688 | + property bool enablePointerMenu: revealControls && |
1689 | + applicationMenus.available && |
1690 | + applicationMenus.model |
1691 | + |
1692 | + property bool showTouchMenu: !greeterShown && |
1693 | + !showPointerMenu |
1694 | + |
1695 | + property bool enableTouchMenus: showTouchMenu && |
1696 | + applicationMenus.available && |
1697 | + applicationMenus.model |
1698 | } |
1699 | |
1700 | Item { |
1701 | - id: indicatorArea |
1702 | - objectName: "indicatorArea" |
1703 | + id: panelArea |
1704 | + objectName: "panelArea" |
1705 | |
1706 | anchors.fill: parent |
1707 | |
1708 | transform: Translate { |
1709 | y: indicators.state === "initial" |
1710 | - ? (1.0 - indicatorAreaShowProgress) * -d.indicatorHeight |
1711 | + ? (1.0 - panelAreaShowProgress) * - minimizedPanelHeight |
1712 | : 0 |
1713 | } |
1714 | |
1715 | BorderImage { |
1716 | id: indicatorsDropShadow |
1717 | anchors { |
1718 | - fill: indicators |
1719 | - leftMargin: -units.gu(1) |
1720 | - bottomMargin: -units.gu(1) |
1721 | - } |
1722 | - visible: !indicators.fullyClosed |
1723 | + fill: __indicators |
1724 | + margins: -units.gu(1) |
1725 | + } |
1726 | + visible: !__indicators.fullyClosed |
1727 | + source: "graphics/rectangular_dropshadow.sci" |
1728 | + } |
1729 | + |
1730 | + BorderImage { |
1731 | + id: appmenuDropShadow |
1732 | + anchors { |
1733 | + fill: __applicationMenus |
1734 | + margins: -units.gu(1) |
1735 | + } |
1736 | + visible: !__applicationMenus.fullyClosed |
1737 | source: "graphics/rectangular_dropshadow.sci" |
1738 | } |
1739 | |
1740 | BorderImage { |
1741 | id: panelDropShadow |
1742 | anchors { |
1743 | - fill: indicatorAreaBackground |
1744 | + fill: panelAreaBackground |
1745 | bottomMargin: -units.gu(1) |
1746 | } |
1747 | - visible: PanelState.dropShadow && !callHint.visible |
1748 | + visible: PanelState.dropShadow |
1749 | source: "graphics/rectangular_dropshadow.sci" |
1750 | } |
1751 | |
1752 | Rectangle { |
1753 | - id: indicatorAreaBackground |
1754 | + id: panelAreaBackground |
1755 | color: callHint.visible ? theme.palette.normal.positive : theme.palette.normal.background |
1756 | anchors { |
1757 | top: parent.top |
1758 | left: parent.left |
1759 | right: parent.right |
1760 | } |
1761 | - height: indicators.minimizedPanelHeight |
1762 | + height: minimizedPanelHeight |
1763 | |
1764 | Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } } |
1765 | } |
1766 | |
1767 | MouseArea { |
1768 | + id: decorationMouseArea |
1769 | objectName: "windowControlArea" |
1770 | anchors { |
1771 | - top: parent.top |
1772 | left: parent.left |
1773 | - right: indicators.left |
1774 | - } |
1775 | - height: indicators.minimizedPanelHeight |
1776 | - hoverEnabled: true |
1777 | - onClicked: if (callHint.visible) { callHint.showLiveCall(); } |
1778 | + right: parent.right |
1779 | + } |
1780 | + height: minimizedPanelHeight |
1781 | + hoverEnabled: !__indicators.shown |
1782 | + onClicked: { |
1783 | + if (callHint.visible) { |
1784 | + callHint.showLiveCall(); |
1785 | + } |
1786 | + } |
1787 | |
1788 | onPressed: { |
1789 | if (!callHint.visible) { |
1790 | @@ -109,29 +178,135 @@ |
1791 | } |
1792 | } |
1793 | |
1794 | - // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/ |
1795 | - // cf. https://bugreports.qt.io/browse/QTBUG-32909 |
1796 | - WindowControlButtons { |
1797 | - id: windowControlButtons |
1798 | - objectName: "panelWindowControlButtons" |
1799 | - anchors { |
1800 | - left: parent.left |
1801 | - top: parent.top |
1802 | - } |
1803 | - height: indicators.minimizedPanelHeight |
1804 | - |
1805 | - visible: ((PanelState.buttonsVisible && parent.containsMouse) || PanelState.buttonsAlwaysVisible) |
1806 | - && !root.locked && !callHint.visible |
1807 | - active: PanelState.buttonsVisible || PanelState.buttonsAlwaysVisible |
1808 | - windowIsMaximized: true |
1809 | - onCloseClicked: PanelState.closeClicked() |
1810 | - onMinimizeClicked: PanelState.minimizeClicked() |
1811 | - onMaximizeClicked: PanelState.restoreClicked() |
1812 | - closeButtonShown: PanelState.closeButtonShown |
1813 | - } |
1814 | - } |
1815 | - |
1816 | - IndicatorsMenu { |
1817 | + Row { |
1818 | + anchors.fill: parent |
1819 | + spacing: units.gu(2) |
1820 | + |
1821 | + // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/ |
1822 | + // cf. https://bugreports.qt.io/browse/QTBUG-32909 |
1823 | + WindowControlButtons { |
1824 | + id: windowControlButtons |
1825 | + objectName: "panelWindowControlButtons" |
1826 | + height: indicators.minimizedPanelHeight |
1827 | + opacity: d.showWindowDecorationControls ? 1 : 0 |
1828 | + visible: opacity != 0 |
1829 | + Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
1830 | + |
1831 | + active: PanelState.decorationsVisible || PanelState.decorationsAlwaysVisible |
1832 | + windowIsMaximized: true |
1833 | + onCloseClicked: PanelState.closeClicked() |
1834 | + onMinimizeClicked: PanelState.minimizeClicked() |
1835 | + onMaximizeClicked: PanelState.restoreClicked() |
1836 | + closeButtonShown: PanelState.closeButtonShown |
1837 | + } |
1838 | + |
1839 | + Loader { |
1840 | + id: menuBarLoader |
1841 | + height: parent.height |
1842 | + enabled: d.enablePointerMenu |
1843 | + opacity: d.showPointerMenu ? 1 : 0 |
1844 | + visible: opacity != 0 |
1845 | + Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
1846 | + active: __applicationMenus.model |
1847 | + |
1848 | + property bool menusRequested: menuBarLoader.item ? menuBarLoader.item.showRequested : false |
1849 | + |
1850 | + sourceComponent: MenuBar { |
1851 | + id: bar |
1852 | + objectName: "menuBar" |
1853 | + anchors.left: parent.left |
1854 | + anchors.margins: units.gu(1) |
1855 | + height: menuBarLoader.height |
1856 | + enableKeyFilter: valid && PanelState.decorationsVisible |
1857 | + unityMenuModel: __applicationMenus.model |
1858 | + |
1859 | + Connections { |
1860 | + target: __applicationMenus |
1861 | + onShownChanged: bar.dismiss(); |
1862 | + } |
1863 | + |
1864 | + Connections { |
1865 | + target: __indicators |
1866 | + onShownChanged: bar.dismiss(); |
1867 | + } |
1868 | + } |
1869 | + } |
1870 | + } |
1871 | + |
1872 | + ActiveCallHint { |
1873 | + id: callHint |
1874 | + objectName: "callHint" |
1875 | + |
1876 | + anchors.centerIn: parent |
1877 | + height: minimizedPanelHeight |
1878 | + |
1879 | + visible: active && indicators.state == "initial" && __applicationMenus.state == "initial" |
1880 | + greeterShown: root.greeterShown |
1881 | + } |
1882 | + } |
1883 | + |
1884 | + PanelMenu { |
1885 | + id: __applicationMenus |
1886 | + |
1887 | + model: registeredMenuModel.model |
1888 | + width: root.applicationMenuWidth |
1889 | + minimizedPanelHeight: root.minimizedPanelHeight |
1890 | + expandedPanelHeight: root.expandedPanelHeight |
1891 | + openedHeight: root.height |
1892 | + alignment: Qt.AlignLeft |
1893 | + enableHint: !callHint.active && !fullscreenMode |
1894 | + showOnClick: false |
1895 | + panelColor: panelAreaBackground.color |
1896 | + |
1897 | + onShowTapped: { |
1898 | + if (callHint.active) { |
1899 | + callHint.showLiveCall(); |
1900 | + } |
1901 | + } |
1902 | + |
1903 | + showRowTitle: !expanded |
1904 | + rowTitle: PanelState.title |
1905 | + rowItemDelegate: ActionItem { |
1906 | + id: actionItem |
1907 | + property int ownIndex: index |
1908 | + objectName: "appMenuItem"+index |
1909 | + |
1910 | + width: _title.width + units.gu(2) |
1911 | + height: parent.height |
1912 | + |
1913 | + action: Action { |
1914 | + text: model.label.replace("_", "&") |
1915 | + } |
1916 | + |
1917 | + Label { |
1918 | + id: _title |
1919 | + anchors.centerIn: parent |
1920 | + text: actionItem.text |
1921 | + horizontalAlignment: Text.AlignLeft |
1922 | + color: enabled ? "white" : "#5d5d5d" |
1923 | + } |
1924 | + } |
1925 | + |
1926 | + pageDelegate: PanelMenuPage { |
1927 | + menuModel: __applicationMenus.model |
1928 | + submenuIndex: modelIndex |
1929 | + |
1930 | + factory: ApplicationMenuItemFactory { |
1931 | + rootModel: __applicationMenus.model |
1932 | + } |
1933 | + } |
1934 | + |
1935 | + enabled: d.enableTouchMenus |
1936 | + opacity: d.showTouchMenu ? 1 : 0 |
1937 | + visible: opacity != 0 |
1938 | + Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
1939 | + |
1940 | + onEnabledChanged: { |
1941 | + if (!enabled) hide(); |
1942 | + } |
1943 | + } |
1944 | + |
1945 | + PanelMenu { |
1946 | id: __indicators |
1947 | objectName: "indicators" |
1948 | |
1949 | @@ -139,72 +314,93 @@ |
1950 | top: parent.top |
1951 | right: parent.right |
1952 | } |
1953 | - |
1954 | - shown: false |
1955 | - width: root.width - (windowControlButtons.visible ? windowControlButtons.width + titleLabel.width : 0) |
1956 | - minimizedPanelHeight: units.gu(3) |
1957 | - expandedPanelHeight: units.gu(7) |
1958 | + width: root.indicatorMenuWidth |
1959 | + minimizedPanelHeight: root.minimizedPanelHeight |
1960 | + expandedPanelHeight: root.expandedPanelHeight |
1961 | openedHeight: root.height |
1962 | |
1963 | - overFlowWidth: { |
1964 | - if (callHint.visible) { |
1965 | - return Math.max(root.width - (callHint.width + units.gu(2)), 0) |
1966 | - } |
1967 | - return root.width |
1968 | - } |
1969 | + overFlowWidth: root.width |
1970 | enableHint: !callHint.active && !fullscreenMode |
1971 | showOnClick: !callHint.visible |
1972 | - panelColor: indicatorAreaBackground.color |
1973 | + panelColor: panelAreaBackground.color |
1974 | |
1975 | onShowTapped: { |
1976 | if (callHint.active) { |
1977 | callHint.showLiveCall(); |
1978 | } |
1979 | } |
1980 | - } |
1981 | - |
1982 | - Label { |
1983 | - id: titleLabel |
1984 | - objectName: "windowDecorationTitle" |
1985 | - anchors { |
1986 | - left: parent.left |
1987 | - right: __indicators.left |
1988 | - top: parent.top |
1989 | - leftMargin: units.gu(1) |
1990 | - rightMargin: units.gu(1) |
1991 | - topMargin: units.gu(0.5) |
1992 | - bottomMargin: units.gu(0.5) |
1993 | - } |
1994 | - color: "white" |
1995 | - height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin |
1996 | - opacity: !windowControlButtons.visible && !root.locked && !callHint.visible ? 1 : 0 |
1997 | - visible: opacity != 0 |
1998 | - verticalAlignment: Text.AlignVCenter |
1999 | - fontSize: "medium" |
2000 | - font.weight: PanelState.buttonsVisible ? Font.Light : Font.Medium |
2001 | - text: PanelState.title |
2002 | - elide: Text.ElideRight |
2003 | - maximumLineCount: 1 |
2004 | - Behavior on opacity { UbuntuNumberAnimation {} } |
2005 | - } |
2006 | - |
2007 | - // TODO here would the Locally integrated menus come |
2008 | - |
2009 | - ActiveCallHint { |
2010 | - id: __callHint |
2011 | - anchors { |
2012 | - top: parent.top |
2013 | - left: parent.left |
2014 | - } |
2015 | - height: indicators.minimizedPanelHeight |
2016 | - visible: active && indicators.state == "initial" |
2017 | - } |
2018 | - } |
2019 | - |
2020 | - QtObject { |
2021 | - id: d |
2022 | - objectName: "panelPriv" |
2023 | - readonly property real indicatorHeight: indicators.minimizedPanelHeight |
2024 | + |
2025 | + rowItemDelegate: IndicatorItem { |
2026 | + id: indicatorItem |
2027 | + objectName: identifier+"-panelItem" |
2028 | + |
2029 | + property int ownIndex: index |
2030 | + property bool overflow: parent.width - x > __indicators.overFlowWidth |
2031 | + property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator) |
2032 | + // HACK for indicator-session |
2033 | + readonly property bool hideSessionIndicator: identifier == "indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60) |
2034 | + // HACK for indicator-keyboard |
2035 | + readonly property bool hideKeyboardIndicator: identifier == "indicator-keyboard" && (AccountsService.keymaps.length < 2 || keyboardsModel.count == 0) |
2036 | + |
2037 | + height: parent.height |
2038 | + expanded: indicators.expanded |
2039 | + selected: ListView.isCurrentItem |
2040 | + |
2041 | + identifier: model.identifier |
2042 | + busName: indicatorProperties.busName |
2043 | + actionsObjectPath: indicatorProperties.actionsObjectPath |
2044 | + menuObjectPath: indicatorProperties.menuObjectPath |
2045 | + |
2046 | + opacity: hidden ? 0.0 : 1.0 |
2047 | + Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
2048 | + |
2049 | + width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0 |
2050 | + |
2051 | + Behavior on width { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
2052 | + } |
2053 | + |
2054 | + pageDelegate: PanelMenuPage { |
2055 | + objectName: modelData.identifier + "-page" |
2056 | + submenuIndex: 0 |
2057 | + |
2058 | + menuModel: delegate.menuModel |
2059 | + |
2060 | + factory: IndicatorMenuItemFactory { |
2061 | + indicator: { |
2062 | + var context = modelData.identifier; |
2063 | + if (context && context.indexOf("fake-") === 0) { |
2064 | + context = context.substring("fake-".length) |
2065 | + } |
2066 | + return context; |
2067 | + } |
2068 | + rootModel: delegate.menuModel |
2069 | + } |
2070 | + |
2071 | + IndicatorDelegate { |
2072 | + id: delegate |
2073 | + busName: modelData.indicatorProperties.busName |
2074 | + actionsObjectPath: modelData.indicatorProperties.actionsObjectPath |
2075 | + menuObjectPath: modelData.indicatorProperties.menuObjectPath |
2076 | + } |
2077 | + } |
2078 | + |
2079 | + enabled: !applicationMenus.expanded |
2080 | + opacity: !applicationMenus.expanded ? 1 : 0 |
2081 | + Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
2082 | + |
2083 | + onEnabledChanged: { |
2084 | + if (!enabled) hide(); |
2085 | + } |
2086 | + } |
2087 | + } |
2088 | + |
2089 | + InputDeviceModel { |
2090 | + id: keyboardsModel |
2091 | + deviceFilter: InputInfo.Keyboard |
2092 | + } |
2093 | + |
2094 | + IndicatorsLight { |
2095 | + id: indicatorLights |
2096 | } |
2097 | |
2098 | states: [ |
2099 | @@ -212,7 +408,7 @@ |
2100 | name: "onscreen" //fully opaque and visible at top edge of screen |
2101 | when: !fullscreenMode |
2102 | PropertyChanges { |
2103 | - target: indicatorArea; |
2104 | + target: panelArea; |
2105 | anchors.topMargin: 0 |
2106 | opacity: 1; |
2107 | } |
2108 | @@ -221,25 +417,33 @@ |
2109 | name: "offscreen" //pushed off screen |
2110 | when: fullscreenMode |
2111 | PropertyChanges { |
2112 | - target: indicatorArea; |
2113 | - anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0 |
2114 | - opacity: indicators.fullyClosed ? 0.0 : 1.0 |
2115 | + target: panelArea; |
2116 | + anchors.topMargin: { |
2117 | + if (indicators.state !== "initial") return 0; |
2118 | + if (applicationMenus.state !== "initial") return 0; |
2119 | + return -minimizedPanelHeight; |
2120 | + } |
2121 | + opacity: indicators.fullyClosed && applicationMenus.fullyClosed ? 0.0 : 1.0 |
2122 | } |
2123 | PropertyChanges { |
2124 | target: indicators.showDragHandle; |
2125 | anchors.bottomMargin: -units.gu(1) |
2126 | } |
2127 | + PropertyChanges { |
2128 | + target: applicationMenus.showDragHandle; |
2129 | + anchors.bottomMargin: -units.gu(1) |
2130 | + } |
2131 | } |
2132 | ] |
2133 | |
2134 | transitions: [ |
2135 | Transition { |
2136 | to: "onscreen" |
2137 | - UbuntuNumberAnimation { target: indicatorArea; properties: "anchors.topMargin,opacity" } |
2138 | + UbuntuNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" } |
2139 | }, |
2140 | Transition { |
2141 | to: "offscreen" |
2142 | - UbuntuNumberAnimation { target: indicatorArea; properties: "anchors.topMargin,opacity" } |
2143 | + UbuntuNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" } |
2144 | } |
2145 | ] |
2146 | } |
2147 | |
2148 | === renamed file 'qml/Panel/IndicatorsBar.qml' => 'qml/Panel/PanelBar.qml' |
2149 | --- qml/Panel/IndicatorsBar.qml 2015-09-02 07:42:27 +0000 |
2150 | +++ qml/Panel/PanelBar.qml 2017-01-09 15:26:31 +0000 |
2151 | @@ -22,13 +22,19 @@ |
2152 | id: root |
2153 | property alias expanded: row.expanded |
2154 | property alias interactive: flickable.interactive |
2155 | - property alias indicatorsModel: row.indicatorsModel |
2156 | + property alias model: row.model |
2157 | property alias unitProgress: row.unitProgress |
2158 | property alias enableLateralChanges: row.enableLateralChanges |
2159 | property alias overFlowWidth: row.overFlowWidth |
2160 | readonly property alias currentItemIndex: row.currentItemIndex |
2161 | property real lateralPosition: -1 |
2162 | - readonly property string currentIndicator: row.currentItem ? row.currentItem.identifier : "" |
2163 | + property int alignment: Qt.AlignRight |
2164 | + |
2165 | + property alias showRowTitle: row.showRowTitle |
2166 | + property alias rowTitle: row.rowTitle |
2167 | + property alias rowItemDelegate: row.delegate |
2168 | + |
2169 | + implicitWidth: flickable.contentWidth |
2170 | |
2171 | function selectItemAt(lateralPosition) { |
2172 | if (!expanded) { |
2173 | @@ -46,15 +52,21 @@ |
2174 | } |
2175 | |
2176 | function addScrollOffset(scrollAmmout) { |
2177 | + if (root.alignment == Qt.AlignLeft) { |
2178 | + scrollAmmout = -scrollAmmout; |
2179 | + } |
2180 | + |
2181 | if (scrollAmmout < 0) { // left scroll |
2182 | if (flickable.contentX + flickable.width > row.width) return; // already off the left. |
2183 | |
2184 | - if (flickable.contentX + flickable.width - scrollAmmout > row.width) { // going to be off the right |
2185 | + if (flickable.contentX + flickable.width - scrollAmmout > row.width) { // going to be off the left |
2186 | scrollAmmout = (flickable.contentX + flickable.width) - row.width; |
2187 | } |
2188 | } else { // right scroll |
2189 | if (flickable.contentX < 0) return; // already off the right. |
2190 | - if (flickable.contentX - scrollAmmout < 0) scrollAmmout = flickable.contentX; // going to be off the right |
2191 | + if (flickable.contentX - scrollAmmout < 0) { // going to be off the right |
2192 | + scrollAmmout = flickable.contentX; |
2193 | + } |
2194 | } |
2195 | d.scrollOffset = d.scrollOffset + scrollAmmout; |
2196 | } |
2197 | @@ -62,14 +74,19 @@ |
2198 | QtObject { |
2199 | id: d |
2200 | property var initialItem |
2201 | - // the non-expanded distance from row offset to center of initial item |
2202 | - property real originalDistanceFromRight: -1 |
2203 | + // the non-expanded distance from alignment edge to center of initial item |
2204 | + property real originalDistanceFromEdge: -1 |
2205 | |
2206 | - // calculate the distance from row offset to center of initial item |
2207 | - property real distanceFromRight: { |
2208 | - if (originalDistanceFromRight == -1) return 0; |
2209 | + // calculate the distance from row alignment edge edge to center of initial item |
2210 | + property real distanceFromEdge: { |
2211 | + if (originalDistanceFromEdge == -1) return 0; |
2212 | if (!initialItem) return 0; |
2213 | - return row.width - initialItem.x - initialItem.width /2; |
2214 | + |
2215 | + if (root.alignment == Qt.AlignLeft) { |
2216 | + return initialItem.x - initialItem.width / 2; |
2217 | + } else { |
2218 | + return row.width - initialItem.x - initialItem.width / 2; |
2219 | + } |
2220 | } |
2221 | |
2222 | // offset to the intially selected expanded item |
2223 | @@ -82,7 +99,11 @@ |
2224 | onScrollOffsetChanged: root.lateralPositionChanged() |
2225 | |
2226 | onInitialItemChanged: { |
2227 | - originalDistanceFromRight = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1; |
2228 | + if (root.alignment == Qt.AlignLeft) { |
2229 | + originalDistanceFromEdge = initialItem ? (initialItem.x - initialItem.width/2) : -1; |
2230 | + } else { |
2231 | + originalDistanceFromEdge = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1; |
2232 | + } |
2233 | } |
2234 | |
2235 | Behavior on alignmentAdjustment { |
2236 | @@ -93,26 +114,38 @@ |
2237 | flickable.resetContentXComponents(); |
2238 | |
2239 | if (expanded && !flickable.moving) { |
2240 | - // gap between left and row? |
2241 | - if (flickable.contentX + flickable.width > row.width) { |
2242 | - // row width is less than flickable |
2243 | - if (row.width < flickable.width) { |
2244 | + |
2245 | + if (root.alignment == Qt.AlignLeft) { |
2246 | + // current item overlap on left |
2247 | + if (row.currentItem && flickable.contentX > row.currentItem.x) { |
2248 | + d.alignmentAdjustment -= (flickable.contentX - row.currentItem.x); |
2249 | + |
2250 | + // current item overlap on right |
2251 | + } else if (row.currentItem && flickable.contentX + flickable.width < row.currentItem.x + row.currentItem.width) { |
2252 | + d.alignmentAdjustment += (row.currentItem.x + row.currentItem.width) - (flickable.contentX + flickable.width); |
2253 | + } |
2254 | + } else { |
2255 | + // gap between left and row? |
2256 | + if (flickable.contentX + flickable.width > row.width) { |
2257 | + // row width is less than flickable |
2258 | + if (row.width < flickable.width) { |
2259 | + d.alignmentAdjustment -= flickable.contentX; |
2260 | + } else { |
2261 | + d.alignmentAdjustment -= ((flickable.contentX + flickable.width) - row.width); |
2262 | + } |
2263 | + |
2264 | + // gap between right and row? |
2265 | + } else if (flickable.contentX < 0) { |
2266 | d.alignmentAdjustment -= flickable.contentX; |
2267 | - } else { |
2268 | - d.alignmentAdjustment -= ((flickable.contentX + flickable.width) - row.width); |
2269 | + |
2270 | + // current item overlap on left |
2271 | + } else if (row.currentItem && (flickable.contentX + flickable.width) < (row.width - row.currentItem.x)) { |
2272 | + d.alignmentAdjustment += ((row.width - row.currentItem.x) - (flickable.contentX + flickable.width)); |
2273 | + |
2274 | + // current item overlap on right |
2275 | + } else if (row.currentItem && flickable.contentX > (row.width - row.currentItem.x - row.currentItem.width)) { |
2276 | + d.alignmentAdjustment -= flickable.contentX - (row.width - row.currentItem.x - row.currentItem.width); |
2277 | } |
2278 | - |
2279 | - // gap between right and row? |
2280 | - } else if (flickable.contentX < 0) { |
2281 | - d.alignmentAdjustment -= flickable.contentX; |
2282 | - |
2283 | - // current item overlap on left |
2284 | - } else if (row.currentItem && (flickable.contentX + flickable.width) < (row.width - row.currentItem.x)) { |
2285 | - d.alignmentAdjustment += ((row.width - row.currentItem.x) - (flickable.contentX + flickable.width)); |
2286 | - |
2287 | - // current item overlap on right |
2288 | - } else if (row.currentItem && flickable.contentX > (row.width - row.currentItem.x - row.currentItem.width)) { |
2289 | - d.alignmentAdjustment -= flickable.contentX - (row.width - row.currentItem.x - row.currentItem.width); |
2290 | } |
2291 | } |
2292 | } |
2293 | @@ -140,7 +173,7 @@ |
2294 | |
2295 | // we rotate it because we want the Flickable to align its content item |
2296 | // on the right instead of on the left |
2297 | - rotation: 180 |
2298 | + rotation: root.alignment != Qt.AlignRight ? 0 : 180 |
2299 | |
2300 | anchors.fill: parent |
2301 | contentWidth: row.width |
2302 | @@ -161,16 +194,16 @@ |
2303 | } |
2304 | } |
2305 | |
2306 | - IndicatorItemRow { |
2307 | + PanelItemRow { |
2308 | id: row |
2309 | - objectName: "indicatorItemRow" |
2310 | + objectName: "panelItemRow" |
2311 | anchors { |
2312 | top: parent.top |
2313 | bottom: parent.bottom |
2314 | } |
2315 | |
2316 | // Compensate for the Flickable rotation (ie, counter-rotate) |
2317 | - rotation: 180 |
2318 | + rotation: root.alignment != Qt.AlignRight ? 0 : 180 |
2319 | |
2320 | lateralPosition: { |
2321 | if (root.lateralPosition == -1) return -1; |
2322 | @@ -230,15 +263,15 @@ |
2323 | target: d |
2324 | rowOffset: { |
2325 | if (!initialItem) return 0; |
2326 | - if (distanceFromRight - initialItem.width <= 0) return 0; |
2327 | + if (distanceFromEdge - initialItem.width <= 0) return 0; |
2328 | |
2329 | - var rowOffset = distanceFromRight - originalDistanceFromRight; |
2330 | + var rowOffset = distanceFromEdge - originalDistanceFromEdge; |
2331 | return rowOffset; |
2332 | } |
2333 | restoreEntryValues: false |
2334 | } |
2335 | - } |
2336 | - , State { |
2337 | + }, |
2338 | + State { |
2339 | name: "interactive" |
2340 | when: expanded && interactive |
2341 | |
2342 | |
2343 | === renamed file 'qml/Panel/IndicatorItemRow.qml' => 'qml/Panel/PanelItemRow.qml' |
2344 | --- qml/Panel/IndicatorItemRow.qml 2016-07-13 21:16:45 +0000 |
2345 | +++ qml/Panel/PanelItemRow.qml 2017-01-09 15:26:31 +0000 |
2346 | @@ -15,29 +15,29 @@ |
2347 | */ |
2348 | |
2349 | import QtQuick 2.4 |
2350 | -import QtQuick.Window 2.2 |
2351 | import Ubuntu.Components 1.3 |
2352 | - |
2353 | -// for indicator-keyboard |
2354 | -import AccountsService 0.1 |
2355 | -import Unity.InputInfo 0.1 |
2356 | +import "../Components" |
2357 | |
2358 | Item { |
2359 | id: root |
2360 | - width: row.width |
2361 | - height: units.gu(3) |
2362 | + implicitWidth: showRowTitle && !expanded ? rowTitle != "" ? rowLabel.width : 0 : row.width |
2363 | + implicitHeight: units.gu(3) |
2364 | |
2365 | - property QtObject indicatorsModel: null |
2366 | + property bool showRowTitle: false |
2367 | + property alias rowTitle: rowLabel.text |
2368 | + property QtObject model: null |
2369 | property real overFlowWidth: width |
2370 | property bool expanded: false |
2371 | - property var currentItem |
2372 | - readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1 |
2373 | + readonly property alias currentItem: row.currentItem |
2374 | + readonly property alias currentItemIndex: row.currentIndex |
2375 | |
2376 | property real unitProgress: 0.0 |
2377 | property real selectionChangeBuffer: units.gu(2) |
2378 | property bool enableLateralChanges: false |
2379 | property color hightlightColor: "#ffffff" |
2380 | |
2381 | + property alias delegate: row.delegate |
2382 | + |
2383 | property real lateralPosition: -1 |
2384 | onLateralPositionChanged: { |
2385 | updateItemFromLateralPosition(); |
2386 | @@ -86,21 +86,21 @@ |
2387 | } |
2388 | |
2389 | function indicatorAt(x, y) { |
2390 | - var item = row.childAt(x, y); |
2391 | + var item = row.itemAt(x, y); |
2392 | return item && item.hasOwnProperty("ownIndex") ? item : null; |
2393 | } |
2394 | |
2395 | function resetCurrentItem() { |
2396 | d.firstItemSwitch = true; |
2397 | - d.previousItem = undefined; |
2398 | - currentItem = undefined; |
2399 | + d.previousItem = null; |
2400 | + row.currentIndex = -1; |
2401 | } |
2402 | |
2403 | function setCurrentItemIndex(index) { |
2404 | - for (var i = 0; i < row.children.length; i++) { |
2405 | - var item = row.children[i]; |
2406 | + for (var i = 0; i < row.contentItem.children.length; i++) { |
2407 | + var item = row.contentItem.children[i]; |
2408 | if (item.hasOwnProperty("ownIndex") && item.ownIndex === index) { |
2409 | - if (currentItem !== item) currentItem = item; |
2410 | + if (currentItem !== item) row.currentIndex = index; |
2411 | break; |
2412 | } |
2413 | } |
2414 | @@ -109,18 +109,18 @@ |
2415 | function selectItemAt(lateralPosition) { |
2416 | var item = indicatorAt(lateralPosition, 0); |
2417 | if (item && item.opacity > 0) { |
2418 | - currentItem = item; |
2419 | + row.currentIndex = item.ownIndex; |
2420 | } else { |
2421 | // Select default item. |
2422 | - var searchIndex = lateralPosition > width ? repeater.count - 1 : 0; |
2423 | + var searchIndex = lateralPosition >= width ? row.count - 1 : 0; |
2424 | |
2425 | - for (var i = 0; i < row.children.length; i++) { |
2426 | - if (row.children[i].hasOwnProperty("ownIndex") && row.children[i].ownIndex === searchIndex) { |
2427 | - item = row.children[i]; |
2428 | + for (var i = 0; i < row.contentItem.children.length; i++) { |
2429 | + if (row.contentItem.children[i].hasOwnProperty("ownIndex") && row.contentItem.children[i].ownIndex === searchIndex) { |
2430 | + item = row.contentItem.children[i]; |
2431 | break; |
2432 | } |
2433 | } |
2434 | - if (currentItem !== item) currentItem = item; |
2435 | + if (currentItem !== item) row.currentIndex = item ? item.ownIndex : -1; |
2436 | } |
2437 | } |
2438 | |
2439 | @@ -131,20 +131,39 @@ |
2440 | property bool forceAlignmentAnimationDisabled: false |
2441 | } |
2442 | |
2443 | - InputDeviceModel { |
2444 | - id: keyboardsModel |
2445 | - deviceFilter: InputInfo.Keyboard |
2446 | - } |
2447 | - |
2448 | onCurrentItemChanged: { |
2449 | if (d.previousItem) { |
2450 | d.firstItemSwitch = false; |
2451 | } |
2452 | - d.previousItem = currentItem; |
2453 | - } |
2454 | - |
2455 | - Row { |
2456 | + d.previousItem = currentItem;\ |
2457 | + } |
2458 | + |
2459 | + Label { |
2460 | + id: rowLabel |
2461 | + objectName: "panelTitle" |
2462 | + anchors { |
2463 | + left: parent.left |
2464 | + leftMargin: units.gu(1) |
2465 | + verticalCenter: parent.verticalCenter |
2466 | + } |
2467 | + width: implicitWidth + units.gu(2) |
2468 | + elide: Text.ElideRight |
2469 | + maximumLineCount: 1 |
2470 | + fontSize: "medium" |
2471 | + font.weight: Font.Medium |
2472 | + color: Theme.palette.selected.backgroundText |
2473 | + opacity: showRowTitle ? 1 : 0 |
2474 | + visible: opacity != 0 |
2475 | + Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
2476 | + } |
2477 | + |
2478 | + ListView { |
2479 | id: row |
2480 | + objectName: "panelRow" |
2481 | + orientation: ListView.Horizontal |
2482 | + model: root.model |
2483 | + opacity: showRowTitle ? 0 : 1 |
2484 | + // dont set visible on basis of opacity; otherwise width will not be calculated correctly |
2485 | anchors { |
2486 | top: parent.top |
2487 | bottom: parent.bottom |
2488 | @@ -153,7 +172,7 @@ |
2489 | // TODO: make this better |
2490 | // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation |
2491 | // and adjust the highlight X immediately. |
2492 | - width: implicitWidth |
2493 | + width: childrenRect.width |
2494 | Behavior on width { |
2495 | SequentialAnimation { |
2496 | ScriptAction { |
2497 | @@ -166,75 +185,7 @@ |
2498 | } |
2499 | } |
2500 | |
2501 | - Repeater { |
2502 | - id: repeater |
2503 | - model: indicatorsModel |
2504 | - visible: false |
2505 | - |
2506 | - onItemRemoved: { |
2507 | - // current item removed. |
2508 | - if (currentItem === item) { |
2509 | - var i = 0; |
2510 | - while (i < row.children.length) { |
2511 | - var childItem = row.children[i]; |
2512 | - if (childItem !== item) { |
2513 | - setCurrentItemIndex(i); |
2514 | - break; |
2515 | - } |
2516 | - i++; |
2517 | - } |
2518 | - } |
2519 | - } |
2520 | - |
2521 | - |
2522 | - delegate: IndicatorItem { |
2523 | - id: indicatorItem |
2524 | - objectName: identifier+"-panelItem" |
2525 | - |
2526 | - property int ownIndex: index |
2527 | - property bool overflow: row.width - x > overFlowWidth |
2528 | - property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator) |
2529 | - // HACK for indicator-session |
2530 | - readonly property bool hideSessionIndicator: identifier == "indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60) |
2531 | - // HACK for indicator-keyboard |
2532 | - readonly property bool hideKeyboardIndicator: identifier == "indicator-keyboard" && (AccountsService.keymaps.length < 2 || keyboardsModel.count == 0) |
2533 | - |
2534 | - height: row.height |
2535 | - expanded: root.expanded |
2536 | - selected: currentItem === this |
2537 | - |
2538 | - identifier: model.identifier |
2539 | - busName: indicatorProperties.busName |
2540 | - actionsObjectPath: indicatorProperties.actionsObjectPath |
2541 | - menuObjectPath: indicatorProperties.menuObjectPath |
2542 | - |
2543 | - opacity: hidden ? 0.0 : 1.0 |
2544 | - Behavior on opacity { |
2545 | - NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing } |
2546 | - } |
2547 | - |
2548 | - width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0 |
2549 | - |
2550 | - Behavior on width { |
2551 | - NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing } |
2552 | - } |
2553 | - |
2554 | - Component.onDestruction: { |
2555 | - // current item removed. |
2556 | - if (currentItem === this) { |
2557 | - var i = 0; |
2558 | - while (i < row.children.length) { |
2559 | - var childItem = row.children[i]; |
2560 | - if (childItem !== this) { |
2561 | - setCurrentItemIndex(i); |
2562 | - break; |
2563 | - } |
2564 | - i++; |
2565 | - } |
2566 | - } |
2567 | - } |
2568 | - } |
2569 | - } |
2570 | + Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
2571 | } |
2572 | |
2573 | Rectangle { |
2574 | @@ -244,7 +195,7 @@ |
2575 | anchors.bottom: row.bottom |
2576 | height: units.dp(2) |
2577 | color: root.hightlightColor |
2578 | - visible: currentItem !== undefined |
2579 | + visible: currentItem !== null |
2580 | opacity: 0.0 |
2581 | |
2582 | width: currentItem ? currentItem.width : 0 |
2583 | @@ -269,7 +220,7 @@ |
2584 | |
2585 | if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) { |
2586 | return 0; |
2587 | - } else if (currentItem && currentItem.ownIndex === repeater.count-1 & distanceFromCenter > 0) { |
2588 | + } else if (currentItem && currentItem.ownIndex === row.count-1 & distanceFromCenter > 0) { |
2589 | return 0; |
2590 | } |
2591 | return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1); |
2592 | |
2593 | === renamed file 'qml/Panel/IndicatorsMenu.qml' => 'qml/Panel/PanelMenu.qml' |
2594 | --- qml/Panel/IndicatorsMenu.qml 2016-06-15 14:36:15 +0000 |
2595 | +++ qml/Panel/PanelMenu.qml 2017-01-09 15:26:31 +0000 |
2596 | @@ -22,22 +22,30 @@ |
2597 | |
2598 | Showable { |
2599 | id: root |
2600 | - property alias indicatorsModel: bar.indicatorsModel |
2601 | + property alias model: bar.model |
2602 | property alias showDragHandle: __showDragHandle |
2603 | property alias hideDragHandle: __hideDragHandle |
2604 | property alias overFlowWidth: bar.overFlowWidth |
2605 | property alias verticalVelocityThreshold: yVelocityCalculator.velocityThreshold |
2606 | - property alias currentIndicator: bar.currentIndicator |
2607 | property int minimizedPanelHeight: units.gu(3) |
2608 | property int expandedPanelHeight: units.gu(7) |
2609 | property real openedHeight: units.gu(71) |
2610 | + property bool enableHint: true |
2611 | + property bool showOnClick: true |
2612 | + property color panelColor: theme.palette.normal.background |
2613 | + |
2614 | + property alias alignment: bar.alignment |
2615 | + property alias rowTitle: bar.rowTitle |
2616 | + property alias showRowTitle: bar.showRowTitle |
2617 | + property alias rowItemDelegate: bar.rowItemDelegate |
2618 | + property alias pageDelegate: content.pageDelegate |
2619 | + |
2620 | readonly property real unitProgress: Math.max(0, (height - minimizedPanelHeight) / (openedHeight - minimizedPanelHeight)) |
2621 | readonly property bool fullyOpened: unitProgress >= 1 |
2622 | readonly property bool partiallyOpened: unitProgress > 0 && unitProgress < 1.0 |
2623 | readonly property bool fullyClosed: unitProgress == 0 |
2624 | - property bool enableHint: true |
2625 | - property bool showOnClick: true |
2626 | - property color panelColor: theme.palette.normal.background |
2627 | + readonly property alias expanded: bar.expanded |
2628 | + readonly property int barWidth: Math.min(bar.width, bar.implicitWidth) |
2629 | |
2630 | signal showTapped() |
2631 | |
2632 | @@ -67,21 +75,20 @@ |
2633 | ScriptAction { script: root.height = Qt.binding( function(){ return root.minimizedPanelHeight; } ) } |
2634 | } |
2635 | |
2636 | + shown: false |
2637 | height: minimizedPanelHeight |
2638 | + clip: root.partiallyOpened |
2639 | |
2640 | onUnitProgressChanged: d.updateState() |
2641 | - clip: root.partiallyOpened |
2642 | - |
2643 | - IndicatorsLight { |
2644 | - id: indicatorLights |
2645 | - } |
2646 | |
2647 | // eater |
2648 | MouseArea { |
2649 | - anchors.fill: parent |
2650 | + anchors.fill: content |
2651 | hoverEnabled: true |
2652 | acceptedButtons: Qt.AllButtons |
2653 | onWheel: wheel.accepted = true; |
2654 | + enabled: root.state != "initial" |
2655 | + visible: content.visible |
2656 | } |
2657 | |
2658 | MenuContent { |
2659 | @@ -94,7 +101,7 @@ |
2660 | top: bar.bottom |
2661 | } |
2662 | height: openedHeight - bar.height - handle.height |
2663 | - indicatorsModel: root.indicatorsModel |
2664 | + model: root.model |
2665 | visible: root.unitProgress > 0 |
2666 | currentMenuIndex: bar.currentItemIndex |
2667 | } |
2668 | @@ -109,6 +116,7 @@ |
2669 | } |
2670 | height: units.gu(2) |
2671 | active: d.activeDragHandle ? true : false |
2672 | + visible: !root.fullyClosed |
2673 | |
2674 | //small shadow gradient at bottom of menu |
2675 | Rectangle { |
2676 | @@ -129,9 +137,10 @@ |
2677 | Rectangle { |
2678 | anchors.fill: bar |
2679 | color: panelColor |
2680 | + visible: !root.fullyClosed |
2681 | } |
2682 | |
2683 | - IndicatorsBar { |
2684 | + PanelBar { |
2685 | id: bar |
2686 | objectName: "indicatorsBar" |
2687 | |
2688 | @@ -178,12 +187,14 @@ |
2689 | |
2690 | MouseArea { |
2691 | anchors.bottom: parent.bottom |
2692 | - anchors.left: parent.left |
2693 | - anchors.right: parent.right |
2694 | + anchors.left: alignment == Qt.AlignLeft ? parent.left : undefined |
2695 | + anchors.right: alignment == Qt.AlignRight ? parent.right : undefined |
2696 | + width: root.barWidth // show handle should only cover panel items. |
2697 | height: minimizedPanelHeight |
2698 | enabled: __showDragHandle.enabled && showOnClick |
2699 | onClicked: { |
2700 | - bar.selectItemAt(mouseX) |
2701 | + var barPosition = mapToItem(bar, mouseX, mouseY); |
2702 | + bar.selectItemAt(barPosition.x) |
2703 | root.show() |
2704 | } |
2705 | } |
2706 | @@ -192,8 +203,9 @@ |
2707 | id: __showDragHandle |
2708 | objectName: "showDragHandle" |
2709 | anchors.bottom: parent.bottom |
2710 | - anchors.left: parent.left |
2711 | - anchors.right: parent.right |
2712 | + anchors.left: alignment == Qt.AlignLeft ? parent.left : undefined |
2713 | + anchors.right: alignment == Qt.AlignRight ? parent.right : undefined |
2714 | + width: root.barWidth // show handle should only cover panel items. |
2715 | height: minimizedPanelHeight |
2716 | direction: Direction.Downwards |
2717 | enabled: !root.shown && root.available |
2718 | @@ -311,7 +323,7 @@ |
2719 | script: { |
2720 | yVelocityCalculator.reset(); |
2721 | // initial item selection |
2722 | - if (!d.hasCommitted) bar.selectItemAt(d.activeDragHandle ? d.activeDragHandle.touchPosition.x : -1); |
2723 | + if (!d.hasCommitted) bar.selectItemAt(d.rowMappedLateralPosition); |
2724 | d.hasCommitted = false; |
2725 | } |
2726 | } |
2727 | |
2728 | === renamed file 'qml/Panel/IndicatorPage.qml' => 'qml/Panel/PanelMenuPage.qml' |
2729 | --- qml/Panel/IndicatorPage.qml 2016-07-27 16:23:18 +0000 |
2730 | +++ qml/Panel/PanelMenuPage.qml 2017-01-09 15:26:31 +0000 |
2731 | @@ -1,5 +1,5 @@ |
2732 | /* |
2733 | - * Copyright 2013-2014 Canonical Ltd. |
2734 | + * Copyright 2013-2016 Canonical Ltd. |
2735 | * |
2736 | * This program is free software; you can redistribute it and/or modify |
2737 | * it under the terms of the GNU Lesser General Public License as published by |
2738 | @@ -15,174 +15,211 @@ |
2739 | */ |
2740 | |
2741 | import QtQuick 2.4 |
2742 | -import Ubuntu.Components 1.3 as Components |
2743 | +import Ubuntu.Components 1.3 |
2744 | +import Ubuntu.Components.ListItems 1.3 as ListItems |
2745 | import Unity.Indicators 0.1 as Indicators |
2746 | import "../Components" |
2747 | import "Indicators" |
2748 | |
2749 | -IndicatorBase { |
2750 | - id: main |
2751 | - |
2752 | - //const |
2753 | - property string title: rootActionState.title || rootActionState.accessibleName // some indicators don't expose a title but only the accessible-desc |
2754 | - property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem |
2755 | - readonly property alias factory: _factory |
2756 | - |
2757 | - Indicators.UnityMenuModelStack { |
2758 | - id: menuStack |
2759 | - head: main.menuModel |
2760 | - |
2761 | - property var rootMenu: null |
2762 | - |
2763 | - onTailChanged: { |
2764 | - if (!tail) { |
2765 | - rootMenu = null; |
2766 | - } else if (rootMenu != tail) { |
2767 | - if (tail.get(0, "type") === rootMenuType) { |
2768 | - rootMenu = menuStack.tail.submenu(0); |
2769 | - push(rootMenu, 0); |
2770 | - } else { |
2771 | - rootMenu = null; |
2772 | - } |
2773 | - } |
2774 | - } |
2775 | - } |
2776 | +PageStack { |
2777 | + id: root |
2778 | + |
2779 | + property var submenuIndex: undefined |
2780 | + property QtObject menuModel: null |
2781 | + property Component factory |
2782 | |
2783 | Connections { |
2784 | - target: menuStack.tail |
2785 | + id: dynamicChanges |
2786 | + target: root.menuModel |
2787 | + property bool ready: false |
2788 | |
2789 | // fix async creation with signal from model before it's finished. |
2790 | - Component.onCompleted: update(); |
2791 | - onRowsInserted: update(); |
2792 | - onModelReset: update(); |
2793 | - |
2794 | - function update() { |
2795 | - if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) { |
2796 | - menuStack.rootMenu = menuStack.tail.submenu(0); |
2797 | - menuStack.push(menuStack.rootMenu, 0); |
2798 | - } |
2799 | - } |
2800 | - } |
2801 | - |
2802 | - ListView { |
2803 | - id: mainMenu |
2804 | - objectName: "mainMenu" |
2805 | - model: menuStack.rootMenu |
2806 | - |
2807 | - anchors { |
2808 | - fill: parent |
2809 | - bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - main.anchors.bottomMargin) : 0 |
2810 | - |
2811 | - Behavior on bottomMargin { |
2812 | - NumberAnimation { |
2813 | - duration: 175 |
2814 | - easing.type: Easing.OutQuad |
2815 | - } |
2816 | - } |
2817 | - // TODO - does ever frame. |
2818 | - onBottomMarginChanged: { |
2819 | - mainMenu.positionViewAtIndex(mainMenu.currentIndex, ListView.End) |
2820 | - } |
2821 | - } |
2822 | - |
2823 | - // Don't load all the delegates (only max of 3 pages worth -1/0/+1) |
2824 | - cacheBuffer: Math.max(height * 3, units.gu(70)) |
2825 | - |
2826 | - // Only allow flicking if the content doesn't fit on the page |
2827 | - interactive: contentHeight > height |
2828 | - |
2829 | - property int selectedIndex: -1 |
2830 | - property bool blockCurrentIndexChange: false |
2831 | - // for count = 0 |
2832 | - onCountChanged: { |
2833 | - if (count == 0 && selectedIndex != -1) { |
2834 | - selectedIndex = -1; |
2835 | - } |
2836 | - } |
2837 | - // for highlight following |
2838 | - onSelectedIndexChanged: { |
2839 | - if (currentIndex != selectedIndex) { |
2840 | - var blocked = blockCurrentIndexChange; |
2841 | - blockCurrentIndexChange = true; |
2842 | - |
2843 | - currentIndex = selectedIndex; |
2844 | - |
2845 | - blockCurrentIndexChange = blocked; |
2846 | - } |
2847 | - } |
2848 | - // for item addition/removal |
2849 | - onCurrentIndexChanged: { |
2850 | - if (!blockCurrentIndexChange) { |
2851 | - if (selectedIndex != -1 && selectedIndex != currentIndex) { |
2852 | - selectedIndex = currentIndex; |
2853 | - } |
2854 | - } |
2855 | - } |
2856 | - |
2857 | - Connections { |
2858 | - target: mainMenu.model ? mainMenu.model : null |
2859 | - onRowsAboutToBeRemoved: { |
2860 | - // track current item deletion. |
2861 | - if (mainMenu.selectedIndex >= first && mainMenu.selectedIndex <= last) { |
2862 | - mainMenu.selectedIndex = -1; |
2863 | - } |
2864 | - } |
2865 | - } |
2866 | - |
2867 | - delegate: Loader { |
2868 | - id: loader |
2869 | - objectName: "menuItem" + index |
2870 | - width: ListView.view.width |
2871 | - visible: status == Loader.Ready |
2872 | - |
2873 | - property int modelIndex: index |
2874 | - sourceComponent: factory.load(model, main.identifier) |
2875 | - |
2876 | - onLoaded: { |
2877 | - if (item.hasOwnProperty("selected")) { |
2878 | - item.selected = mainMenu.selectedIndex == index; |
2879 | - } |
2880 | - if (item.hasOwnProperty("menuSelected")) { |
2881 | - item.menuSelected.connect(function() { mainMenu.selectedIndex = index; }); |
2882 | - } |
2883 | - if (item.hasOwnProperty("menuDeselected")) { |
2884 | - item.menuDeselected.connect(function() { mainMenu.selectedIndex = -1; }); |
2885 | - } |
2886 | - if (item.hasOwnProperty("menuData")) { |
2887 | - item.menuData = Qt.binding(function() { return model; }); |
2888 | - } |
2889 | - if (item.hasOwnProperty("menuIndex")) { |
2890 | - item.menuIndex = Qt.binding(function() { return modelIndex; }); |
2891 | - } |
2892 | - } |
2893 | - |
2894 | - Binding { |
2895 | - target: item ? item : null |
2896 | - property: "objectName" |
2897 | - value: model.action |
2898 | - } |
2899 | - |
2900 | - // TODO: Fixes lp#1243146 |
2901 | - // This is a workaround for a Qt bug. https://bugreports.qt-project.org/browse/QTBUG-34351 |
2902 | - Connections { |
2903 | - target: mainMenu |
2904 | + onRowsInserted: { |
2905 | + if (submenuIndex !== undefined && first <= submenuIndex) { |
2906 | + reset(true); |
2907 | + } |
2908 | + } |
2909 | + onRowsRemoved: { |
2910 | + if (submenuIndex !== undefined && first <= submenuIndex) { |
2911 | + reset(true); |
2912 | + } |
2913 | + } |
2914 | + onModelReset: { |
2915 | + if (root.submenuIndex !== undefined) { |
2916 | + reset(true); |
2917 | + } |
2918 | + } |
2919 | + } |
2920 | + |
2921 | + Component.onCompleted: { |
2922 | + reset(true); |
2923 | + dynamicChanges.ready = true; |
2924 | + } |
2925 | + |
2926 | + function reset(clearModel) { |
2927 | + if (clearModel) { |
2928 | + clear(); |
2929 | + var model = submenuIndex == undefined ? menuModel : menuModel.submenu(submenuIndex) |
2930 | + if (model) { |
2931 | + push(pageComponent, { "menuModel": model }); |
2932 | + } |
2933 | + } else { |
2934 | + root.currentPage.reset(); |
2935 | + } |
2936 | + } |
2937 | + |
2938 | + Component { |
2939 | + id: pageComponent |
2940 | + Page { |
2941 | + id: page |
2942 | + |
2943 | + property alias menuModel: listView.model |
2944 | + property alias title: backLabel.title |
2945 | + property bool isSubmenu: false |
2946 | + |
2947 | + function reset() { |
2948 | + listView.positionViewAtBeginning(); |
2949 | + } |
2950 | + |
2951 | + property QtObject factory: root.factory.createObject(page, { menuModel: page.menuModel } ) |
2952 | + |
2953 | + header: PageHeader { |
2954 | + id: backLabel |
2955 | + visible: page.isSubmenu |
2956 | + leadingActionBar.actions: [ |
2957 | + Action { |
2958 | + iconName: "back" |
2959 | + text: "Back" |
2960 | + onTriggered: { |
2961 | + root.pop(); |
2962 | + } |
2963 | + } |
2964 | + ] |
2965 | + } |
2966 | + |
2967 | + ListView { |
2968 | + id: listView |
2969 | + objectName: "listView" |
2970 | + |
2971 | + anchors { |
2972 | + top: page.isSubmenu ? backLabel.bottom : parent.top |
2973 | + left: parent.left |
2974 | + right: parent.right |
2975 | + bottom: parent.bottom |
2976 | + bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - root.anchors.bottomMargin) : 0 |
2977 | + |
2978 | + Behavior on bottomMargin { |
2979 | + NumberAnimation { |
2980 | + duration: 175 |
2981 | + easing.type: Easing.OutQuad |
2982 | + } |
2983 | + } |
2984 | + // TODO - does ever frame. |
2985 | + onBottomMarginChanged: { |
2986 | + listView.positionViewAtIndex(listView.currentIndex, ListView.End) |
2987 | + } |
2988 | + } |
2989 | + |
2990 | + // Don't load all the delegates (only max of 3 pages worth -1/0/+1) |
2991 | + cacheBuffer: Math.max(height * 3, units.gu(70)) |
2992 | + |
2993 | + // Only allow flicking if the content doesn't fit on the page |
2994 | + interactive: contentHeight > height |
2995 | + |
2996 | + property int selectedIndex: -1 |
2997 | + property bool blockCurrentIndexChange: false |
2998 | + // for count = 0 |
2999 | + onCountChanged: { |
3000 | + if (count == 0 && selectedIndex != -1) { |
3001 | + selectedIndex = -1; |
3002 | + } |
3003 | + } |
3004 | + // for highlight following |
3005 | onSelectedIndexChanged: { |
3006 | - if (loader.item && loader.item.hasOwnProperty("selected")) { |
3007 | - loader.item.selected = mainMenu.selectedIndex == index; |
3008 | + if (currentIndex != selectedIndex) { |
3009 | + var blocked = blockCurrentIndexChange; |
3010 | + blockCurrentIndexChange = true; |
3011 | + |
3012 | + currentIndex = selectedIndex; |
3013 | + |
3014 | + blockCurrentIndexChange = blocked; |
3015 | + } |
3016 | + } |
3017 | + // for item addition/removal |
3018 | + onCurrentIndexChanged: { |
3019 | + if (!blockCurrentIndexChange) { |
3020 | + if (selectedIndex != -1 && selectedIndex != currentIndex) { |
3021 | + selectedIndex = currentIndex; |
3022 | + } |
3023 | + } |
3024 | + } |
3025 | + |
3026 | + Connections { |
3027 | + target: listView.model ? listView.model : null |
3028 | + onRowsAboutToBeRemoved: { |
3029 | + // track current item deletion. |
3030 | + if (listView.selectedIndex >= first && listView.selectedIndex <= last) { |
3031 | + listView.selectedIndex = -1; |
3032 | + } |
3033 | + } |
3034 | + } |
3035 | + |
3036 | + delegate: Loader { |
3037 | + id: loader |
3038 | + objectName: "menuItem" + index |
3039 | + width: ListView.view.width |
3040 | + visible: status == Loader.Ready |
3041 | + |
3042 | + property int modelIndex: index |
3043 | + sourceComponent: page.factory.load(model) |
3044 | + |
3045 | + onLoaded: { |
3046 | + if (item.hasOwnProperty("selected")) { |
3047 | + item.selected = listView.selectedIndex == index; |
3048 | + } |
3049 | + if (item.hasOwnProperty("menuSelected")) { |
3050 | + item.menuSelected.connect(function() { listView.selectedIndex = index; }); |
3051 | + } |
3052 | + if (item.hasOwnProperty("menuDeselected")) { |
3053 | + item.menuDeselected.connect(function() { listView.selectedIndex = -1; }); |
3054 | + } |
3055 | + if (item.hasOwnProperty("menuData")) { |
3056 | + item.menuData = Qt.binding(function() { return model; }); |
3057 | + } |
3058 | + if (item.hasOwnProperty("menuIndex")) { |
3059 | + item.menuIndex = Qt.binding(function() { return modelIndex; }); |
3060 | + } |
3061 | + if (item.hasOwnProperty("clicked")) { |
3062 | + item.clicked.connect(function() { |
3063 | + if (model.hasSubmenu) { |
3064 | + root.push(pageComponent, { |
3065 | + "isSubmenu": true, |
3066 | + "title": model.label.replace(/_|&/, ""), |
3067 | + "menuModel": page.menuModel.submenu(modelIndex) |
3068 | + }); |
3069 | + } |
3070 | + }); |
3071 | + } |
3072 | + } |
3073 | + |
3074 | + Binding { |
3075 | + target: item ? item : null |
3076 | + property: "objectName" |
3077 | + value: model.action |
3078 | + } |
3079 | + |
3080 | + // TODO: Fixes lp#1243146 |
3081 | + // This is a workaround for a Qt bug. https://bugreports.qt-project.org/browse/QTBUG-34351 |
3082 | + Connections { |
3083 | + target: listView |
3084 | + onSelectedIndexChanged: { |
3085 | + if (loader.item && loader.item.hasOwnProperty("selected")) { |
3086 | + loader.item.selected = listView.selectedIndex == index; |
3087 | + } |
3088 | + } |
3089 | } |
3090 | } |
3091 | } |
3092 | } |
3093 | } |
3094 | - |
3095 | - MenuItemFactory { |
3096 | - id: _factory |
3097 | - rootModel: main.menuModel ? main.menuModel : null |
3098 | - menuModel: mainMenu.model ? mainMenu.model : null |
3099 | - } |
3100 | - |
3101 | - function reset() |
3102 | - { |
3103 | - mainMenu.positionViewAtBeginning(); |
3104 | - } |
3105 | } |
3106 | |
3107 | === modified file 'qml/Rotation/RotationStates.qml' |
3108 | --- qml/Rotation/RotationStates.qml 2016-09-22 10:16:28 +0000 |
3109 | +++ qml/Rotation/RotationStates.qml 2017-01-09 15:26:31 +0000 |
3110 | @@ -273,13 +273,13 @@ |
3111 | } } |
3112 | NumberAnimation { |
3113 | duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing |
3114 | - target: root.shell; property: "indicatorAreaShowProgress" |
3115 | + target: root.shell; property: "panelAreaShowProgress" |
3116 | from: 1.0; to: 0.0 |
3117 | } |
3118 | ImmediateRotationAction { info: d; shell: root.shell } |
3119 | NumberAnimation { |
3120 | duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing |
3121 | - target: root.shell; property: "indicatorAreaShowProgress" |
3122 | + target: root.shell; property: "panelAreaShowProgress" |
3123 | from: 0.0; to: 1.0 |
3124 | } |
3125 | ScriptAction { script: { |
3126 | |
3127 | === modified file 'qml/Shell.qml' |
3128 | --- qml/Shell.qml 2016-12-12 16:45:09 +0000 |
3129 | +++ qml/Shell.qml 2017-01-09 15:26:31 +0000 |
3130 | @@ -57,7 +57,7 @@ |
3131 | property Orientations orientations |
3132 | property real nativeWidth |
3133 | property real nativeHeight |
3134 | - property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress |
3135 | + property alias panelAreaShowProgress: panel.panelAreaShowProgress |
3136 | property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop" |
3137 | property string mode: "full-greeter" |
3138 | property alias oskEnabled: inputMethod.enabled |
3139 | @@ -313,6 +313,11 @@ |
3140 | altTabPressed: physicalKeysMapper.altTabPressed |
3141 | oskEnabled: shell.oskEnabled |
3142 | spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown)) |
3143 | + |
3144 | + onSpreadShownChanged: { |
3145 | + panel.indicators.hide(); |
3146 | + panel.applicationMenus.hide(); |
3147 | + } |
3148 | } |
3149 | } |
3150 | |
3151 | @@ -345,7 +350,7 @@ |
3152 | Greeter { |
3153 | |
3154 | enabled: panel.indicators.fullyClosed // hides OSK when panel is open |
3155 | - hides: [launcher, panel.indicators] |
3156 | + hides: [launcher, panel.indicators, panel.applicationMenus] |
3157 | tabletMode: shell.usageScenario != "phone" |
3158 | forcedUnlock: wizard.active || shell.mode === "full-shell" |
3159 | background: wallpaperResolver.cachedBackground |
3160 | @@ -445,6 +450,13 @@ |
3161 | id: panel |
3162 | objectName: "panel" |
3163 | anchors.fill: parent //because this draws indicator menus |
3164 | + |
3165 | + mode: shell.usageScenario == "desktop" ? "windowed" : "staged" |
3166 | + minimizedPanelHeight: units.gu(3) |
3167 | + expandedPanelHeight: units.gu(7) |
3168 | + indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width |
3169 | + applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width |
3170 | + |
3171 | indicators { |
3172 | hides: [launcher] |
3173 | available: tutorial.panelEnabled |
3174 | @@ -452,12 +464,8 @@ |
3175 | && (!greeter || !greeter.hasLockedApp) |
3176 | && !shell.waitingOnGreeter |
3177 | && settings.enableIndicatorMenu |
3178 | - width: parent.width > units.gu(60) ? units.gu(40) : parent.width |
3179 | - |
3180 | - minimizedPanelHeight: units.gu(3) |
3181 | - expandedPanelHeight: units.gu(7) |
3182 | - |
3183 | - indicatorsModel: Indicators.IndicatorsModel { |
3184 | + |
3185 | + model: Indicators.IndicatorsModel { |
3186 | // tablet and phone both use the same profile |
3187 | // FIXME: use just "phone" for greeter too, but first fix |
3188 | // greeter app launching to either load the app inside the |
3189 | @@ -469,8 +477,10 @@ |
3190 | } |
3191 | } |
3192 | |
3193 | - callHint { |
3194 | - greeterShown: greeter.shown |
3195 | + applicationMenus { |
3196 | + hides: [launcher] |
3197 | + available: (!greeter || !greeter.shown) |
3198 | + && !shell.waitingOnGreeter |
3199 | } |
3200 | |
3201 | readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow |
3202 | @@ -478,7 +488,7 @@ |
3203 | : false |
3204 | fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0) |
3205 | || greeter.hasLockedApp |
3206 | - locked: greeter && greeter.active |
3207 | + greeterShown: greeter && greeter.shown |
3208 | } |
3209 | |
3210 | Launcher { |
3211 | |
3212 | === modified file 'qml/Stage/DecoratedWindow.qml' |
3213 | --- qml/Stage/DecoratedWindow.qml 2016-11-10 13:05:29 +0000 |
3214 | +++ qml/Stage/DecoratedWindow.qml 2017-01-09 15:26:31 +0000 |
3215 | @@ -18,6 +18,9 @@ |
3216 | import Ubuntu.Components 1.3 |
3217 | import Unity.Application 0.1 |
3218 | import "Spread/MathUtils.js" as MathUtils |
3219 | +import Unity.ApplicationMenu 0.1 |
3220 | +import Unity.Indicators 0.1 as Indicators |
3221 | +import "../Components/PanelState" |
3222 | |
3223 | FocusScope { |
3224 | id: root |
3225 | @@ -147,46 +150,12 @@ |
3226 | opacity: root.shadowOpacity |
3227 | } |
3228 | |
3229 | - WindowDecoration { |
3230 | - id: decoration |
3231 | - closeButtonVisible: root.application.appId !== "unity8-dash" |
3232 | - objectName: "appWindowDecoration" |
3233 | - anchors { left: parent.left; top: parent.top; right: parent.right } |
3234 | - height: units.gu(3) |
3235 | - width: root.width |
3236 | - title: applicationWindow.title |
3237 | - opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0 |
3238 | - |
3239 | - Behavior on opacity { UbuntuNumberAnimation { } } |
3240 | - |
3241 | - onCloseClicked: root.closeClicked(); |
3242 | - onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); } |
3243 | - onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); } |
3244 | - onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); } |
3245 | - onMinimizeClicked: root.minimizeClicked(); |
3246 | - onPressed: root.decorationPressed(); |
3247 | - |
3248 | - onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY) |
3249 | - onPositionChanged: moveHandler.handlePositionChanged(mouse) |
3250 | - onReleased: { |
3251 | - root.decorationReleased(); |
3252 | - moveHandler.handleReleased(); |
3253 | - } |
3254 | - } |
3255 | - |
3256 | - MoveHandler { |
3257 | - id: moveHandler |
3258 | - objectName: "moveHandler" |
3259 | - target: root.parent |
3260 | - buttonsWidth: decoration.buttonsWidth |
3261 | - } |
3262 | - |
3263 | ApplicationWindow { |
3264 | id: applicationWindow |
3265 | objectName: "appWindow" |
3266 | - anchors.left: parent.left |
3267 | anchors.top: parent.top |
3268 | anchors.topMargin: root.decorationHeight * Math.min(1, root.showDecoration) |
3269 | + anchors.left: parent.left |
3270 | width: implicitWidth |
3271 | height: implicitHeight |
3272 | requestedHeight: !counterRotate ? root.requestedHeight - d.requestedDecorationHeight : root.requestedWidth |
3273 | @@ -228,6 +197,72 @@ |
3274 | } |
3275 | |
3276 | MouseArea { |
3277 | + anchors { left: parent.left; top: parent.top; right: parent.right } |
3278 | + height: units.gu(3) |
3279 | + |
3280 | + opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0 |
3281 | + |
3282 | + Behavior on opacity { UbuntuNumberAnimation { } } |
3283 | + |
3284 | + drag.target: Item {} |
3285 | + drag.filterChildren: true |
3286 | + drag.threshold: 0 |
3287 | + |
3288 | + onPressed: root.decorationPressed(); |
3289 | + onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY) |
3290 | + onPositionChanged: moveHandler.handlePositionChanged(mouse) |
3291 | + onReleased: { |
3292 | + root.decorationReleased(); |
3293 | + moveHandler.handleReleased(); |
3294 | + } |
3295 | + |
3296 | + WindowDecoration { |
3297 | + id: decoration |
3298 | + closeButtonVisible: root.application.appId !== "unity8-dash" |
3299 | + objectName: "appWindowDecoration" |
3300 | + anchors.fill: parent |
3301 | + title: applicationWindow.title |
3302 | + |
3303 | + onCloseClicked: root.closeClicked(); |
3304 | + onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); } |
3305 | + onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); } |
3306 | + onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); } |
3307 | + onMinimizeClicked: root.minimizeClicked(); |
3308 | + |
3309 | + enableMenus: { |
3310 | + return active && |
3311 | + surface && |
3312 | + (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.decorationsVisible) |
3313 | + } |
3314 | + menu: sharedAppModel.model |
3315 | + |
3316 | + Indicators.SharedUnityMenuModel { |
3317 | + id: sharedAppModel |
3318 | + property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] |
3319 | + property var menuService: menus.length > 0 ? menus[0] : undefined |
3320 | + |
3321 | + busName: menuService ? menuService.service : "" |
3322 | + menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : "" |
3323 | + actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {} |
3324 | + } |
3325 | + |
3326 | + Connections { |
3327 | + target: ApplicationMenuRegistry |
3328 | + onSurfaceMenuRegistered: { |
3329 | + if (surface && surfaceId === surface.persistentId) { |
3330 | + sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] }); |
3331 | + } |
3332 | + } |
3333 | + onSurfaceMenuUnregistered: { |
3334 | + if (surface && surfaceId === surface.persistentId) { |
3335 | + sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] }); |
3336 | + } |
3337 | + } |
3338 | + } |
3339 | + } |
3340 | + } |
3341 | + |
3342 | + MouseArea { |
3343 | anchors.fill: applicationWindow |
3344 | acceptedButtons: Qt.LeftButton |
3345 | property bool dragging: false |
3346 | @@ -257,6 +292,13 @@ |
3347 | } |
3348 | } |
3349 | |
3350 | + MoveHandler { |
3351 | + id: moveHandler |
3352 | + objectName: "moveHandler" |
3353 | + target: root.parent |
3354 | + buttonsWidth: decoration.buttonsWidth |
3355 | + } |
3356 | + |
3357 | Rectangle { |
3358 | anchors.fill: parent |
3359 | color: "black" |
3360 | |
3361 | === modified file 'qml/Stage/Stage.qml' |
3362 | --- qml/Stage/Stage.qml 2016-12-12 16:45:09 +0000 |
3363 | +++ qml/Stage/Stage.qml 2017-01-09 15:26:31 +0000 |
3364 | @@ -356,7 +356,7 @@ |
3365 | |
3366 | Binding { |
3367 | target: PanelState |
3368 | - property: "buttonsVisible" |
3369 | + property: "decorationsVisible" |
3370 | value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus |
3371 | } |
3372 | |
3373 | @@ -377,6 +377,20 @@ |
3374 | |
3375 | Binding { |
3376 | target: PanelState |
3377 | + property: "focusedPersistentSurfaceId" |
3378 | + value: { |
3379 | + if (priv.focusedAppDelegate !== null) { |
3380 | + if (priv.focusedAppDelegate.surface) { |
3381 | + return priv.focusedAppDelegate.surface.persistentId; |
3382 | + } |
3383 | + } |
3384 | + return ""; |
3385 | + } |
3386 | + when: priv.focusedAppDelegate |
3387 | + } |
3388 | + |
3389 | + Binding { |
3390 | + target: PanelState |
3391 | property: "dropShadow" |
3392 | value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed" |
3393 | } |
3394 | @@ -389,7 +403,7 @@ |
3395 | |
3396 | Component.onDestruction: { |
3397 | PanelState.title = ""; |
3398 | - PanelState.buttonsVisible = false; |
3399 | + PanelState.decorationsVisible = false; |
3400 | PanelState.dropShadow = false; |
3401 | } |
3402 | |
3403 | @@ -1492,7 +1506,7 @@ |
3404 | |
3405 | Binding { |
3406 | target: PanelState |
3407 | - property: "buttonsAlwaysVisible" |
3408 | + property: "decorationsAlwaysVisible" |
3409 | value: appDelegate && appDelegate.maximized && touchControls.overlayShown |
3410 | } |
3411 | |
3412 | |
3413 | === modified file 'qml/Stage/WindowDecoration.qml' |
3414 | --- qml/Stage/WindowDecoration.qml 2016-11-29 09:38:44 +0000 |
3415 | +++ qml/Stage/WindowDecoration.qml 2017-01-09 15:26:31 +0000 |
3416 | @@ -15,18 +15,23 @@ |
3417 | */ |
3418 | |
3419 | import QtQuick 2.4 |
3420 | +import QtQuick.Layouts 1.1 |
3421 | import Ubuntu.Components 1.3 |
3422 | +import Unity.Application 0.1 |
3423 | import "../Components" |
3424 | +import "../Components/PanelState" |
3425 | +import "../ApplicationMenus" |
3426 | |
3427 | MouseArea { |
3428 | id: root |
3429 | - clip: true |
3430 | |
3431 | property alias closeButtonVisible: buttons.closeButtonShown |
3432 | property alias title: titleLabel.text |
3433 | property alias maximizeButtonShown: buttons.maximizeButtonShown |
3434 | property bool active: false |
3435 | property alias overlayShown: buttons.overlayShown |
3436 | + property var menu: undefined |
3437 | + property bool enableMenus: true |
3438 | |
3439 | readonly property real buttonsWidth: buttons.width + row.spacing |
3440 | |
3441 | @@ -39,6 +44,7 @@ |
3442 | signal maximizeHorizontallyClicked() |
3443 | signal maximizeVerticallyClicked() |
3444 | |
3445 | + onClicked: mouse.accepted = true // propogated event |
3446 | onDoubleClicked: { |
3447 | if (mouse.button == Qt.LeftButton) { |
3448 | root.maximizeClicked(); |
3449 | @@ -48,21 +54,46 @@ |
3450 | // do not let unhandled wheel event pass thru the decoration |
3451 | onWheel: wheel.accepted = true; |
3452 | |
3453 | + QtObject { |
3454 | + id: priv |
3455 | + property var menuBar: menuBarLoader.item |
3456 | + |
3457 | + property bool shouldShowMenus: root.enableMenus && |
3458 | + menuBar && |
3459 | + menuBar.valid && |
3460 | + (menuBar.showRequested || root.containsMouse) |
3461 | + } |
3462 | + |
3463 | + // We dont want touch events to fall through to parent, |
3464 | + // otherwise the containsMouse will not work. |
3465 | + MouseArea { |
3466 | + anchors.fill: parent |
3467 | + propagateComposedEvents: true |
3468 | + } |
3469 | + |
3470 | Rectangle { |
3471 | + id: background |
3472 | anchors.fill: parent |
3473 | - anchors.bottomMargin: -radius |
3474 | radius: units.gu(.5) |
3475 | color: theme.palette.normal.background |
3476 | } |
3477 | |
3478 | - Row { |
3479 | + Rectangle { |
3480 | + anchors { |
3481 | + bottom: background.bottom |
3482 | + left: parent.left |
3483 | + right: parent.right |
3484 | + } |
3485 | + height: background.radius |
3486 | + color: theme.palette.normal.background |
3487 | + } |
3488 | + |
3489 | + RowLayout { |
3490 | id: row |
3491 | anchors { |
3492 | fill: parent |
3493 | leftMargin: overlayShown ? units.gu(5) : units.gu(1) |
3494 | rightMargin: units.gu(1) |
3495 | - topMargin: units.gu(0.5) |
3496 | - bottomMargin: units.gu(0.5) |
3497 | } |
3498 | Behavior on anchors.leftMargin { |
3499 | UbuntuNumberAnimation {} |
3500 | @@ -72,7 +103,12 @@ |
3501 | |
3502 | WindowControlButtons { |
3503 | id: buttons |
3504 | - height: parent.height |
3505 | + anchors { |
3506 | + top: parent.top |
3507 | + bottom: parent.bottom |
3508 | + topMargin: units.gu(0.5) |
3509 | + bottomMargin: units.gu(0.5) |
3510 | + } |
3511 | active: root.active |
3512 | onCloseClicked: root.closeClicked(); |
3513 | onMinimizeClicked: root.minimizeClicked(); |
3514 | @@ -81,19 +117,44 @@ |
3515 | onMaximizeVerticallyClicked: root.maximizeVerticallyClicked(); |
3516 | } |
3517 | |
3518 | - Label { |
3519 | - id: titleLabel |
3520 | - objectName: "windowDecorationTitle" |
3521 | - color: root.active ? "white" : UbuntuColors.slate |
3522 | - height: parent.height |
3523 | - width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin |
3524 | - verticalAlignment: Text.AlignVCenter |
3525 | - fontSize: "medium" |
3526 | - font.weight: root.active ? Font.Light : Font.Medium |
3527 | - elide: Text.ElideRight |
3528 | - opacity: overlayShown ? 0 : 1 |
3529 | - visible: opacity != 0 |
3530 | - Behavior on opacity { UbuntuNumberAnimation {} } |
3531 | + Item { |
3532 | + Layout.preferredHeight: parent.height |
3533 | + Layout.fillWidth: true |
3534 | + |
3535 | + Label { |
3536 | + id: titleLabel |
3537 | + objectName: "windowDecorationTitle" |
3538 | + color: root.active ? "white" : UbuntuColors.slate |
3539 | + height: parent.height |
3540 | + width: parent.width |
3541 | + verticalAlignment: Text.AlignVCenter |
3542 | + fontSize: "medium" |
3543 | + font.weight: root.active ? Font.Light : Font.Medium |
3544 | + elide: Text.ElideRight |
3545 | + opacity: overlayShown || priv.shouldShowMenus ? 0 : 1 |
3546 | + visible: opacity != 0 |
3547 | + Behavior on opacity { UbuntuNumberAnimation {} } |
3548 | + } |
3549 | + |
3550 | + Loader { |
3551 | + id: menuBarLoader |
3552 | + objectName: "menuBarLoader" |
3553 | + anchors.bottom: parent.bottom |
3554 | + height: parent.height |
3555 | + width: parent.width |
3556 | + active: root.menu !== undefined |
3557 | + |
3558 | + sourceComponent: MenuBar { |
3559 | + id: menuBar |
3560 | + height: menuBarLoader.height |
3561 | + enableKeyFilter: valid && root.active && root.enableMenus |
3562 | + unityMenuModel: root.menu |
3563 | + } |
3564 | + |
3565 | + opacity: !overlayShown && priv.shouldShowMenus ? 1 : 0 |
3566 | + visible: opacity == 1 |
3567 | + Behavior on opacity { UbuntuNumberAnimation {} } |
3568 | + } |
3569 | } |
3570 | } |
3571 | } |
3572 | |
3573 | === modified file 'src/main.cpp' |
3574 | --- src/main.cpp 2016-10-27 00:05:28 +0000 |
3575 | +++ src/main.cpp 2017-01-09 15:26:31 +0000 |
3576 | @@ -27,8 +27,8 @@ |
3577 | { |
3578 | qSetMessagePattern("[%{time yyyy-MM-dd:hh:mm:ss.zzz}] %{if-category}%{category}: %{endif}%{message}"); |
3579 | |
3580 | - bool isMirServer = false; |
3581 | - if (qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") { |
3582 | + bool isMirServer = qgetenv("QT_QPA_PLATFORM") == "mirserver"; |
3583 | + if (!isMirServer && qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") { |
3584 | setenv("QT_QPA_PLATFORM", "mirserver", 1 /* overwrite */); |
3585 | isMirServer = true; |
3586 | } |
3587 | |
3588 | === modified file 'tests/mocks/QMenuModel/unitymenumodel.cpp' |
3589 | --- tests/mocks/QMenuModel/unitymenumodel.cpp 2014-10-07 10:28:59 +0000 |
3590 | +++ tests/mocks/QMenuModel/unitymenumodel.cpp 2017-01-09 15:26:31 +0000 |
3591 | @@ -29,7 +29,9 @@ |
3592 | ActionStateRole, |
3593 | IsCheckRole, |
3594 | IsRadioRole, |
3595 | - IsToggledRole |
3596 | + IsToggledRole, |
3597 | + ShortcutRole, |
3598 | + HasSubmenuRole |
3599 | }; |
3600 | |
3601 | UnityMenuModel::UnityMenuModel(QObject *parent) |
3602 | @@ -141,7 +143,29 @@ |
3603 | |
3604 | QVariant UnityMenuModel::data(const QModelIndex &index, int role) const |
3605 | { |
3606 | - return rowData(index.row())[roleNames()[role]]; |
3607 | + QVariantMap v = rowData(index.row()); |
3608 | + QString roleName = roleNames()[role]; |
3609 | + |
3610 | + if (v.contains(roleName)) return v[roleName]; |
3611 | + |
3612 | + // defaults |
3613 | + switch (role) { |
3614 | + case LabelRole: return QString(); |
3615 | + case SensitiveRole: return true; |
3616 | + case IsSeparatorRole: return false; |
3617 | + case IconRole: return QString(); |
3618 | + case TypeRole: return QString(); |
3619 | + case ExtendedAttributesRole: return QVariantMap(); |
3620 | + case ActionRole: return QString(); |
3621 | + case ActionStateRole: return QVariant(); |
3622 | + case IsCheckRole: return false; |
3623 | + case IsRadioRole: return false; |
3624 | + case IsToggledRole: return false; |
3625 | + case ShortcutRole: return QString(); |
3626 | + case HasSubmenuRole: return subMenuData(index.row()).isValid(); |
3627 | + default: break; |
3628 | + } |
3629 | + return QVariant(); |
3630 | } |
3631 | |
3632 | QVariantMap UnityMenuModel::rowData(int row) const |
3633 | @@ -149,12 +173,14 @@ |
3634 | if (m_modelData.count() <= row) { |
3635 | return QVariantMap(); |
3636 | } |
3637 | - return m_modelData[row].toMap()["rowData"].toMap(); |
3638 | + QVariantMap vRow = m_modelData.value(row, QVariantMap()).toMap(); |
3639 | + return vRow["rowData"].toMap(); |
3640 | } |
3641 | |
3642 | QVariant UnityMenuModel::subMenuData(int row) const |
3643 | { |
3644 | - return m_modelData[row].toMap()["submenu"]; |
3645 | + QVariantMap v = m_modelData.value(row, QVariantMap()).toMap(); |
3646 | + return v.value("submenu", QVariant()); |
3647 | } |
3648 | |
3649 | QModelIndex UnityMenuModel::index(int row, int column, const QModelIndex&) const |
3650 | @@ -182,6 +208,8 @@ |
3651 | names[IsCheckRole] = "isCheck"; |
3652 | names[IsRadioRole] = "isRadio"; |
3653 | names[IsToggledRole] = "isToggled"; |
3654 | + names[ShortcutRole] = "shortcut"; |
3655 | + names[HasSubmenuRole] = "hasSubmenu"; |
3656 | |
3657 | return names; |
3658 | } |
3659 | @@ -201,6 +229,7 @@ |
3660 | UnityMenuModel*& model = submenus[position]; |
3661 | if (!model) { |
3662 | model = new UnityMenuModel(this); |
3663 | + connect(model, &UnityMenuModel::activated, this, &UnityMenuModel::activated); |
3664 | } |
3665 | if (model->modelData() != submenuData) { |
3666 | model->setModelData(submenuData); |
3667 | @@ -225,11 +254,23 @@ |
3668 | roles.insert(names[role], role); |
3669 | } |
3670 | |
3671 | - return this->data(this->index(row, 0), roles[role]); |
3672 | + return data(index(row, 0), roles[role]); |
3673 | } |
3674 | |
3675 | -void UnityMenuModel::activate(int, const QVariant&) |
3676 | +void UnityMenuModel::activate(int row, const QVariant&) |
3677 | { |
3678 | + QVariantMap vModelData = m_modelData.value(row, QVariantMap()).toMap(); |
3679 | + QVariantMap rd = vModelData["rowData"].toMap(); |
3680 | + |
3681 | + bool isCheckable = rd[roleNames()[IsCheckRole]].toBool() || rd[roleNames()[IsRadioRole]].toBool(); |
3682 | + if (isCheckable) { |
3683 | + rd[roleNames()[IsToggledRole]] = !rd[roleNames()[IsToggledRole]].toBool(); |
3684 | + vModelData["rowData"] = rd; |
3685 | + m_modelData[row] = vModelData; |
3686 | + |
3687 | + dataChanged(index(row, 0), index(row, 0), QVector<int>() << IsToggledRole); |
3688 | + } |
3689 | + Q_EMIT activated(rd[roleNames()[ActionRole]].toString()); |
3690 | } |
3691 | |
3692 | void UnityMenuModel::changeState(int, const QVariant&) |
3693 | |
3694 | === modified file 'tests/mocks/QMenuModel/unitymenumodel.h' |
3695 | --- tests/mocks/QMenuModel/unitymenumodel.h 2015-04-30 09:31:51 +0000 |
3696 | +++ tests/mocks/QMenuModel/unitymenumodel.h 2017-01-09 15:26:31 +0000 |
3697 | @@ -61,7 +61,7 @@ |
3698 | |
3699 | QString nameOwner() const; |
3700 | |
3701 | - int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
3702 | + Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
3703 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; |
3704 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
3705 | QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; |
3706 | @@ -88,6 +88,8 @@ |
3707 | // Internal mock usage |
3708 | void modelDataChanged(); |
3709 | |
3710 | + void activated(const QString& action); |
3711 | + |
3712 | private: |
3713 | QVariantMap rowData(int row) const; |
3714 | QVariant subMenuData(int row) const; |
3715 | |
3716 | === modified file 'tests/mocks/Unity/Application/SurfaceManager.cpp' |
3717 | --- tests/mocks/Unity/Application/SurfaceManager.cpp 2016-12-03 18:41:45 +0000 |
3718 | +++ tests/mocks/Unity/Application/SurfaceManager.cpp 2017-01-09 15:26:31 +0000 |
3719 | @@ -83,9 +83,11 @@ |
3720 | this->onStateRequested(surface, state); |
3721 | }); |
3722 | |
3723 | + const QString persistentId = surface->persistentId(); |
3724 | connect(surface, &QObject::destroyed, this, [=]() { |
3725 | - this->onSurfaceDestroyed(surface); |
3726 | + this->onSurfaceDestroyed(surface, persistentId); |
3727 | }); |
3728 | + |
3729 | } |
3730 | |
3731 | void SurfaceManager::notifySurfaceCreated(unityapi::MirSurfaceInterface *surface) |
3732 | @@ -223,7 +225,7 @@ |
3733 | DEBUG_MSG("("<<surface<<","<<state<<") ended"); |
3734 | } |
3735 | |
3736 | -void SurfaceManager::onSurfaceDestroyed(MirSurface *surface) |
3737 | +void SurfaceManager::onSurfaceDestroyed(MirSurface *surface, const QString& persistentId) |
3738 | { |
3739 | m_surfaces.removeAll(surface); |
3740 | if (m_focusedSurface == surface) { |
3741 | @@ -237,6 +239,7 @@ |
3742 | m_underModification = false; |
3743 | Q_EMIT modificationsEnded(); |
3744 | } |
3745 | + Q_EMIT surfaceDestroyed(persistentId); |
3746 | } |
3747 | |
3748 | void SurfaceManager::focusFirstAvailableSurface() |
3749 | |
3750 | === modified file 'tests/mocks/Unity/Application/SurfaceManager.h' |
3751 | --- tests/mocks/Unity/Application/SurfaceManager.h 2016-11-30 19:24:02 +0000 |
3752 | +++ tests/mocks/Unity/Application/SurfaceManager.h 2017-01-09 15:26:31 +0000 |
3753 | @@ -76,6 +76,8 @@ |
3754 | void createInputMethodSurface(); |
3755 | |
3756 | Q_SIGNALS: |
3757 | + void surfaceDestroyed(const QString& persistentSurfaceId); |
3758 | + |
3759 | void newSurfaceMinimumWidthChanged(int value); |
3760 | void newSurfaceMaximumWidthChanged(int value); |
3761 | void newSurfaceMinimumHeightChanged(int value); |
3762 | @@ -85,7 +87,7 @@ |
3763 | |
3764 | private Q_SLOTS: |
3765 | void onStateRequested(MirSurface *surface, Mir::State state); |
3766 | - void onSurfaceDestroyed(MirSurface *surface); |
3767 | + void onSurfaceDestroyed(MirSurface *surface, const QString& persistentId); |
3768 | |
3769 | private: |
3770 | void doRaise(unity::shell::application::MirSurfaceInterface *surface); |
3771 | |
3772 | === modified file 'tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cpp' |
3773 | --- tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cpp 2017-01-09 15:26:30 +0000 |
3774 | +++ tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.cpp 2017-01-09 15:26:31 +0000 |
3775 | @@ -40,3 +40,8 @@ |
3776 | QDBusObjectPath(actionObjectPath), |
3777 | service); |
3778 | } |
3779 | + |
3780 | +void MockApplicationMenuRegistry::UnregisterSurfaceMenu(const QString &surfaceId, const QDBusObjectPath &menuObjectPath) |
3781 | +{ |
3782 | + ApplicationMenuRegistry::UnregisterSurfaceMenu(surfaceId, menuObjectPath); |
3783 | +} |
3784 | |
3785 | === modified file 'tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.h' |
3786 | --- tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.h 2017-01-09 15:26:30 +0000 |
3787 | +++ tests/mocks/Unity/ApplicationMenu/mockapplicationmenuregistry.h 2017-01-09 15:26:31 +0000 |
3788 | @@ -31,6 +31,9 @@ |
3789 | const QString &actionObjectPath, |
3790 | const QString &service); |
3791 | |
3792 | + Q_INVOKABLE void UnregisterSurfaceMenu(const QString &surfaceId, |
3793 | + const QDBusObjectPath &menuObjectPath); |
3794 | + |
3795 | private: |
3796 | MockApplicationMenuRegistry(QObject *parent = 0); |
3797 | }; |
3798 | |
3799 | === modified file 'tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js' |
3800 | --- tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js 2016-11-11 04:01:46 +0000 |
3801 | +++ tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js 2017-01-09 15:26:31 +0000 |
3802 | @@ -567,7 +567,7 @@ |
3803 | "state": "Stopped" |
3804 | }, |
3805 | "ext": {}, |
3806 | - "icon": "file:///usr/share/mediaplayer-app/mediaplayer-app.png", |
3807 | + "icon": "image://theme/mediaplayer-app", |
3808 | "isCheck": false, |
3809 | "isRadio": false, |
3810 | "isSeparator": false, |
3811 | @@ -863,7 +863,7 @@ |
3812 | "ext": { |
3813 | "xCanonicalUid": 1003 |
3814 | }, |
3815 | - "icon": "file:///usr/share/webbrowser-app/webbrowser-app.png", |
3816 | + "icon": "image://theme/webbrowser-app", |
3817 | "isCheck": false, |
3818 | "isRadio": false, |
3819 | "isSeparator": false, |
3820 | @@ -912,7 +912,7 @@ |
3821 | "ext": { |
3822 | "xCanonicalUid": 1002 |
3823 | }, |
3824 | - "icon": "file:///usr/share/webbrowser-app/webbrowser-app.png", |
3825 | + "icon": "image://theme/webbrowser-app", |
3826 | "isCheck": false, |
3827 | "isRadio": false, |
3828 | "isSeparator": false, |
3829 | @@ -929,7 +929,7 @@ |
3830 | "ext": { |
3831 | "xCanonicalUid": 1001 |
3832 | }, |
3833 | - "icon": "file:///usr/share/webbrowser-app/webbrowser-app.png", |
3834 | + "icon": "image://theme/webbrowser-app", |
3835 | "isCheck": false, |
3836 | "isRadio": false, |
3837 | "isSeparator": false, |
3838 | |
3839 | === added file 'tests/qmltests/ApplicationMenuDataLoader.qml' |
3840 | --- tests/qmltests/ApplicationMenuDataLoader.qml 1970-01-01 00:00:00 +0000 |
3841 | +++ tests/qmltests/ApplicationMenuDataLoader.qml 2017-01-09 15:26:31 +0000 |
3842 | @@ -0,0 +1,75 @@ |
3843 | +import QtQuick 2.4 |
3844 | +import Ubuntu.Components 1.3 |
3845 | +import Unity.Application 0.1 |
3846 | +import Unity.ApplicationMenu 0.1 |
3847 | +import Unity.Indicators 0.1 as Indicators |
3848 | + |
3849 | +Object { |
3850 | + |
3851 | + property alias surfaceManager: sMgrHandler.target |
3852 | + |
3853 | + Connections { |
3854 | + id: sMgrHandler |
3855 | + onSurfaceCreated: { |
3856 | + var fakeMenuPath = "/" + surface.persistentId.replace(/\W+/g, ""); |
3857 | + |
3858 | + ApplicationMenuRegistry.RegisterSurfaceMenu(surface.persistentId, fakeMenuPath, fakeMenuPath, ":1"); |
3859 | + Indicators.UnityMenuModelCache.setCachedModelData(fakeMenuPath, generateTestData(4, 3, 2, 3, "menu")); |
3860 | + } |
3861 | + onSurfaceDestroyed: { |
3862 | + ApplicationMenuRegistry.UnregisterSurfaceMenu(persistentSurfaceId, "/app"); |
3863 | + } |
3864 | + } |
3865 | + |
3866 | + function generateTestData(length, depth, submenuInterval, separatorInterval, prefix, root) { |
3867 | + var data = []; |
3868 | + if (root === undefined) root = true; |
3869 | + |
3870 | + if (prefix === undefined) prefix = "menu" |
3871 | + |
3872 | + for (var i = 0; i < length; i++) { |
3873 | + |
3874 | + var menuCode = String.fromCharCode(i+65); |
3875 | + |
3876 | + var isSeparator = !root && separatorInterval > 0 && ((i+1) % separatorInterval == 0); |
3877 | + var row = { |
3878 | + "rowData": { // 1 |
3879 | + "label": prefix + "&" + menuCode, |
3880 | + "sensitive": true, |
3881 | + "isSeparator": isSeparator, |
3882 | + "icon": "", |
3883 | + "ext": {}, |
3884 | + "action": prefix + menuCode, |
3885 | + "actionState": {}, |
3886 | + "isCheck": false, |
3887 | + "isRadio": false, |
3888 | + "isToggled": false, |
3889 | + "shortcut": "" |
3890 | + } |
3891 | + } |
3892 | + var isSubmenu = root === undefined || root === true || (submenuInterval > 0 && ((i+1) % submenuInterval == 0)); |
3893 | + if (isSubmenu && !isSeparator && depth > 1) { |
3894 | + row["submenu"] = generateTestData(length, depth-1, submenuInterval, separatorInterval, prefix + menuCode + ".", false); |
3895 | + } |
3896 | + data[i] = row; |
3897 | + } |
3898 | + return data; |
3899 | + } |
3900 | + |
3901 | + // Test Data |
3902 | + property var singleCheckable: [{ |
3903 | + "rowData": { // 1 |
3904 | + "label": "checkable1", |
3905 | + "sensitive": true, |
3906 | + "isSeparator": false, |
3907 | + "icon": "", |
3908 | + "ext": {}, |
3909 | + "action": "checkable1", |
3910 | + "actionState": {}, |
3911 | + "isCheck": true, |
3912 | + "isRadio": false, |
3913 | + "isToggled": false, |
3914 | + "shortcut": "Alt+F" |
3915 | + } |
3916 | + }] |
3917 | +} |
3918 | |
3919 | === added directory 'tests/qmltests/ApplicationMenus' |
3920 | === added file 'tests/qmltests/ApplicationMenus/tst_MenuBar.qml' |
3921 | --- tests/qmltests/ApplicationMenus/tst_MenuBar.qml 1970-01-01 00:00:00 +0000 |
3922 | +++ tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-01-09 15:26:31 +0000 |
3923 | @@ -0,0 +1,183 @@ |
3924 | +/* |
3925 | + * Copyright (C) 2016 Canonical, Ltd. |
3926 | + * |
3927 | + * This program is free software; you can redistribute it and/or modify |
3928 | + * it under the terms of the GNU General Public License as published by |
3929 | + * the Free Software Foundation; version 3. |
3930 | + * |
3931 | + * This program is distributed in the hope that it will be useful, |
3932 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3933 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3934 | + * GNU General Public License for more details. |
3935 | + * |
3936 | + * You should have received a copy of the GNU General Public License |
3937 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3938 | + */ |
3939 | + |
3940 | +import QtQuick 2.4 |
3941 | +import QtTest 1.0 |
3942 | +import Ubuntu.Components 1.3 |
3943 | +import Ubuntu.Components.ListItems 1.3 |
3944 | +import Unity.Application 0.1 |
3945 | +import QMenuModel 0.1 |
3946 | +import Unity.Test 0.1 |
3947 | +import Utils 0.1 |
3948 | + |
3949 | +import "../../../qml/ApplicationMenus" |
3950 | +import ".." |
3951 | + |
3952 | +Item { |
3953 | + id: root |
3954 | + width: units.gu(100) |
3955 | + height: units.gu(50) |
3956 | + |
3957 | + Component.onCompleted: { |
3958 | + QuickUtils.keyboardAttached = true; |
3959 | + theme.name = "Ubuntu.Components.Themes.SuruDark" |
3960 | + } |
3961 | + |
3962 | + Binding { |
3963 | + target: MouseTouchAdaptor |
3964 | + property: "enabled" |
3965 | + value: false |
3966 | + } |
3967 | + |
3968 | + SurfaceManager { id: sMgr } |
3969 | + ApplicationMenuDataLoader { |
3970 | + id: appMenuData |
3971 | + surfaceManager: sMgr |
3972 | + } |
3973 | + |
3974 | + Rectangle { |
3975 | + anchors { |
3976 | + left: parent.left |
3977 | + right: parent.right |
3978 | + top: parent.top |
3979 | + margins: units.gu(1) |
3980 | + } |
3981 | + height: units.gu(3) |
3982 | + color: "grey" |
3983 | + |
3984 | + MenuBar { |
3985 | + id: menuBar |
3986 | + anchors.fill: parent |
3987 | + enableKeyFilter: true |
3988 | + |
3989 | + unityMenuModel: UnityMenuModel { |
3990 | + id: menuBackend |
3991 | + modelData: appMenuData.generateTestData(7,5,2,3,"menu") |
3992 | + } |
3993 | + } |
3994 | + } |
3995 | + |
3996 | + SignalSpy { |
3997 | + id: activatedSpy |
3998 | + target: menuBackend |
3999 | + signalName: "activated" |
4000 | + } |
4001 | + |
4002 | + UnityTestCase { |
4003 | + id: testCase |
4004 | + name: "MenuBar" |
4005 | + when: windowShown |
4006 | + |
4007 | + function init() { |
4008 | + menuBar.dismiss(); |
4009 | + menuBackend.modelData = appMenuData.generateTestData(5,5,2,3,"menu") |
4010 | + activatedSpy.clear(); |
4011 | + } |
4012 | + |
4013 | + function test_mouseNavigation() { |
4014 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu"); |
4015 | + wait(50) // wait for row to build |
4016 | + var priv = findInvisibleChild(menuBar, "d"); |
4017 | + |
4018 | + var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); |
4019 | + var menuItem1 = findChild(menuBar, "menuBar-item1"); verify(menuItem1); |
4020 | + var menuItem2 = findChild(menuBar, "menuBar-item2"); verify(menuItem2); |
4021 | + |
4022 | + menuItem0.show(); |
4023 | + compare(priv.currentItem, menuItem0, "CurrentItem should be set to item 0"); |
4024 | + compare(priv.currentItem.popupVisible, true, "Popup should be visible"); |
4025 | + |
4026 | + mouseMove(menuItem1, menuItem1.width/2, menuItem1.height/2); |
4027 | + tryCompare(priv, "currentItem", menuItem1, undefined, "CurrentItem should have moved to item 1"); |
4028 | + compare(menuItem1.popupVisible, true, "Popup should be visible"); |
4029 | + |
4030 | + mouseMove(menuItem2, menuItem2.width/2, menuItem2.height/2); |
4031 | + tryCompare(priv, "currentItem", menuItem2, undefined, "CurrentItem should have moved to item 2"); |
4032 | + compare(menuItem2.popupVisible, true, "Popup should be visible"); |
4033 | + |
4034 | + mouseMove(menuItem0, menuItem0.width/2, menuItem0.height/2); |
4035 | + tryCompare(priv, "currentItem", menuItem0, undefined, "CurrentItem should have moved to item 0"); |
4036 | + compare(menuItem0.popupVisible, true, "Popup should be visible"); |
4037 | + } |
4038 | + |
4039 | + function test_keyboardNavigation_RightKeySelectsNextMenuItem(data) { |
4040 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu"); |
4041 | + var priv = findInvisibleChild(menuBar, "d"); |
4042 | + |
4043 | + var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); |
4044 | + var menuItem1 = findChild(menuBar, "menuBar-item1"); verify(menuItem1); |
4045 | + var menuItem2 = findChild(menuBar, "menuBar-item2"); verify(menuItem2); |
4046 | + |
4047 | + menuItem0.show(); |
4048 | + compare(priv.currentItem, menuItem0, "CurrentItem should be set to item 0"); |
4049 | + compare(priv.currentItem.popupVisible, true, "Popup should be visible"); |
4050 | + |
4051 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
4052 | + compare(priv.currentItem, menuItem1, "CurrentItem should have moved to item 1"); |
4053 | + compare(menuItem1.popupVisible, true, "Popup should be visible"); |
4054 | + |
4055 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
4056 | + compare(priv.currentItem, menuItem2, "CurrentItem should have moved to item 2"); |
4057 | + compare(menuItem2.popupVisible, true, "Popup should be visible"); |
4058 | + |
4059 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
4060 | + compare(priv.currentItem, menuItem0, "CurrentItem should have moved back to item 0"); |
4061 | + compare(menuItem0.popupVisible, true, "Popup should be visible"); |
4062 | + } |
4063 | + |
4064 | + function test_keyboardNavigation_LeftKeySelectsPreviousMenuItem(data) { |
4065 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu"); |
4066 | + var priv = findInvisibleChild(menuBar, "d"); |
4067 | + |
4068 | + var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); |
4069 | + var menuItem1 = findChild(menuBar, "menuBar-item1"); verify(menuItem1); |
4070 | + var menuItem2 = findChild(menuBar, "menuBar-item2"); verify(menuItem2); |
4071 | + |
4072 | + menuItem0.show(); |
4073 | + compare(priv.currentItem, menuItem0, "CurrentItem should be set to item 0"); |
4074 | + compare(priv.currentItem.popupVisible, true, "Popup should be visible"); |
4075 | + |
4076 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
4077 | + compare(priv.currentItem, menuItem2, "CurrentItem should have moved to item 2"); |
4078 | + compare(menuItem2.popupVisible, true, "Popup should be visible"); |
4079 | + |
4080 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
4081 | + compare(priv.currentItem, menuItem1, "CurrentItem should have moved to item 1"); |
4082 | + compare(menuItem1.popupVisible, true, "Popup should be visible"); |
4083 | + |
4084 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
4085 | + compare(priv.currentItem, menuItem0, "CurrentItem should have moved back to item 0"); |
4086 | + compare(menuItem0.popupVisible, true, "Popup should be visible"); |
4087 | + } |
4088 | + |
4089 | + function test_mnemonics_data() { |
4090 | + return [ |
4091 | + { tag: "a", expectedItem: "menuBar-item0" }, |
4092 | + { tag: "c", expectedItem: "menuBar-item2" }, |
4093 | + ] |
4094 | + } |
4095 | + |
4096 | + function test_mnemonics(data) { |
4097 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu"); |
4098 | + var priv = findInvisibleChild(menuBar, "d"); |
4099 | + |
4100 | + var menuItem = findChild(menuBar, data.expectedItem); verify(menuItem); |
4101 | + |
4102 | + keyPress(data.tag, Qt.AltModifier, 100); |
4103 | + tryCompare(priv, "currentItem", menuItem); |
4104 | + } |
4105 | + } |
4106 | +} |
4107 | |
4108 | === added file 'tests/qmltests/ApplicationMenus/tst_MenuPopup.qml' |
4109 | --- tests/qmltests/ApplicationMenus/tst_MenuPopup.qml 1970-01-01 00:00:00 +0000 |
4110 | +++ tests/qmltests/ApplicationMenus/tst_MenuPopup.qml 2017-01-09 15:26:31 +0000 |
4111 | @@ -0,0 +1,207 @@ |
4112 | +/* |
4113 | + * Copyright (C) 2016 Canonical, Ltd. |
4114 | + * |
4115 | + * This program is free software; you can redistribute it and/or modify |
4116 | + * it under the terms of the GNU General Public License as published by |
4117 | + * the Free Software Foundation; version 3. |
4118 | + * |
4119 | + * This program is distributed in the hope that it will be useful, |
4120 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4121 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4122 | + * GNU General Public License for more details. |
4123 | + * |
4124 | + * You should have received a copy of the GNU General Public License |
4125 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4126 | + */ |
4127 | + |
4128 | +import QtQuick 2.4 |
4129 | +import QtTest 1.0 |
4130 | +import Ubuntu.Components 1.3 |
4131 | +import Ubuntu.Components.ListItems 1.3 |
4132 | +import Unity.Application 0.1 |
4133 | +import QMenuModel 0.1 |
4134 | +import Unity.Test 0.1 |
4135 | +import Utils 0.1 |
4136 | + |
4137 | +import "../../../qml/ApplicationMenus" |
4138 | +import ".." |
4139 | + |
4140 | +Item { |
4141 | + id: root |
4142 | + width: Math.max(units.gu(100), page.width + units.gu(6)) |
4143 | + height: Math.max(units.gu(50), page.height + units.gu(6)) |
4144 | + |
4145 | + Binding { |
4146 | + target: MouseTouchAdaptor |
4147 | + property: "enabled" |
4148 | + value: false |
4149 | + } |
4150 | + |
4151 | + ApplicationMenuDataLoader { id: appMenuData } |
4152 | + |
4153 | + MenuPopup { |
4154 | + id: page |
4155 | + |
4156 | + anchors { |
4157 | + left: parent.left |
4158 | + top: parent.top |
4159 | + leftMargin: units.gu(3) |
4160 | + topMargin: units.gu(3) |
4161 | + } |
4162 | + |
4163 | + unityMenuModel: UnityMenuModel { |
4164 | + id: menuBackend |
4165 | + modelData: appMenuData.generateTestData(7,5,2,3,"menu") |
4166 | + } |
4167 | + } |
4168 | + |
4169 | + SignalSpy { |
4170 | + id: activatedSpy |
4171 | + target: menuBackend |
4172 | + signalName: "activated" |
4173 | + } |
4174 | + |
4175 | + UnityTestCase { |
4176 | + id: testCase |
4177 | + name: "MenuPopup" |
4178 | + when: windowShown |
4179 | + |
4180 | + function init() { |
4181 | + page.show(); |
4182 | + } |
4183 | + |
4184 | + function cleanup() { |
4185 | + page.reset(); |
4186 | + wait(100); // let the page dismiss |
4187 | + activatedSpy.clear(); |
4188 | + } |
4189 | + |
4190 | + // visit and verify that all the backend menus have been created |
4191 | + function recurseMenuConstruction(rows, menuPage) { |
4192 | + for (var i = 0; i < rows.length; ++i) { |
4193 | + var rowData = rows[i]["rowData"]; |
4194 | + |
4195 | + var menuItemName = menuPage.objectName +"-item"+i |
4196 | + |
4197 | + var menuItem = findChild(menuPage, menuItemName); verify(menuItem); |
4198 | + |
4199 | + var menuPriv = findInvisibleChild(menuPage, "d"); |
4200 | + |
4201 | + // recurse into submenu |
4202 | + var submenu = rows[i]["submenu"]; |
4203 | + if (submenu) { |
4204 | + mouseClick(menuItem, menuItem.width/2, menuItem.height/2); |
4205 | + tryCompare(menuPriv, "currentItem", menuItem); |
4206 | + |
4207 | + tryCompareFunction(function() { return menuItem.popup !== null && menuItem.visible }, true); |
4208 | + |
4209 | + var submenuPage = findChild(menuPage, menuItemName + "-menu"); verify(submenuPage); |
4210 | + |
4211 | + recurseMenuConstruction(submenu, submenuPage); |
4212 | + } else { |
4213 | + mouseMove(menuItem, menuItem.width/2, menuItem.height/2); |
4214 | + tryCompare(menuPriv, "currentItem", menuItem); |
4215 | + } |
4216 | + } |
4217 | + } |
4218 | + |
4219 | + function test_mouseNavigation_data() { |
4220 | + return [ |
4221 | + { tag: "long", testData: appMenuData.generateTestData(4, 2, 1, 0, "menu", false) }, |
4222 | + { tag: "deep", testData: appMenuData.generateTestData(2, 4, 1, 0, "menu", false) } |
4223 | + ] |
4224 | + } |
4225 | + |
4226 | + function test_mouseNavigation(data) { |
4227 | + menuBackend.modelData = data.testData; |
4228 | + |
4229 | + recurseMenuConstruction(data.testData, page); |
4230 | + } |
4231 | + |
4232 | + function test_checkableMenuTogglesOnClick() { |
4233 | + menuBackend.modelData = appMenuData.singleCheckable; |
4234 | + |
4235 | + var menuItem = findChild(page, "menu-item0-actionItem"); |
4236 | + verify(menuItem); |
4237 | + compare(menuItem.action.checkable, true, "Menu item should be checkable"); |
4238 | + compare(menuItem.action.checked, false, "Menu item should not be checked"); |
4239 | + |
4240 | + mouseClick(menuItem, menuItem.width/2, menuItem.height/2); |
4241 | + |
4242 | + compare(menuItem.action.checked, true, "Checkable menu item should have toggled"); |
4243 | + } |
4244 | + |
4245 | + function test_keyboardNavigation_DownKeySelectsAndOpensNextMenuItemAndRotates() { |
4246 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu",false); |
4247 | + |
4248 | + var item0 = findChild(page, "menu-item0"); verify(item0); |
4249 | + var item1 = findChild(page, "menu-item1"); verify(item1); |
4250 | + var item2 = findChild(page, "menu-item2"); verify(item2); |
4251 | + |
4252 | + var priv = findInvisibleChild(page, "d"); |
4253 | + |
4254 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4255 | + compare(priv.currentItem, item0, "CurrentItem should have moved to item 0"); |
4256 | + |
4257 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4258 | + compare(priv.currentItem, item1, "CurrentItem should have moved to item 1"); |
4259 | + |
4260 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4261 | + compare(priv.currentItem, item2, "CurrentItem should have moved to item 2"); |
4262 | + |
4263 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4264 | + compare(priv.currentItem, item0, "CurrentItem should have moved to item 0"); |
4265 | + } |
4266 | + |
4267 | + function test_keyboardNavigation_UpKeySelectsAndOpensPreviousMenuItemAndRotates() { |
4268 | + menuBackend.modelData = appMenuData.generateTestData(3,3,0,0,"menu",false); |
4269 | + |
4270 | + var item0 = findChild(page, "menu-item0"); verify(item0); |
4271 | + var item1 = findChild(page, "menu-item1"); verify(item1); |
4272 | + var item2 = findChild(page, "menu-item2"); verify(item2); |
4273 | + |
4274 | + var priv = findInvisibleChild(page, "d"); |
4275 | + |
4276 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4277 | + compare(priv.currentItem, item0, "CurrentItem should have moved to item 2"); |
4278 | + |
4279 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4280 | + compare(priv.currentItem, item1, "CurrentItem should have moved to item 1"); |
4281 | + |
4282 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4283 | + compare(priv.currentItem, item2, "CurrentItem should have moved to item 0"); |
4284 | + |
4285 | + keyClick(Qt.Key_Down, Qt.NoModifier); |
4286 | + compare(priv.currentItem, item0, "CurrentItem should have moved to item 2"); |
4287 | + } |
4288 | + |
4289 | + function test_keyboardNavigation_RightKeyEntersSubMenu() { |
4290 | + menuBackend.modelData = appMenuData.generateTestData(3,3,1,0,"menu",false); |
4291 | + |
4292 | + var menuItem = findChild(page, "menu-item0"); |
4293 | + |
4294 | + var priv = findInvisibleChild(page, "d"); |
4295 | + priv.currentItem = menuItem; |
4296 | + |
4297 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
4298 | + tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, true); |
4299 | + |
4300 | + var submenu0 = findChild(page, "menu-item0-menu"); verify(submenu0); |
4301 | + var submenu0item0 = findChild(submenu0, "menu-item0-menu-item0"); verify(submenu0item0); |
4302 | + |
4303 | + var submenu0Priv = findInvisibleChild(submenu0, "d"); verify(submenu0Priv); |
4304 | + compare(submenu0Priv.currentItem, submenu0item0, "First item of submenu should be selected"); |
4305 | + } |
4306 | + |
4307 | + function test_keyboardNavigation_LeftKeyClosesSubMenu() { |
4308 | + menuBackend.modelData = appMenuData.generateTestData(3,3,1,0,"menu",false); |
4309 | + |
4310 | + var menuItem = findChild(page, "menu-item0"); verify(menuItem); |
4311 | + mouseClick(menuItem, menuItem.width/2, menuItem.height/2); |
4312 | + tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, true); |
4313 | + |
4314 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
4315 | + tryCompareFunction(function() { return menuItem.popup !== null && menuItem.popup.visible }, false); |
4316 | + } |
4317 | + } |
4318 | +} |
4319 | |
4320 | === modified file 'tests/qmltests/CMakeLists.txt' |
4321 | --- tests/qmltests/CMakeLists.txt 2017-01-09 15:26:30 +0000 |
4322 | +++ tests/qmltests/CMakeLists.txt 2017-01-09 15:26:31 +0000 |
4323 | @@ -7,6 +7,9 @@ |
4324 | add_unity8_qmltest(. ShellWithPin) |
4325 | add_unity8_qmltest(. DeviceConfiguration) |
4326 | add_unity8_qmltest_data(. EdgeBarrierControls) |
4327 | +add_unity8_qmltest_data(. ApplicationMenuDataLoader) |
4328 | +add_unity8_qmltest(ApplicationMenus MenuBar) |
4329 | +add_unity8_qmltest(ApplicationMenus MenuPopup) |
4330 | add_unity8_qmltest(Components Background) |
4331 | add_unity8_qmltest(Components Carousel) |
4332 | add_unity8_qmltest(Components Dialogs) |
4333 | @@ -85,16 +88,16 @@ |
4334 | add_unity8_qmltest(Notifications SwipeToAct) |
4335 | add_unity8_qmltest_data(Notifications Notification) |
4336 | add_unity8_qmltest(Panel ActiveCallHint) |
4337 | -add_unity8_qmltest(Panel IndicatorItem) |
4338 | -add_unity8_qmltest(Panel IndicatorItemRow) |
4339 | -add_unity8_qmltest(Panel IndicatorPage) |
4340 | -add_unity8_qmltest(Panel IndicatorsBar) |
4341 | -add_unity8_qmltest(Panel IndicatorsMenu) |
4342 | +add_unity8_qmltest(Panel PanelItemRow) |
4343 | +add_unity8_qmltest(Panel PanelMenuPage) |
4344 | +add_unity8_qmltest(Panel PanelBar) |
4345 | +add_unity8_qmltest(Panel PanelMenu) |
4346 | add_unity8_qmltest(Panel MenuContent) |
4347 | add_unity8_qmltest(Panel Panel) |
4348 | -add_unity8_qmltest_data(Panel IndicatorTest) |
4349 | +add_unity8_qmltest_data(Panel PanelTest) |
4350 | +add_unity8_qmltest(Panel/Indicators IndicatorItem) |
4351 | add_unity8_qmltest(Panel/Indicators IndicatorsLight) |
4352 | -add_unity8_qmltest(Panel/Indicators MenuItemFactory) |
4353 | +add_unity8_qmltest(Panel/Indicators IndicatorMenuItemFactory) |
4354 | add_unity8_qmltest(Panel/Indicators MessageMenuItemFactory) |
4355 | add_unity8_qmltest(Stage ApplicationWindow) |
4356 | add_unity8_qmltest(Stage DesktopStage) |
4357 | @@ -104,6 +107,7 @@ |
4358 | add_unity8_qmltest(Stage WindowResizeArea) |
4359 | add_unity8_qmltest(Stage Splash) |
4360 | add_unity8_qmltest(Stage DecoratedWindow) |
4361 | +add_unity8_qmltest(Stage WindowDecoration) |
4362 | add_unity8_qmltest_data(Stage ApplicationCheckBox) |
4363 | add_unity8_qmltest_data(Stage SizeHintField) |
4364 | add_unity8_qmltest_data(Stage SurfaceManagerControls) |
4365 | |
4366 | === renamed file 'tests/qmltests/Panel/tst_IndicatorItem.qml' => 'tests/qmltests/Panel/Indicators/tst_IndicatorItem.qml' |
4367 | --- tests/qmltests/Panel/tst_IndicatorItem.qml 2016-09-01 12:56:08 +0000 |
4368 | +++ tests/qmltests/Panel/Indicators/tst_IndicatorItem.qml 2017-01-09 15:26:31 +0000 |
4369 | @@ -19,7 +19,7 @@ |
4370 | import QtTest 1.0 |
4371 | import Ubuntu.Components 1.3 |
4372 | import Unity.Test 0.1 as UT |
4373 | -import "../../../qml/Panel" |
4374 | +import "../../../../qml/Panel/Indicators" |
4375 | |
4376 | Rectangle { |
4377 | width: units.gu(80) |
4378 | |
4379 | === renamed file 'tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml' => 'tests/qmltests/Panel/Indicators/tst_IndicatorMenuItemFactory.qml' |
4380 | --- tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml 2016-11-30 12:28:19 +0000 |
4381 | +++ tests/qmltests/Panel/Indicators/tst_IndicatorMenuItemFactory.qml 2017-01-09 15:26:31 +0000 |
4382 | @@ -28,7 +28,7 @@ |
4383 | width: units.gu(40) |
4384 | height: units.gu(70) |
4385 | |
4386 | - MenuItemFactory { |
4387 | + IndicatorMenuItemFactory { |
4388 | id: factory |
4389 | menuModel: UnityMenuModel {} |
4390 | } |
4391 | @@ -51,7 +51,7 @@ |
4392 | } |
4393 | |
4394 | UT.UnityTestCase { |
4395 | - name: "MenuItemFactory" |
4396 | + name: "IndicatorMenuItemFactory" |
4397 | when: windowShown |
4398 | |
4399 | property QtObject menuData: QtObject { |
4400 | |
4401 | === renamed file 'tests/qmltests/Panel/IndicatorTest.qml' => 'tests/qmltests/Panel/PanelTest.qml' |
4402 | --- tests/qmltests/Panel/IndicatorTest.qml 2016-03-29 03:47:39 +0000 |
4403 | +++ tests/qmltests/Panel/PanelTest.qml 2017-01-09 15:26:31 +0000 |
4404 | @@ -15,11 +15,6 @@ |
4405 | */ |
4406 | |
4407 | import QtQuick 2.4 |
4408 | -import QtQuick.Layouts 1.1 |
4409 | -import QtTest 1.0 |
4410 | -import "../../../qml/Panel" |
4411 | -import Ubuntu.Components 1.3 |
4412 | -import Unity.Test 0.1 as UT |
4413 | import Unity.Indicators 0.1 as Indicators |
4414 | |
4415 | Rectangle { |
4416 | |
4417 | === modified file 'tests/qmltests/Panel/tst_MenuContent.qml' |
4418 | --- tests/qmltests/Panel/tst_MenuContent.qml 2015-07-15 15:07:19 +0000 |
4419 | +++ tests/qmltests/Panel/tst_MenuContent.qml 2017-01-09 15:26:31 +0000 |
4420 | @@ -20,7 +20,7 @@ |
4421 | import "../../../qml/Panel" |
4422 | import Unity.Indicators 0.1 as Indicators |
4423 | |
4424 | -IndicatorTest { |
4425 | +PanelTest { |
4426 | id: root |
4427 | width: units.gu(40) |
4428 | height: units.gu(70) |
4429 | @@ -31,8 +31,20 @@ |
4430 | |
4431 | MenuContent { |
4432 | id: menuContent |
4433 | - indicatorsModel: root.indicatorsModel |
4434 | + model: root.indicatorsModel |
4435 | height: parent.height - 50 |
4436 | + |
4437 | + pageDelegate: Rectangle { |
4438 | + |
4439 | + function reset() { |
4440 | + } |
4441 | + |
4442 | + Binding { |
4443 | + target: parent |
4444 | + property: "objectName" |
4445 | + value: modelData ? modelData.identifier : "" |
4446 | + } |
4447 | + } |
4448 | } |
4449 | |
4450 | Rectangle { |
4451 | |
4452 | === modified file 'tests/qmltests/Panel/tst_Panel.qml' |
4453 | --- tests/qmltests/Panel/tst_Panel.qml 2016-12-02 13:25:44 +0000 |
4454 | +++ tests/qmltests/Panel/tst_Panel.qml 2017-01-09 15:26:31 +0000 |
4455 | @@ -19,26 +19,42 @@ |
4456 | import QtTest 1.0 |
4457 | import Unity.Test 0.1 |
4458 | import Ubuntu.Components 1.3 |
4459 | +import Ubuntu.Components.ListItems 1.3 as ListItem |
4460 | import Unity.Application 0.1 |
4461 | -import Unity.Indicators 0.1 as Indicators |
4462 | +import QMenuModel 0.1 |
4463 | import Ubuntu.Telephony 0.1 as Telephony |
4464 | +import AccountsService 0.1 |
4465 | +import Unity.InputInfo 0.1 |
4466 | import "../../../qml/Panel" |
4467 | import "../../../qml/Components/PanelState" |
4468 | +import "../Stage" |
4469 | +import ".." |
4470 | |
4471 | -IndicatorTest { |
4472 | +PanelTest { |
4473 | id: root |
4474 | - width: units.gu(100) |
4475 | + width: units.gu(120) |
4476 | height: units.gu(71) |
4477 | color: "black" |
4478 | |
4479 | - Component.onCompleted: theme.name = "Ubuntu.Components.Themes.SuruDark" |
4480 | - |
4481 | - SurfaceManager {} |
4482 | - |
4483 | Binding { |
4484 | - target: mouseEmulation |
4485 | - property: "checked" |
4486 | - value: !windowControlsCB.checked |
4487 | + target: QuickUtils |
4488 | + property: "keyboardAttached" |
4489 | + value: keyboardAttached.checked |
4490 | + } |
4491 | + |
4492 | + SurfaceManager { id: sMgr } |
4493 | + ApplicationMenuDataLoader { |
4494 | + id: appMenuData |
4495 | + surfaceManager: sMgr |
4496 | + } |
4497 | + |
4498 | + Component.onCompleted: { |
4499 | + theme.name = "Ubuntu.Components.Themes.SuruDark" |
4500 | + } |
4501 | + |
4502 | + Rectangle { |
4503 | + anchors.fill: parent |
4504 | + color: "darkgrey" |
4505 | } |
4506 | |
4507 | RowLayout { |
4508 | @@ -61,12 +77,23 @@ |
4509 | Panel { |
4510 | id: panel |
4511 | anchors.fill: parent |
4512 | + mode: modeSelector.model[modeSelector.selectedIndex] |
4513 | + |
4514 | + indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width |
4515 | + applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width |
4516 | + |
4517 | + applicationMenus { |
4518 | + model: UnityMenuModel { |
4519 | + modelData: appMenuData.generateTestData(5, 4, 2, 3, "menu") |
4520 | + } |
4521 | + |
4522 | + hides: [ panel.indicators ] |
4523 | + } |
4524 | + |
4525 | indicators { |
4526 | - width: parent.width > units.gu(60) ? units.gu(40) : parent.width |
4527 | - indicatorsModel: root.indicatorsModel |
4528 | + model: root.indicatorsModel |
4529 | + hides: [ panel.applicationMenus ] |
4530 | } |
4531 | - |
4532 | - property real panelAndSeparatorHeight: panel.indicators.minimizedPanelHeight |
4533 | } |
4534 | } |
4535 | } |
4536 | @@ -75,6 +102,18 @@ |
4537 | Layout.alignment: Qt.AlignTop |
4538 | Layout.fillWidth: false |
4539 | |
4540 | + ListItem.ItemSelector { |
4541 | + id: modeSelector |
4542 | + anchors { left: parent.left; right: parent.right } |
4543 | + activeFocusOnPress: false |
4544 | + text: "Mode" |
4545 | + model: ["staged", "windowed" ] |
4546 | + onSelectedIndexChanged: { |
4547 | + panel.mode = model[selectedIndex]; |
4548 | + keyboardAttached.checked = panel.mode == "windowed" |
4549 | + } |
4550 | + } |
4551 | + |
4552 | Button { |
4553 | Layout.fillWidth: true |
4554 | text: panel.indicators.shown ? "Hide" : "Show" |
4555 | @@ -109,25 +148,22 @@ |
4556 | Layout.fillWidth: true |
4557 | CheckBox { |
4558 | id: windowControlsCB |
4559 | - onClicked: PanelState.buttonsVisible = checked |
4560 | + onClicked: PanelState.decorationsVisible = checked |
4561 | } |
4562 | Label { |
4563 | - text: "Show window controls" |
4564 | + text: "Show window decorations" |
4565 | + color: "white" |
4566 | } |
4567 | } |
4568 | |
4569 | RowLayout { |
4570 | Layout.fillWidth: true |
4571 | CheckBox { |
4572 | - onClicked: { |
4573 | - if (checked) |
4574 | - PanelState.title = "Fake window title" |
4575 | - else |
4576 | - PanelState.title = "" |
4577 | - } |
4578 | + onClicked: PanelState.title = checked ? "Fake window title" : "" |
4579 | } |
4580 | Label { |
4581 | text: "Show fake window title" |
4582 | + color: "white" |
4583 | } |
4584 | } |
4585 | |
4586 | @@ -137,6 +173,12 @@ |
4587 | color: "black" |
4588 | } |
4589 | |
4590 | + Rectangle { |
4591 | + Layout.preferredHeight: units.dp(1); |
4592 | + Layout.fillWidth: true; |
4593 | + color: "black" |
4594 | + } |
4595 | + |
4596 | Repeater { |
4597 | model: root.originalModelData |
4598 | RowLayout { |
4599 | @@ -147,6 +189,7 @@ |
4600 | Label { |
4601 | Layout.fillWidth: true |
4602 | text: modelData["identifier"] |
4603 | + color: "white" |
4604 | } |
4605 | |
4606 | CheckBox { |
4607 | @@ -155,6 +198,7 @@ |
4608 | } |
4609 | Label { |
4610 | text: "visible" |
4611 | + color: "white" |
4612 | } |
4613 | } |
4614 | } |
4615 | @@ -165,7 +209,22 @@ |
4616 | color: "black" |
4617 | } |
4618 | |
4619 | - MouseTouchEmulationCheckbox { id: mouseEmulation } |
4620 | + MouseTouchEmulationCheckbox { |
4621 | + id: mouseEmulation |
4622 | + color: "white" |
4623 | + checked: panel.mode == "staged" |
4624 | + } |
4625 | + |
4626 | + RowLayout { |
4627 | + Layout.fillWidth: true |
4628 | + CheckBox { |
4629 | + id: keyboardAttached |
4630 | + } |
4631 | + Label { |
4632 | + text: "Keyboard Attached" |
4633 | + color: "white" |
4634 | + } |
4635 | + } |
4636 | } |
4637 | } |
4638 | |
4639 | @@ -191,17 +250,18 @@ |
4640 | } |
4641 | |
4642 | function init() { |
4643 | + panel.mode = "staged"; |
4644 | + mouseEmulation.checked = true; |
4645 | panel.fullscreenMode = false; |
4646 | callManager.foregroundCall = null; |
4647 | |
4648 | - panel.indicators.hide(); |
4649 | - // Wait for animation to complete |
4650 | - tryCompare(panel.indicators.hideAnimation, "running", false); |
4651 | + PanelState.title = ""; |
4652 | + PanelState.decorationsVisible = false; |
4653 | |
4654 | // Wait for the indicators to get into position. |
4655 | // (switches between normal and fullscreen modes are animated) |
4656 | - var indicatorArea = findChild(panel, "indicatorArea"); |
4657 | - tryCompare(indicatorArea, "y", 0); |
4658 | + var panelArea = findChild(panel, "panelArea"); |
4659 | + tryCompare(panelArea, "y", 0); |
4660 | |
4661 | backgroundPressedSpy.clear(); |
4662 | compare(backgroundPressedSpy.valid, true); |
4663 | @@ -209,6 +269,12 @@ |
4664 | compare(windowControlButtonsSpy.valid, true); |
4665 | } |
4666 | |
4667 | + function cleanup() { |
4668 | + panel.indicators.hide(); |
4669 | + panel.applicationMenus.hide(); |
4670 | + waitForAllAnimationToComplete("initial"); |
4671 | + } |
4672 | + |
4673 | function get_indicator_item(index) { |
4674 | var indicatorItem = findChild(panel, root.originalModelData[index]["identifier"]+"-panelItem"); |
4675 | verify(indicatorItem !== null); |
4676 | @@ -216,8 +282,27 @@ |
4677 | return indicatorItem; |
4678 | } |
4679 | |
4680 | + function waitForAllAnimationToComplete(state) { |
4681 | + |
4682 | + tryCompare(panel.indicators.hideAnimation, "running", false); |
4683 | + tryCompare(panel.indicators .showAnimation, "running", false); |
4684 | + tryCompare(panel.indicators, "state", state); |
4685 | + |
4686 | + for (var i = 0; i < root.originalModelData.length; i++) { |
4687 | + var indicatorItem = get_indicator_item(i); |
4688 | + |
4689 | + var itemState = findInvisibleChild(indicatorItem, "indicatorItemState"); |
4690 | + verify(itemState !== null); |
4691 | + |
4692 | + waitUntilTransitionsEnd(itemState); |
4693 | + } |
4694 | + |
4695 | + tryCompare(panel.applicationMenus.hideAnimation, "running", false); |
4696 | + tryCompare(panel.applicationMenus, "state", state); |
4697 | + } |
4698 | + |
4699 | function pullDownIndicatorsMenu() { |
4700 | - var showDragHandle = findChild(panel, "showDragHandle"); |
4701 | + var showDragHandle = findChild(panel.indicators, "showDragHandle"); |
4702 | touchFlick(showDragHandle, |
4703 | showDragHandle.width / 2, |
4704 | showDragHandle.height / 2, |
4705 | @@ -226,16 +311,22 @@ |
4706 | tryCompare(panel.indicators, "fullyOpened", true); |
4707 | } |
4708 | |
4709 | - function test_drag_show_data() { |
4710 | + function pullDownApplicationsMenu() { |
4711 | + var showDragHandle = findChild(panel.applicationMenus, "showDragHandle"); |
4712 | + touchFlick(showDragHandle, |
4713 | + showDragHandle.width / 2, |
4714 | + showDragHandle.height / 2, |
4715 | + showDragHandle.width / 2, |
4716 | + showDragHandle.height / 2 + (showDragHandle.autoCompleteDragThreshold * 1.1)); |
4717 | + tryCompare(panel.applicationMenus, "fullyOpened", true); |
4718 | + } |
4719 | + |
4720 | + function test_drag_indicator_item_down_shows_menu_data() { |
4721 | return [ |
4722 | - { tag: "pinned", fullscreen: false, call: null, |
4723 | - indicatorY: 0 }, |
4724 | - { tag: "fullscreen", fullscreen: true, call: null, |
4725 | - indicatorY: -panel.panelAndSeparatorHeight }, |
4726 | - { tag: "pinned-callActive", fullscreen: false, call: phoneCall, |
4727 | - indicatorY: 0}, |
4728 | - { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall, |
4729 | - indicatorY: -panel.panelAndSeparatorHeight } |
4730 | + { tag: "pinned", fullscreen: false, call: null, y: 0 }, |
4731 | + { tag: "fullscreen", fullscreen: true, call: null, y: -panel.minimizedPanelHeight }, |
4732 | + { tag: "pinned-callActive", fullscreen: false, call: phoneCall, y: 0}, |
4733 | + { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall, y: -panel.minimizedPanelHeight } |
4734 | ]; |
4735 | } |
4736 | |
4737 | @@ -243,22 +334,22 @@ |
4738 | // indicators, first by running the hint animation, then after dragging down will |
4739 | // expose more of the panel, binding it to the selected indicator and opening it's menu. |
4740 | // Tested from first Y pixel to check for swipe from offscreen. |
4741 | - function test_drag_show(data) { |
4742 | + function test_drag_indicator_item_down_shows_menu(data) { |
4743 | panel.fullscreenMode = data.fullscreen; |
4744 | callManager.foregroundCall = data.call; |
4745 | |
4746 | - var indicatorRow = findChild(panel.indicators, "indicatorItemRow"); |
4747 | - verify(indicatorRow !== null); |
4748 | + var panelRow = findChild(panel.indicators, "panelItemRow"); |
4749 | + verify(panelRow !== null); |
4750 | |
4751 | var menuContent = findChild(panel.indicators, "menuContent"); |
4752 | verify(menuContent !== null); |
4753 | |
4754 | - var indicatorArea = findChild(panel, "indicatorArea"); |
4755 | - verify(indicatorArea !== null); |
4756 | + var panelArea = findChild(panel, "panelArea"); |
4757 | + verify(panelArea !== null); |
4758 | |
4759 | // Wait for the indicators to get into position. |
4760 | // (switches between normal and fullscreen modes are animated) |
4761 | - tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY); |
4762 | + tryCompareFunction(function() { return panelArea.y }, data.y); |
4763 | |
4764 | for (var i = 0; i < root.originalModelData.length; i++) { |
4765 | var indicatorItem = get_indicator_item(i); |
4766 | @@ -268,7 +359,7 @@ |
4767 | touchFlick(panel, |
4768 | startXPosition, 0, |
4769 | startXPosition, panel.height, |
4770 | - true /* beginTouch */, false /* endTouch */, units.gu(5), 15); |
4771 | + true /* beginTouch */, false /* endTouch */, 1, 15); |
4772 | |
4773 | // Indicators height should follow the drag, and therefore increase accordingly. |
4774 | // They should be at least half-way through the screen |
4775 | @@ -278,105 +369,153 @@ |
4776 | |
4777 | touchRelease(panel, startXPosition, panel.height); |
4778 | |
4779 | - compare(indicatorRow.currentItemIndex, i, "Indicator item should be activated at position " + i); |
4780 | + tryCompare(panelRow, "currentItemIndex", i, undefined, "Indicator item should be activated at position " + i); |
4781 | compare(menuContent.currentMenuIndex, i, "Menu conetent should be activated for item at position " + i); |
4782 | |
4783 | // init for next indicatorItem |
4784 | panel.indicators.hide(); |
4785 | - tryCompare(panel.indicators.hideAnimation, "running", false); |
4786 | - tryCompare(panel.indicators, "state", "initial"); |
4787 | + waitForAllAnimationToComplete("initial"); |
4788 | } |
4789 | } |
4790 | |
4791 | - function test_drag_hide_data() { |
4792 | + function test_drag_panel_handle_up_hides_menu_data() { |
4793 | return [ |
4794 | - { tag: "pinned", fullscreen: false, call: null, |
4795 | - indicatorY: 0 }, |
4796 | - { tag: "fullscreen", fullscreen: true, call: null, |
4797 | - indicatorY: -panel.panelAndSeparatorHeight }, |
4798 | - { tag: "pinned-callActive", fullscreen: false, call: phoneCall, |
4799 | - indicatorY: 0}, |
4800 | - { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall, |
4801 | - indicatorY: -panel.panelAndSeparatorHeight } |
4802 | + { tag: "indicators-pinned", section: panel.indicators, fullscreen: false, call: null }, |
4803 | + { tag: "indicators-fullscreen", section: panel.indicators, fullscreen: true, call: null }, |
4804 | + { tag: "indicators-pinned-callActive", section: panel.indicators, fullscreen: false, call: phoneCall }, |
4805 | + { tag: "indicators-fullscreen-callActive", section: panel.indicators, fullscreen: true, call: phoneCall }, |
4806 | + { tag: "appMenus-pinned", section: panel.applicationMenus, fullscreen: false, call: null }, |
4807 | + { tag: "appMenus-fullscreen", section: panel.applicationMenus, fullscreen: true, call: null }, |
4808 | + { tag: "appMenus-pinned-callActive", section: panel.applicationMenus, fullscreen: false, call: phoneCall }, |
4809 | + { tag: "appMenus-fullscreen-callActive", section: panel.applicationMenus, fullscreen: true, call: phoneCall } |
4810 | ]; |
4811 | } |
4812 | |
4813 | // Dragging the shown indicators up from bottom of panel will hide the indicators |
4814 | // Tested from last Y pixel to check for swipe from offscreen. |
4815 | - function test_drag_hide(data) { |
4816 | + function test_drag_panel_handle_up_hides_menu(data) { |
4817 | panel.fullscreenMode = data.fullscreen; |
4818 | callManager.foregroundCall = data.call; |
4819 | |
4820 | - var indicatorRow = findChild(panel.indicators, "indicatorItemRow"); |
4821 | - verify(indicatorRow !== null); |
4822 | - |
4823 | - var menuContent = findChild(panel.indicators, "menuContent"); |
4824 | - verify(menuContent !== null); |
4825 | - |
4826 | - var indicatorArea = findChild(panel, "indicatorArea"); |
4827 | - verify(indicatorArea !== null); |
4828 | + var panelRow = findChild(data.section, "panelItemRow"); |
4829 | + verify(panelRow !== null); |
4830 | + |
4831 | + var panelArea = findChild(panel, "panelArea"); |
4832 | + verify(panelArea !== null); |
4833 | |
4834 | // Wait for the indicators to get into position. |
4835 | // (switches between normal and fullscreen modes are animated) |
4836 | - tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY); |
4837 | - |
4838 | - panel.indicators.show(); |
4839 | - tryCompare(panel.indicators.showAnimation, "running", false); |
4840 | - tryCompare(panel.indicators, "unitProgress", 1); |
4841 | - |
4842 | - touchFlick(panel.indicators, |
4843 | - panel.indicators.width / 2, panel.height, |
4844 | - panel.indicators.width / 2, 0, |
4845 | + tryCompareFunction(function() { return panelArea.y }, data.fullscreen ? -panel.minimizedPanelHeight : 0); |
4846 | + |
4847 | + data.section.show(); |
4848 | + tryCompare(data.section.showAnimation, "running", false); |
4849 | + tryCompare(data.section, "unitProgress", 1); |
4850 | + |
4851 | + touchFlick(data.section, |
4852 | + data.section.width / 2, panel.height, |
4853 | + data.section.width / 2, 0, |
4854 | true /* beginTouch */, false /* endTouch */, units.gu(5), 15); |
4855 | |
4856 | // Indicators height should follow the drag, and therefore increase accordingly. |
4857 | // They should be at least half-way through the screen |
4858 | tryCompareFunction( |
4859 | - function() {return panel.indicators.height <= panel.height * 0.5}, |
4860 | + function() {return data.section.height <= panel.height * 0.5}, |
4861 | true); |
4862 | |
4863 | - touchRelease(panel.indicators, panel.indicators.width / 2, 0); |
4864 | + touchRelease(data.section, data.section.width / 2, 0); |
4865 | |
4866 | - tryCompare(panel.indicators.hideAnimation, "running", true); |
4867 | - tryCompare(panel.indicators.hideAnimation, "running", false); |
4868 | - tryCompare(panel.indicators, "state", "initial"); |
4869 | + tryCompare(data.section.hideAnimation, "running", true); |
4870 | + tryCompare(data.section.hideAnimation, "running", false); |
4871 | + tryCompare(data.section, "state", "initial"); |
4872 | } |
4873 | |
4874 | function test_hint_data() { |
4875 | return [ |
4876 | - { tag: "normal", fullscreen: false, call: null, hintExpected: true}, |
4877 | - { tag: "fullscreen", fullscreen: true, call: null, hintExpected: false}, |
4878 | - { tag: "call hint", fullscreen: false, call: phoneCall, hintExpected: false}, |
4879 | + { tag: "indicators-normal", section: panel.indicators, fullscreen: false, call: null, hintExpected: true}, |
4880 | + { tag: "indicators-fullscreen", section: panel.indicators, fullscreen: true, call: null, hintExpected: false}, |
4881 | + { tag: "indicators-callActive", section: panel.indicators, fullscreen: false, call: phoneCall, hintExpected: false}, |
4882 | + { tag: "appMenus-normal", section: panel.applicationMenus, fullscreen: false, call: null, hintExpected: true}, |
4883 | + { tag: "appMenus-fullscreen", section: panel.applicationMenus, fullscreen: true, call: null, hintExpected: false}, |
4884 | + { tag: "appMenus-callActive", section: panel.applicationMenus, fullscreen: false, call: phoneCall, hintExpected: false}, |
4885 | ]; |
4886 | } |
4887 | |
4888 | function test_hint(data) { |
4889 | + PanelState.title = "Fake Title" |
4890 | panel.fullscreenMode = data.fullscreen; |
4891 | callManager.foregroundCall = data.call; |
4892 | |
4893 | if (data.fullscreen) { |
4894 | // Wait for the indicators to get into position. |
4895 | // (switches between normal and fullscreen modes are animated) |
4896 | - var indicatorArea = findChild(panel, "indicatorArea"); |
4897 | - tryCompare(indicatorArea, "y", -panel.panelHeight); |
4898 | + var panelArea = findChild(panel, "panelArea"); |
4899 | + tryCompare(panelArea, "y", -panel.minimizedPanelHeight); |
4900 | } |
4901 | |
4902 | - var indicatorItem = get_indicator_item(0); |
4903 | - var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, indicatorItem.height / 2); |
4904 | - |
4905 | - touchPress(panel, mappedPosition.x, panel.indicators.minimizedPanelHeight / 2); |
4906 | - |
4907 | - // Give some time for a hint animation to change things, if any |
4908 | - wait(500); |
4909 | + var mappedPosition = root.mapFromItem(data.section, |
4910 | + data.section.barWidth / 2, data.section.minimizedPanelHeight / 2); |
4911 | + |
4912 | + touchPress(panel, mappedPosition.x, panel.minimizedPanelHeight / 2); |
4913 | + |
4914 | + var showDragHandle = findChild(data.section, "showDragHandle") |
4915 | + var hintingAnimation = findInvisibleChild(showDragHandle, "hintingAnimation"); |
4916 | + verify(hintingAnimation); |
4917 | + |
4918 | + compare(hintingAnimation.running, data.hintExpected) |
4919 | + tryCompare(hintingAnimation, "running", false); // wait till animation completes |
4920 | |
4921 | // no hint animation when fullscreen |
4922 | - compare(panel.indicators.fullyClosed, !data.hintExpected, "Indicator should be fully closed"); |
4923 | - compare(panel.indicators.partiallyOpened, data.hintExpected, "Indicator should be partialy opened"); |
4924 | - compare(panel.indicators.fullyOpened, false, "Indicator should not be fully opened"); |
4925 | + compare(data.section.partiallyOpened, data.hintExpected, "Indicator should be partialy opened"); |
4926 | + compare(data.section.fullyOpened, false, "Indicator should not be fully opened"); |
4927 | |
4928 | touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2); |
4929 | } |
4930 | |
4931 | + function test_drag_applicationMenu_down_shows_menu_data() { |
4932 | + return [ |
4933 | + { tag: "normal", fullscreen: false, call: null, hintExpected: true}, |
4934 | + { tag: "fullscreen", fullscreen: true, call: null, hintExpected: false}, |
4935 | + { tag: "callActive", fullscreen: false, call: phoneCall, hintExpected: false} |
4936 | + ]; |
4937 | + } |
4938 | + |
4939 | + // Dragging the application menu will gradually expose the |
4940 | + // menus, first by running the hint animation, then after dragging down will |
4941 | + // expose more of the panel. Releasing the touch will complete the show. |
4942 | + function test_drag_applicationMenu_down_shows_menu(data) { |
4943 | + PanelState.title = "Fake Title"; |
4944 | + panel.fullscreenMode = data.fullscreen; |
4945 | + callManager.foregroundCall = data.call; |
4946 | + |
4947 | + var panelRow = findChild(panel.indicators, "panelItemRow"); |
4948 | + verify(panelRow !== null); |
4949 | + |
4950 | + var menuContent = findChild(panel.indicators, "menuContent"); |
4951 | + verify(menuContent !== null); |
4952 | + |
4953 | + var panelArea = findChild(panel, "panelArea"); |
4954 | + verify(panelArea !== null); |
4955 | + |
4956 | + // Wait for the indicators to get into position. |
4957 | + // (switches between normal and fullscreen modes are animated) |
4958 | + tryCompareFunction(function() { return panelArea.y }, data.fullscreen ? -panel.minimizedPanelHeight : 0); |
4959 | + |
4960 | + touchFlick(panel, |
4961 | + units.gu(1), 0, |
4962 | + units.gu(1), panel.height, |
4963 | + true /* beginTouch */, false /* endTouch */, units.gu(5), 15); |
4964 | + |
4965 | + // Indicators height should follow the drag, and therefore increase accordingly. |
4966 | + // They should be at least half-way through the screen |
4967 | + tryCompareFunction( |
4968 | + function() {return panel.applicationMenus.height >= panel.height * 0.5}, |
4969 | + true); |
4970 | + |
4971 | + touchRelease(panel, units.gu(1), panel.height); |
4972 | + |
4973 | + tryCompare(panel.applicationMenus, "fullyOpened", true) |
4974 | + } |
4975 | + |
4976 | /* Checks that no input reaches items behind the indicator bar. |
4977 | Ie., the indicator bar should eat all input events that hit it. |
4978 | */ |
4979 | @@ -385,20 +524,20 @@ |
4980 | // that it doesn't have a "weak spot" from where taps pass through. |
4981 | var numTaps = 5; |
4982 | var stepLength = (panel.width / (numTaps + 1)); |
4983 | - var tapY = panel.indicators.minimizedPanelHeight / 2; |
4984 | + var tapY = panel.minimizedPanelHeight / 2; |
4985 | for (var i = 1; i <= numTaps; ++i) { |
4986 | tap(panel, stepLength * i, tapY); |
4987 | tryCompare(panel.indicators, "fullyClosed", true); |
4988 | } |
4989 | } |
4990 | |
4991 | - function test_darkenedAreaEatsAllEvents() { |
4992 | + function test_darkenedAreaEatsAllIndicatorEvents() { |
4993 | |
4994 | // The center of the area not covered by the indicators menu |
4995 | // Ie, the visible darkened area behind the menu |
4996 | var touchPosX = (panel.width - panel.indicators.width) / 2 |
4997 | - var touchPosY = panel.indicators.minimizedPanelHeight + |
4998 | - ((panel.height - panel.indicators.minimizedPanelHeight) / 2) |
4999 | + var touchPosY = panel.minimizedPanelHeight + |
5000 | + ((panel.height - panel.minimizedPanelHeight) / 2) |
Better rebase on top of https:/ /code.launchpad .net/~mzanetti/ unity8/ unified- stages/ +merge/ 305588
I don't think it's going to land before it.