1import QtQuick 2.5 2import QtQuick.Controls 1.0 3import QtGraphicalEffects 1.0 4import "themes.js" as Themes 5 6Item { 7 8 id: root 9 10 property bool ctrl: false 11 12 width: frame.width+2*preferences.shadow_size 13 height: frame.height+2*preferences.shadow_size 14 15 layer.enabled: true 16 layer.effect: DropShadow { 17 transparentBorder: true 18 verticalOffset: preferences.shadow_size/3 19 radius: preferences.shadow_size 20 samples: preferences.shadow_size*2 21 color: preferences.shadow_color 22 } 23 24 Preferences { 25 id: preferences 26 objectName: "preferences" 27 } 28 29 FontLoader { 30 source: "fonts/Roboto-Thin.ttf" 31 onStatusChanged: if (loader.status === FontLoader.Ready) preferences.font_name = fontname 32 } 33 34 Rectangle { 35 36 id: frame 37 objectName: "frame" // for C++ 38 x: preferences.shadow_size; 39 y: preferences.shadow_size 40 width: preferences.window_width 41 height: content.height+2*content.anchors.margins 42 radius: preferences.radius 43 color: preferences.background_color 44 Behavior on color { ColorAnimation { duration: preferences.animation_duration; easing.type: Easing.OutCubic } } 45 Behavior on border.color { ColorAnimation { duration: preferences.animation_duration; easing.type: Easing.OutCubic } } 46 border.color: preferences.border_color 47 border.width: preferences.border_size 48 49 Column { 50 51 id: content 52 anchors { 53 top: parent.top 54 left: parent.left 55 right: parent.right 56 margins: preferences.border_size+preferences.padding 57 } 58 spacing: preferences.spacing 59 60 HistoryTextInput { 61 id: historyTextInput 62 anchors { 63 left: parent.left; 64 right: parent.right; 65 } 66 clip: true 67 color: preferences.input_color 68 focus: true 69 font.pixelSize: preferences.input_fontsize 70 font.family: preferences.font_name 71 selectByMouse: true 72 selectedTextColor: preferences.background_color 73 selectionColor: preferences.selection_color 74 Keys.forwardTo: [root, resultsList] 75 cursorDelegate : Item { 76 Rectangle { 77 width: 1 78 height: parent.height 79 color: preferences.cursor_color 80 } 81 SequentialAnimation on opacity { 82 id: animation 83 loops: Animation.Infinite; 84 NumberAnimation { to: 0; duration: 750; easing.type: Easing.InOutExpo } 85 NumberAnimation { to: 1; duration: 750; easing.type: Easing.InOutExpo } 86 } 87 Connections { 88 target: historyTextInput 89 onTextChanged: { opacity=1; animation.restart() } 90 onCursorPositionChanged: { opacity=1; animation.restart() } 91 } 92 } 93 onTextChanged: { root.state="" } 94 } // historyTextInput 95 96 DesktopListView { 97 id: resultsList 98 width: parent.width 99 model: resultsModel 100 itemCount: preferences.max_items 101 delegate: Component { 102 ItemViewDelegate{ 103 iconSize: preferences.icon_size 104 spacing: preferences.spacing 105 textSize: preferences.item_title_fontsize 106 descriptionSize: preferences.item_description_fontsize 107 textColor: preferences.foreground_color 108 highlightColor: preferences.highlight_color 109 fontName: preferences.font_name 110 animationDuration: preferences.animation_duration 111 } 112 } 113 Keys.onEnterPressed: (event.modifiers===Qt.KeypadModifier) ? root.activate(resultsList.currentIndex) : root.activate(resultsList.currentIndex,-event.modifiers) 114 Keys.onReturnPressed: (event.modifiers===Qt.NoModifier) ? root.activate(resultsList.currentIndex) : root.activate(resultsList.currentIndex,-event.modifiers) 115 onCurrentIndexChanged: if (root.state==="detailsView") root.state="" 116 } // resultsList (ListView) 117 118 DesktopListView { 119 id: actionsListView 120 width: parent.width 121 model: ListModel { id: actionsModel } 122 itemCount: actionsModel.count 123 spacing: preferences.spacing 124 Behavior on visible { 125 SequentialAnimation { 126 PropertyAction { } 127 NumberAnimation { target: actionsListView; property: "opacity"; from:0; to: 1; duration: preferences.animation_duration } 128 } 129 } 130 delegate: Text { 131 horizontalAlignment: Text.AlignHCenter 132 width: parent.width 133 text: name 134 textFormat: Text.PlainText 135 font.family: preferences.font_name 136 elide: Text.ElideRight 137 font.pixelSize: (preferences.item_description_fontsize+preferences.item_title_fontsize)/2 138 color: ListView.isCurrentItem ? preferences.highlight_color : preferences.foreground_color 139 Behavior on color { ColorAnimation{ duration: preferences.animation_duration } } 140 MouseArea { 141 anchors.fill: parent 142 onClicked: actionsListView.currentIndex = index 143 onDoubleClicked: root.activate(resultsList.currentIndex, actionsListView.currentIndex) 144 } 145 } 146 visible: false 147 Keys.onEnterPressed: root.activate(resultsList.currentIndex, actionsListView.currentIndex) 148 Keys.onReturnPressed: root.activate(resultsList.currentIndex, actionsListView.currentIndex) 149 } // actionsListView (ListView) 150 } // content (Column) 151 152 153 SettingsButton { 154 id: settingsButton 155 size: preferences.settingsbutton_size 156 color: preferences.settingsbutton_color 157 hoverColor: preferences.settingsbutton_hover_color 158 onLeftClicked: settingsWidgetRequested() 159 onRightClicked: menu.popup() 160 anchors { 161 top: parent.top 162 right: parent.right 163 topMargin: preferences.padding+preferences.border_size 164 rightMargin: preferences.padding+preferences.border_size 165 } 166 167 Menu { 168 id: menu 169 MenuItem { 170 text: "Settings" 171 shortcut: "Alt+," 172 onTriggered: settingsWidgetRequested() 173 } 174 MenuItem { 175 text: "Quit" 176 shortcut: "Alt+F4" 177 onTriggered: Qt.quit() 178 179 } 180 } 181 } 182 } // frame (Rectangle) 183 184 onActiveFocusChanged: state="" 185 186 // Key handling 187 Keys.onPressed: { 188 event.accepted = true 189 if (state === "" 190 && ((event.key === Qt.Key_Up && resultsList.currentIndex === 0 && !event.isAutoRepeat) // top item selected 191 || (event.modifiers === Qt.ControlModifier && (event.key === Qt.Key_P || event.key === Qt.Key_Up)))){ // whatever item, using control modifier 192 state == "" 193 historyTextInput.forwardSearchHistory() 194 } 195 else if (event.modifiers === Qt.ControlModifier && (event.key === Qt.Key_Down || event.key === Qt.Key_N)) { 196 state == "" 197 historyTextInput.backwardSearchHistory() 198 } 199 else if (event.key === Qt.Key_Meta) { 200 if (resultsList.currentIndex === -1) 201 resultsList.currentIndex = 0 202 state="fallback" 203 } 204 else if (event.key === Qt.Key_Comma && (event.modifiers === Qt.AltModifier || event.modifiers === Qt.ControlModifier)) { 205 settingsWidgetRequested() 206 } 207 else if (event.key === Qt.Key_Alt && resultsList.count > 0) { 208 if (resultsList.currentIndex === -1) 209 resultsList.currentIndex = 0 210 state = "detailsView" 211 } 212 else if (48 <= event.key && event.key <= 57 && event.modifiers === Qt.ControlModifier){ 213 var num = 9 214 if (event.key !== Qt.Key_0) 215 num = event.key - 49 216 if (num < preferences.max_items && num < resultsList.count) { 217 var index = resultsList.indexAt(0, resultsList.contentY) 218 index += num 219 resultsList.currentIndex = index 220 root.activate(resultsList.currentIndex) 221 } 222 } 223 else if ( event.key === Qt.Key_Control ) 224 ctrl = true 225 else if ( event.key === Qt.Key_Tab && resultsList.count > 0 ) { 226 if ( resultsList.currentIndex === -1 ) 227 resultsList.currentIndex = 0 228 historyTextInput.text = resultsList.currentItem.attachedModel.itemCompletionStringRole 229 } else event.accepted = false 230 } 231 Keys.onReleased: { 232 event.accepted = true 233 if ( event.key === Qt.Key_Meta ) 234 state="" 235 else if ( event.key === Qt.Key_Alt ) 236 state="" 237 else if ( event.key === Qt.Key_Control ) 238 ctrl = false 239 else event.accepted = false 240 241 } 242 243 states : [ 244 State { 245 name: "" 246 StateChangeScript { 247 name: "defaultStateScript" 248 script: { 249 actionsModel.clear() 250 } 251 } 252 }, 253 State { 254 name: "fallback" 255 }, 256 State { 257 name: "detailsView" 258 PropertyChanges { target: actionsListView; visible: true } 259 PropertyChanges { target: historyTextInput; Keys.forwardTo: [root, actionsListView] } 260 StateChangeScript { 261 name: "detailsViewStateScript" 262 script: { 263 var actionTexts = resultsList.currentItem.actionsList(); 264 for ( var i = 0; i < actionTexts.length; i++ ) 265 actionsModel.append({"name": actionTexts[i]}); 266 } 267 } 268 } 269 ] 270 271 Connections { 272 target: mainWindow 273 onVisibleChanged: { 274 if (!arg) { 275 276 // Save the text if the text displayed has been entered by the user 277 if (historyTextInput.userText === historyTextInput.text) 278 historyTextInput.pushTextToHistory() 279 280 // Reset state 281 state="" 282 ctrl=false 283 historyTextInput.selectAll() 284 historyTextInput.userText = null 285 historyTextInput.resetHistoryMode() 286 } 287 } 288 } 289 290 Component.onCompleted: setTheme("Bright") 291 292 293 // ▼ ▼ ▼ ▼ ▼ DO NOT CHANGE THIS UNLESS YOU KNOW WHAT YOU ARE DOING ▼ ▼ ▼ ▼ ▼ 294 295 /* 296 * Currently the interface with the program logic comprises the following: 297 * 298 * These context properties are set: 299 * - mainWindow 300 * - resultsModel 301 * - history 302 * 303 * These properties must exist in root: 304 * - inputText (string, including the implicitly genreated signal) 305 * 306 * These functions must extist in root: 307 * - availableThemes() 308 * - setTheme(str) 309 * 310 * These signals must exist in root: 311 * - settingsWidgetRequested() 312 * 313 * These object names with must exist somewhere: 314 * - frame (the visual root frame, i.e. withouth shadow) 315 * - preferences (QtObject containing only preference propterties) 316 */ 317 property string interfaceVersion: "1.0-alpha" // Will not change until beta 318 319 property alias inputText: historyTextInput.text 320 signal settingsWidgetRequested() 321 322 function activate(index, action) { 323 if ( resultsList.count > 0 ) { 324 resultsList.currentIndex = index 325 if ( resultsList.currentIndex === -1 ) 326 resultsList.currentIndex = 0 327 resultsList.currentItem.activate(action) 328 329 // Order important! (hide triggers root.onVisibleChanged()) 330 historyTextInput.pushTextToHistory() 331 mainWindow.hide() 332 historyTextInput.text = "" 333 } 334 } 335 336 function availableThemes() { return Object.keys(Themes.themes()) } 337 function setTheme(themeName) { 338 var themeObject = Themes.themes()[themeName] 339 for (var property in themeObject) 340 if (themeObject.hasOwnProperty(property)) 341 preferences[property] = themeObject[property] 342 } 343} 344