Main.qml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /*
  2. * Copyright 2016 David Edmundson <davidedmundson@kde.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Library General Public License as
  6. * published by the Free Software Foundation; either version 2 or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this program; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. */
  19. import QtQuick 2.8
  20. import QtQuick.Layouts 1.1
  21. import QtQuick.Controls 1.1
  22. import QtGraphicalEffects 1.0
  23. import org.kde.plasma.core 2.0 as PlasmaCore
  24. import org.kde.plasma.components 2.0 as PlasmaComponents
  25. import org.kde.plasma.extras 2.0 as PlasmaExtras
  26. import "components"
  27. PlasmaCore.ColorScope {
  28. id: root
  29. readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
  30. colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
  31. width: 1600
  32. height: 900
  33. property string notificationMessage
  34. LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
  35. LayoutMirroring.childrenInherit: true
  36. PlasmaCore.DataSource {
  37. id: keystateSource
  38. engine: "keystate"
  39. connectedSources: "Caps Lock"
  40. }
  41. Image {
  42. id: wallpaper
  43. height: parent.height
  44. width: parent.width
  45. source: config.background || config.Background
  46. asynchronous: true
  47. cache: true
  48. clip: true
  49. }
  50. MouseArea {
  51. id: loginScreenRoot
  52. anchors.fill: parent
  53. property bool uiVisible: true
  54. property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive || config.type != "image"
  55. hoverEnabled: true
  56. drag.filterChildren: true
  57. onPressed: uiVisible = true;
  58. onPositionChanged: uiVisible = true;
  59. onUiVisibleChanged: {
  60. if (blockUI) {
  61. fadeoutTimer.running = false;
  62. } else if (uiVisible) {
  63. fadeoutTimer.restart();
  64. }
  65. }
  66. onBlockUIChanged: {
  67. if (blockUI) {
  68. fadeoutTimer.running = false;
  69. uiVisible = true;
  70. } else {
  71. fadeoutTimer.restart();
  72. }
  73. }
  74. Keys.onPressed: {
  75. uiVisible = true;
  76. event.accepted = false;
  77. }
  78. //takes one full minute for the ui to disappear
  79. Timer {
  80. id: fadeoutTimer
  81. running: true
  82. interval: 60000
  83. onTriggered: {
  84. if (!loginScreenRoot.blockUI) {
  85. loginScreenRoot.uiVisible = false;
  86. }
  87. }
  88. }
  89. StackView {
  90. id: mainStack
  91. anchors.centerIn: parent
  92. height: root.height / 2
  93. width: parent.width / 3
  94. focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it
  95. Timer {
  96. //SDDM has a bug in 0.13 where even though we set the focus on the right item within the window, the window doesn't have focus
  97. //it is fixed in 6d5b36b28907b16280ff78995fef764bb0c573db which will be 0.14
  98. //we need to call "window->activate()" *After* it's been shown. We can't control that in QML so we use a shoddy timer
  99. //it's been this way for all Plasma 5.x without a huge problem
  100. running: true
  101. repeat: false
  102. interval: 200
  103. onTriggered: mainStack.forceActiveFocus()
  104. }
  105. initialItem: Login {
  106. id: userListComponent
  107. userListModel: userModel
  108. loginScreenUiVisible: loginScreenRoot.uiVisible
  109. userListCurrentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0
  110. lastUserName: userModel.lastUser
  111. showUserList: {
  112. if ( !userListModel.hasOwnProperty("count")
  113. || !userListModel.hasOwnProperty("disableAvatarsThreshold"))
  114. return (userList.y + mainStack.y) > 0
  115. if ( userListModel.count == 0 ) return false
  116. return userListModel.count <= userListModel.disableAvatarsThreshold && (userList.y + mainStack.y) > 0
  117. }
  118. notificationMessage: {
  119. var text = ""
  120. if (keystateSource.data["Caps Lock"]["Locked"]) {
  121. text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on")
  122. if (root.notificationMessage) {
  123. text += " • "
  124. }
  125. }
  126. text += root.notificationMessage
  127. return text
  128. }
  129. actionItems: [
  130. ActionButton {
  131. iconSource: Qt.resolvedUrl("assets/suspend.svg")
  132. text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep")
  133. onClicked: sddm.suspend()
  134. enabled: sddm.canSuspend
  135. visible: !inputPanel.keyboardActive
  136. },
  137. ActionButton {
  138. iconSource: Qt.resolvedUrl("assets/restart.svg")
  139. text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart")
  140. onClicked: sddm.reboot()
  141. enabled: sddm.canReboot
  142. visible: !inputPanel.keyboardActive
  143. },
  144. ActionButton {
  145. iconSource: Qt.resolvedUrl("assets/shutdown.svg")
  146. text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down")
  147. onClicked: sddm.powerOff()
  148. enabled: sddm.canPowerOff
  149. visible: !inputPanel.keyboardActive
  150. },
  151. ActionButton {
  152. iconSource: Qt.resolvedUrl("assets/change_user.svg")
  153. text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Different User")
  154. onClicked: mainStack.push(userPromptComponent)
  155. enabled: true
  156. visible: !userListComponent.showUsernamePrompt && !inputPanel.keyboardActive
  157. }]
  158. onLoginRequest: {
  159. root.notificationMessage = ""
  160. sddm.login(username, password, sessionButton.currentIndex)
  161. }
  162. }
  163. Behavior on opacity {
  164. OpacityAnimator {
  165. duration: units.longDuration
  166. }
  167. }
  168. }
  169. Loader {
  170. id: inputPanel
  171. state: "hidden"
  172. property bool keyboardActive: item ? item.active : false
  173. onKeyboardActiveChanged: {
  174. if (keyboardActive) {
  175. state = "visible"
  176. } else {
  177. state = "hidden";
  178. }
  179. }
  180. source: "components/VirtualKeyboard.qml"
  181. anchors {
  182. left: parent.left
  183. right: parent.right
  184. }
  185. function showHide() {
  186. state = state == "hidden" ? "visible" : "hidden";
  187. }
  188. states: [
  189. State {
  190. name: "visible"
  191. PropertyChanges {
  192. target: mainStack
  193. y: Math.min(0, root.height - inputPanel.height - userListComponent.visibleBoundary)
  194. }
  195. PropertyChanges {
  196. target: inputPanel
  197. y: root.height - inputPanel.height
  198. opacity: 1
  199. }
  200. },
  201. State {
  202. name: "hidden"
  203. PropertyChanges {
  204. target: mainStack
  205. y: 0
  206. }
  207. PropertyChanges {
  208. target: inputPanel
  209. y: root.height - root.height/4
  210. opacity: 0
  211. }
  212. }
  213. ]
  214. transitions: [
  215. Transition {
  216. from: "hidden"
  217. to: "visible"
  218. SequentialAnimation {
  219. ScriptAction {
  220. script: {
  221. inputPanel.item.activated = true;
  222. Qt.inputMethod.show();
  223. }
  224. }
  225. ParallelAnimation {
  226. NumberAnimation {
  227. target: mainStack
  228. property: "y"
  229. duration: units.longDuration
  230. easing.type: Easing.InOutQuad
  231. }
  232. NumberAnimation {
  233. target: inputPanel
  234. property: "y"
  235. duration: units.longDuration
  236. easing.type: Easing.OutQuad
  237. }
  238. OpacityAnimator {
  239. target: inputPanel
  240. duration: units.longDuration
  241. easing.type: Easing.OutQuad
  242. }
  243. }
  244. }
  245. },
  246. Transition {
  247. from: "visible"
  248. to: "hidden"
  249. SequentialAnimation {
  250. ParallelAnimation {
  251. NumberAnimation {
  252. target: mainStack
  253. property: "y"
  254. duration: units.longDuration
  255. easing.type: Easing.InOutQuad
  256. }
  257. NumberAnimation {
  258. target: inputPanel
  259. property: "y"
  260. duration: units.longDuration
  261. easing.type: Easing.InQuad
  262. }
  263. OpacityAnimator {
  264. target: inputPanel
  265. duration: units.longDuration
  266. easing.type: Easing.InQuad
  267. }
  268. }
  269. ScriptAction {
  270. script: {
  271. Qt.inputMethod.hide();
  272. }
  273. }
  274. }
  275. }
  276. ]
  277. }
  278. Component {
  279. id: userPromptComponent
  280. Login {
  281. showUsernamePrompt: true
  282. notificationMessage: root.notificationMessage
  283. loginScreenUiVisible: loginScreenRoot.uiVisible
  284. // using a model rather than a QObject list to avoid QTBUG-75900
  285. userListModel: ListModel {
  286. ListElement {
  287. name: ""
  288. iconSource: ""
  289. }
  290. Component.onCompleted: {
  291. // as we can't bind inside ListElement
  292. setProperty(0, "name", i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Type in Username and Password"));
  293. }
  294. }
  295. onLoginRequest: {
  296. root.notificationMessage = ""
  297. sddm.login(username, password, sessionButton.currentIndex)
  298. }
  299. actionItems: [
  300. ActionButton {
  301. iconSource: "system-suspend"
  302. text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep")
  303. onClicked: sddm.suspend()
  304. enabled: sddm.canSuspend
  305. visible: !inputPanel.keyboardActive
  306. },
  307. ActionButton {
  308. iconSource: "system-reboot"
  309. text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart")
  310. onClicked: sddm.reboot()
  311. enabled: sddm.canReboot
  312. visible: !inputPanel.keyboardActive
  313. },
  314. ActionButton {
  315. iconSource: "system-shutdown"
  316. text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down")
  317. onClicked: sddm.powerOff()
  318. enabled: sddm.canPowerOff
  319. visible: !inputPanel.keyboardActive
  320. },
  321. ActionButton {
  322. iconSource: "go-previous"
  323. text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","List Users")
  324. onClicked: mainStack.pop()
  325. visible: !inputPanel.keyboardActive
  326. }
  327. ]
  328. }
  329. }
  330. Rectangle {
  331. id: formBg
  332. width: mainStack.width
  333. height: mainStack.height + footerBg.height
  334. x: root.width / 2 - width / 2
  335. y: root.height / 2 - height / 2 + footerBg.height
  336. radius: 14
  337. color: "#21232D"
  338. opacity: 1
  339. z:-1
  340. }
  341. Rectangle {
  342. id: footerBg
  343. width: parent.width
  344. height: footer.height + 10
  345. anchors.left: parent.left
  346. anchors.top: parent.top
  347. radius: 7
  348. color: "#21232D"
  349. opacity: 1
  350. z:-1
  351. }
  352. //Footer
  353. RowLayout {
  354. id: footer
  355. anchors {
  356. top: parent.top
  357. left: parent.left
  358. margins: units.smallSpacing
  359. }
  360. Behavior on opacity {
  361. OpacityAnimator {
  362. duration: units.longDuration
  363. }
  364. }
  365. PlasmaComponents.ToolButton {
  366. text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to show/hide virtual keyboard", "Virtual Keyboard")
  367. iconName: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off"
  368. onClicked: inputPanel.showHide()
  369. visible: true
  370. }
  371. KeyboardButton {
  372. }
  373. SessionButton {
  374. id: sessionButton
  375. }
  376. }
  377. RowLayout {
  378. id: footerRight
  379. spacing: 10
  380. anchors {
  381. top: parent.top
  382. right: parent.right
  383. margins: 10
  384. }
  385. Behavior on opacity {
  386. OpacityAnimator {
  387. duration: units.longDuration
  388. }
  389. }
  390. Battery {}
  391. Clock {
  392. id: clock
  393. visible: true
  394. }
  395. }
  396. }
  397. Connections {
  398. target: sddm
  399. onLoginFailed: {
  400. notificationMessage = i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Login Failed")
  401. }
  402. onLoginSucceeded: {
  403. //note SDDM will kill the greeter at some random point after this
  404. //there is no certainty any transition will finish, it depends on the time it
  405. //takes to complete the init
  406. mainStack.opacity = 0
  407. footer.opacity = 0
  408. footerRight.opacity = 0
  409. }
  410. }
  411. onNotificationMessageChanged: {
  412. if (notificationMessage) {
  413. notificationResetTimer.start();
  414. }
  415. }
  416. Timer {
  417. id: notificationResetTimer
  418. interval: 3000
  419. onTriggered: notificationMessage = ""
  420. }
  421. }