Commit 51265a95 authored by Sergey Chupligin's avatar Sergey Chupligin Committed by Sergey Chupligin

[AppLauncher] Rework search panel

parent 730971ed
// This file is part of colorful-home, a nice user experience for touchscreens.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
......@@ -22,308 +21,177 @@
// Copyright (c) 2011, Tom Swindell <t.swindell@rubyx.co.uk>
// Copyright (c) 2012, Timur Kristóf <venemo@fedoraproject.org>
// Copyright (c) 2017, Eetu Kahelin
// Copyright (c) 2018, Chupligin Sergey <neochapay@gmail.com>
import QtQuick 2.6
import org.nemomobile.lipstick 0.1
import org.nemomobile.configuration 1.0
import QtQuick.Controls.Nemo 1.0
import QtQuick.Controls.Styles.Nemo 1.0
import "applauncher"
// App Launcher page
// the place for browsing installed applications and launching them
GridView {
id: gridview
width: cellWidth * columns
cacheBuffer: gridview.contentHeight
property Item reorderItem
property bool onUninstall
property alias deleter: deleter
Flickable{
id: appLauncher
width: parent.width
height: desktop.height
property var switcher: null
property string searchString
property int minCellSize: Theme.iconSizeLauncher + Theme.iconSizeLauncher/2
property int rows: Math.floor(parent.height / minCellSize)
property int columns: Math.floor(parent.width / minCellSize)
cellWidth: parent.width / columns
cellHeight: Math.round(parent.height / rows)
property int folderIndex: -1
property bool isRootFolder:true
property bool newFolderActive
property bool newFolder: newFolderActive && isRootFolder && folderIndex >= 0
clip: true
onContentYChanged: {
if( contentY < -Theme.itemHeightHuge*2 ) {
headerItem.visible = true;
timer.running = true;
}
ConfigurationValue {
id: alwaysShowSearch
key: "/home/glacier/appLauncher/alwaysShowSearch"
defaultValue: true
}
onSearchStringChanged: timer.restart()
Timer{
id: timer; running: false; interval: 7000; repeat: true
onTriggered: {
if(searchString.length < 1 ) headerItem.visible = false
/*top search line*/
SearchListView {
id: searchListView
width: appLauncher.width
visible: alwaysShowSearch
Timer{
id: searchListViewTimer;
running: false;
interval: 7000;
repeat: true
onTriggered: {
if(searchString.length < 1 && !alwaysShowSearch)
{
headerItem.visible = false
}
}
}
}
Connections {
target: headerItem
onHeightChanged:{
if(headerItem.oldHeight < headerItem.height)
if(!flicking) gridview.contentY = headerItem.y
headerItem.oldHeight = headerItem.height
target: lockScreen
onVisibleChanged: {
if(lockscreenVisible()) {
searchListView.cleanup()
}
}
onVisibleChanged:timer.restart()
}
Connections {
target: Lipstick.compositor
onDisplayOff: {
headerItem.searchField.text = ""
headerItem.visible = false
searchListView.cleanup()
}
onWindowAdded: {
if(window.category=="" && window.title !== "Home"){
headerItem.searchField.text = ""
headerItem.visible = false
if(window.category === "" && window.title !== "Home"){
searchListView.cleanup()
}
}
onWindowRaised: {
if(window.category=="" && window.title !== "Home"){
headerItem.searchField.text = ""
headerItem.visible = false
if(window.category === "" && window.title !== "Home"){
searchListView.cleanup()
}
}
}
Connections {
target: pager
onFlickEnded: {
headerItem.searchField.text = ""
headerItem.visible = false
}
}
Connections {
target: lockScreen
onVisibleChanged: {
if(lockscreenVisible()) {
headerItem.searchField.text = ""
headerItem.visible = false
}
}
}
onSearchStringChanged: searchListViewTimer.restart()
header: SearchListView {
width: gridview.width
}
/*app grid*/
GridView {
id: gridview
width: parent.width
height: parent.height-searchListView.height-Theme.itemSpacingHuge
footer: Item {
height: Theme.itemHeightLarge*1.5
}
visible: searchString.length === 0
Item {//Doesn't yet uninstall applications
id: deleter
anchors.top: parent.top
property alias remove: remove
property alias uninstall: uninstall
function uninstalling(action, caption) {
state = action
if (action==="remove") {
remove.text = qsTr("Removing") + " " + caption
} else if (action == "uninstall") {
uninstall.text = qsTr("Uninstalling") + " " + caption
}
}
cacheBuffer: gridview.contentHeight
property Item reorderItem
property bool onUninstall
states: [
State {
name: "remove"
PropertyChanges {
target: remove
color1: "#D9ff0000"
color2: "#D9ff0000"
color3: "#D9ff0000"
}
PropertyChanges {
target: uninstall
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: uninstall
text: qsTr("Uninstall")
}
},
State {
name: "uninstall"
PropertyChanges {
target: uninstall
color1: "#D9ff0000"
color2: "#D9ff0000"
color3: "#D9ff0000"
}
PropertyChanges {
target: remove
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: remove
text: qsTr("Remove")
}
},
State {
name:"basic"
PropertyChanges {
target: remove
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: remove
text: qsTr("Remove")
}
PropertyChanges {
target: uninstall
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: uninstall
text: qsTr("Uninstall")
}
}
]
property int minCellSize: Theme.iconSizeLauncher + Theme.iconSizeLauncher/2
property int rows: Math.floor(parent.height / minCellSize)
property int columns: Math.floor(parent.width / minCellSize)
Rectangle {//WHY?
id: remove
property color color1: "#D9ff0000"
property color color2: "#80ff0000"
property color color3: "#4Dff0000"
property alias text: removeLabel.text
anchors.left: parent.left
visible: gridview.onUninstall
height: Theme.itemHeightExtraLarge
width: gridview.width / 2
gradient: Gradient {
GradientStop { position: 0.0; color: remove.color1 }
GradientStop { position: 0.5; color: remove.color2 }
GradientStop { position: 1.0; color: remove.color3 }
}
Row {
width: parent.width
height: parent.height
Image {
id:removeIcon
fillMode: Image.PreserveAspectFit
height: parent.height -Theme.itemSpacingExtraSmall
width: height
anchors.verticalCenter: parent.verticalCenter
source: "image://theme/remove"
visible: deleter.state != "remove"
}
cellWidth: parent.width / columns
cellHeight: Math.round(parent.height / rows)
Label {
id: removeLabel
text: qsTr("Remove")
height: parent.height
width: parent.width - (removeIcon.visible ? removeIcon.width : 0)
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: Theme.fontSizeSmall
elide:Text.ElideRight
horizontalAlignment:Text.AlignHCenter
verticalAlignment:Text.AlignVCenter
}
}
anchors{
top: searchListView.bottom
topMargin: Theme.itemSpacingHuge
}
Rectangle {
id: uninstall
property color color1: "#D9ff0000"
property color color2: "#80ff0000"
property color color3: "#4Dff0000"
property alias text: uninstallLabel.text
anchors.left: remove.right
visible: gridview.onUninstall
width: gridview.width / 2
height: Theme.itemHeightExtraLarge
gradient: Gradient {
GradientStop { position: 0.0; color: uninstall.color1 }
GradientStop { position: 0.5; color: uninstall.color2 }
GradientStop { position: 1.0; color: uninstall.color3 }
}
Row {
width: parent.width
height: parent.height
Image {
id:trashIcon
fillMode: Image.PreserveAspectFit
height: parent.height -Theme.itemSpacingExtraSmall
width: height
anchors.verticalCenter: parent.verticalCenter
source: "image://theme/trash"
visible: deleter.state != "uninstall"
}
Label {
id: uninstallLabel
height: parent.height
width: parent.width - (trashIcon.visible ? trashIcon.width : 0)
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Uninstall")
font.pixelSize: Theme.fontSizeSmall
elide:Text.ElideRight
horizontalAlignment:Text.AlignHCenter
verticalAlignment:Text.AlignVCenter
}
property int folderIndex: -1
property bool isRootFolder:true
property bool newFolderActive
property bool newFolder: newFolderActive && isRootFolder && folderIndex >= 0
clip: true
/*onContentYChanged: {
if( contentY < -Theme.itemHeightHuge ) {
headerItem.visible = true;
timer.running = true;
}
}*/
footer: Item {
height: Theme.itemHeightLarge*1.5
}
}
onFolderIndexChanged: if (folderIndex == -1) newFolderActive = false
onFolderIndexChanged: if (folderIndex == -1) newFolderActive = false
model: LauncherFolderModel { id: launcherModel }
//Using loader that in the future we can also have widgets as delegate
delegate: Loader {
id:loader
width: cellWidth
height: cellHeight
onXChanged: item.x = x
onYChanged: item.y = y
property QtObject modelData : model
property int cellIndex: index
sourceComponent: object.type == LauncherModel.Folder ? folder : app
}
model: LauncherFolderModel{
id: launcherModel
}
Component {
id:app
LauncherItemDelegate {
id: launcherItem
parent: gridview
parentItem: gridview
iconCaption.color:Theme.textColor
iconCaption.text: modelData.object.title
isFolder: modelData.object.type == LauncherModel.Folder
source: modelData.object.iconId == "" ? "/usr/share/lipstick-glacier-home-qt5/qml/theme/default-icon.png" : (modelData.object.iconId.indexOf("/") == 0 ? "file://" : "image://theme/") + modelData.object.iconId
notNemoIcon: isFolder || modelData.object.iconId == "" ? false : modelData.object.iconId.indexOf("harbour") > -1 || modelData.object.iconId.indexOf("apkd_launcher") > -1 ? true : false
folderModel:launcherModel
//Using loader that in the future we can also have widgets as delegate
delegate: Loader {
id:loader
width: gridview.cellWidth
height: gridview.cellHeight
onXChanged: item.x = x
onYChanged: item.y = y
property QtObject modelData : model
property int cellIndex: index
sourceComponent: object.type == LauncherModel.Folder ? folder : app
}
Component {
id:app
LauncherItemDelegate {
id: launcherItem
parent: gridview
parentItem: gridview
iconCaption.color:Theme.textColor
iconCaption.text: modelData.object.title
isFolder: modelData.object.type == LauncherModel.Folder
source: modelData.object.iconId == "" ? "/usr/share/lipstick-glacier-home-qt5/qml/theme/default-icon.png" : (modelData.object.iconId.indexOf("/") == 0 ? "file://" : "image://theme/") + modelData.object.iconId
notNemoIcon: isFolder || modelData.object.iconId == "" ? false : modelData.object.iconId.indexOf("harbour") > -1 || modelData.object.iconId.indexOf("apkd_launcher") > -1 ? true : false
folderModel:launcherModel
}
}
Component {
id:folder
LauncherItemFolder {
id: launcherfolder
parent: gridview
iconCaption.color:Theme.textColor
iconCaption.text: modelData.object.title
isFolder: modelData.object.type == LauncherModel.Folder
folderAppsCount: isFolder && modelData.object ? modelData.object.itemCount : 0
notNemoIcon: isFolder || modelData.object.iconId == "" ? false : modelData.object.iconId.indexOf("harbour") > -1 || modelData.object.iconId.indexOf("apkd_launcher") > -1 ? true : false
folderModel:launcherModel
}
}
}
Component {
id:folder
LauncherItemFolder {
id: launcherfolder
parent: gridview
iconCaption.color:Theme.textColor
iconCaption.text: modelData.object.title
isFolder: modelData.object.type == LauncherModel.Folder
folderAppsCount: isFolder && modelData.object ? modelData.object.itemCount : 0
notNemoIcon: isFolder || modelData.object.iconId == "" ? false : modelData.object.iconId.indexOf("harbour") > -1 || modelData.object.iconId.indexOf("apkd_launcher") > -1 ? true : false
folderModel:launcherModel
Deleter{
id: deleter
anchors{
bottom: parent.bottom
}
state: "uninstall"
}
}
......@@ -43,6 +43,7 @@ import org.nemomobile.devicelock 1.0
import "scripts/desktop.js" as Desktop
Page {
id: desktop
// This is used in the favorites page and in the lock screen
WallClock {
id: wallClock
......@@ -63,7 +64,7 @@ Page {
key: "/home/glacier/homeScreen/wallpaperImage"
defaultValue: "/usr/share/lipstick-glacier-home-qt5/qml/images/wallpaper-portrait-bubbles.png"
}
id: desktop
property alias lockscreen: lockScreen
property alias switcher: switcher
property int statusBarHeight: statusbar.height
......
// This file is part of glacier-home, a nice user experience for touchscreens.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Copyright (c) 2018, Chupligin Sergey <neochapay@gmail.com>
import QtQuick 2.6
import QtQuick.Controls.Nemo 1.0
import QtQuick.Controls.Styles.Nemo 1.0
Item {//Doesn't yet uninstall applications
id: deleter
property alias remove: remove
property alias uninstall: uninstall
function uninstalling(action, caption) {
state = action
if (action==="remove") {
remove.text = qsTr("Removing") + " " + caption
} else if (action == "uninstall") {
uninstall.text = qsTr("Uninstalling") + " " + caption
}
}
states: [
State {
name: "remove"
PropertyChanges {
target: remove
color1: "#D9ff0000"
color2: "#D9ff0000"
color3: "#D9ff0000"
}
PropertyChanges {
target: uninstall
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: uninstall
text: qsTr("Uninstall")
}
},
State {
name: "uninstall"
PropertyChanges {
target: uninstall
color1: "#D9ff0000"
color2: "#D9ff0000"
color3: "#D9ff0000"
}
PropertyChanges {
target: remove
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: remove
text: qsTr("Remove")
}
},
State {
name:"basic"
PropertyChanges {
target: remove
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: remove
text: qsTr("Remove")
}
PropertyChanges {
target: uninstall
color1: "#D9ff0000"
color2: "#80ff0000"
color3: "#4Dff0000"
}
PropertyChanges {
target: uninstall
text: qsTr("Uninstall")
}
}
]
Rectangle {//WHY?
id: remove
property color color1: "#D9ff0000"
property color color2: "#80ff0000"
property color color3: "#4Dff0000"
property alias text: removeLabel.text
anchors.left: parent.left
visible: gridview.onUninstall
height: Theme.itemHeightExtraLarge
width: gridview.width / 2
gradient: Gradient {
GradientStop { position: 0.0; color: remove.color1 }
GradientStop { position: 0.5; color: remove.color2 }
GradientStop { position: 1.0; color: remove.color3 }
}
Row {
width: parent.width
height: parent.height
Image {
id:removeIcon
fillMode: Image.PreserveAspectFit
height: parent.height -Theme.itemSpacingExtraSmall
width: height
anchors.verticalCenter: parent.verticalCenter
source: "image://theme/remove"
visible: deleter.state != "remove"
}
Label {
id: removeLabel
text: qsTr("Remove")
height: parent.height
width: parent.width - (removeIcon.visible ? removeIcon.width : 0)
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: Theme.fontSizeSmall
elide:Text.ElideRight
horizontalAlignment:Text.AlignHCenter
verticalAlignment:Text.AlignVCenter
}
}
}
Rectangle {
id: uninstall
property color color1: "#D9ff0000"
property color color2: "#80ff0000"
property color color3: "#4Dff0000"
property alias text: uninstallLabel.text
anchors.left: remove.right
visible: gridview.onUninstall
width: gridview.width / 2
height: Theme.itemHeightExtraLarge
gradient: Gradient {
GradientStop { position: 0.0; color: uninstall.color1 }
GradientStop { position: 0.5; color: uninstall.color2 }
GradientStop { position: 1.0; color: uninstall.color3 }
}
Row {
width: parent.width
height: parent.height
Image {
id:trashIcon
fillMode: Image.PreserveAspectFit
height: parent.height -Theme.itemSpacingExtraSmall
width: height
anchors.verticalCenter: parent.verticalCenter
source: "image://theme/trash"
visible: deleter.state != "uninstall"
}
Label {
id: uninstallLabel
height: parent.height
width: parent.width - (trashIcon.visible ? trashIcon.width : 0)
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Uninstall")
font.pixelSize: Theme.fontSizeSmall
elide:Text.ElideRight
horizontalAlignment:Text.AlignHCenter
verticalAlignment:Text.AlignVCenter
}
}
}
}
/****************************************************************************************
**
** Copyright (c) 2017, Eetu Kahelin
** Copyright (c) 2018, Chupligin Sergey
** All rights reserved.
**
** You may use this file under the terms of BSD license as follows:
......@@ -47,6 +48,22 @@ Item {
NumberAnimation{ duration: 300 }
}
InverseMouseArea {
anchors.fill: parent
onPressed: cleanup()
}
function cleanup(){
searchField.focus = false
appLauncher.searchString = ""
searchField.text = ""
if(!alwaysShowSearch)
{
searchListView.visible = false;
}
}
onVisibleChanged: {
if( visible){
......@@ -85,12 +102,12 @@ Item {
width:parent.width - searchIcon.width - Theme.itemSpacingMedium
placeholderText: qsTr("Search")
Binding {
target: gridview
target: appLauncher
property: "searchString"
value: searchField.text.toLowerCase().trim()
}
onTextChanged: {
if(tex.lenght>0) {
if(searchField.lenght>0) {
searchField.forceActiveFocus()
}
}
......@@ -155,7 +172,7 @@ Item {
}
Connections {
target: gridview
target: appLauncher
onSearchStringChanged: listView.update()
}
......@@ -190,15 +207,34 @@ Item {
for (i = 0; i < searchLauncherModel.itemCount; ++i) {
if (searchLauncherModel.get(i).type === LauncherModel.Folder) {
for(var j = 0; j< searchLauncherModel.get(i).itemCount; ++j ) {
titles.push({'iconTitle':searchLauncherModel.get(i).get(j).title, 'iconSource':searchLauncherModel.get(i).get(j).iconId, 'id':i, 'folderId':j, 'category':qsTr("Application")})
titles.push({
'iconTitle':searchLauncherModel.get(i).get(j).title,
'iconSource':searchLauncherModel.get(i).get(j).iconId,
'id':i,
'folderId':j,
'category':qsTr("Application"),
'extraCaption': qsTr("installed on you device")
})
}
} else {
titles.push({'iconTitle':searchLauncherModel.get(i).title, 'iconSource':searchLauncherModel.get(i).iconId, 'id':i, 'folderId':-1, 'category':qsTr("Application")})
titles.push({
'iconTitle':searchLauncherModel.get(i).title,
'iconSource':searchLauncherModel.get(i).iconId,
'id':i,
'folderId':-1,
'category':qsTr("Application"),
'extraCaption': qsTr("installed on you device")
})
}
}
for (i = 0; i < peopleModel.count; ++i) {
if(peopleModel.get(i).firstName && peopleModel.get(i).lastName) {
contacts.push({'title':(peopleModel.get(i).firstName + " " + peopleModel.get(i).lastName), 'iconSource':peopleModel.get(i).avatarUrl.toString(), 'extraCaption':peopleModel.get(i).phoneNumbers, 'category':qsTr("Contact")})
contacts.push({
'title':(peopleModel.get(i).firstName + " " + peopleModel.get(i).lastName),
'iconSource':peopleModel.get(i).avatarUrl.toString(),
'extraCaption':peopleModel.get(i).phoneNumbers,
'category':qsTr("Contact")
})
}
}
var filteredTitles = titles.filter(function (icon) {
......@@ -236,7 +272,7 @@ Item {
found = existingTitleObject.hasOwnProperty(iconTitle)
if (!found) {
// for simplicity, just adding to end instead of corresponding position in original list
listModel.append({'title':iconTitle, 'iconSource':iconId, 'id':id, 'folderId':folderId, 'category':category})
listModel.append({'title':iconTitle, 'iconSource':iconId, 'id':id, 'folderId':folderId, 'category':category, 'extraCaption': ""})
}
}
for (i = 0; i < contacts.length; ++i) {
......
......@@ -46,8 +46,7 @@ qml.files = qml/MainScreen.qml \
qml/GlacierRotation.qml \
qml/DeviceLockUI.qml \
qml/LauncherItemWrapper.qml \
qml/LauncherItemFolder.qml \
qml/SearchListView.qml
qml/LauncherItemFolder.qml
qmlcompositor.path = /usr/share/lipstick-glacier-home-qt5/qml/compositor
qmlcompositor.files = qml/compositor/WindowWrapperMystic.qml \
......@@ -83,6 +82,10 @@ statusbar.files = qml/statusbar/BatteryPanel.qml\
qml/statusbar/NumButton.qml \
qml/statusbar/MediaController.qml
applauncher.path = /usr/share/lipstick-glacier-home-qt5/qml/applauncher
applauncher.files = qml/applauncher/SearchListView.qml \
qml/applauncher/Deleter.qml
settingswallpaperplugin.files = settings-plugins/wallpaper/wallpaper.qml \
settings-plugins/wallpaper/selectImage.qml \
settings-plugins/wallpaper/wallpaper.svg
......@@ -112,7 +115,8 @@ INSTALLS += styles \
statusbar\
settingswallpaperplugin\
settingsnotificationsplugin\
settingspluginconfig
settingspluginconfig \
applauncher
CONFIG += qt link_pkgconfig
QT += quick compositor
......@@ -130,43 +134,17 @@ PKGCONFIG += lipstick-qt5 \
nemodevicelock
OTHER_FILES += qml/*.qml \
qml/MainScreen.qml \
qml/compositor.qml \
qml/LauncherItemDelegate.qml \
qml/Lockscreen.qml \
qml/LockscreenClock.qml \
qml/AppSwitcher.qml \
qml/AppLauncher.qml \
qml/ToolBarLayoutExample.qml \
qml/SwitcherItem.qml \
qml/CloseButton.qml \
qml/compositor/WindowWrapperMystic.qml \
qml/compositor/WindowWrapperBase.qml \
qml/compositor/WindowWrapperAlpha.qml \
qml/compositor/ScreenGestureArea.qml \
qml/NotificationPreview.qml \
qml/compositor/*.qml \
qml/scripts/desktop.js \
qml/FeedsPage.qml \
qml/Statusbar.qml \
qml/StatusbarItem.qml \
qml/WifiPanel.qml \
nemovars.conf \
qml/SimPanel.qml \
qml/NumButton.qml \
qml/USBModeSelector.qml \
qml/VolumeControl.qml \
qml/BatteryPanel.qml \
qml/CommonPanel.qml \
qml/ShutdownScreen.qml \
qml/GlacierRotation.qml
nemovars.conf \
qml/connectivity/*.qml
TRANSLATIONS += i18n/glacer-home.ts
DISTFILES += \
i18n/glacer-home.ts \
qml/connectivity/ConnectionSelector.qml \
qml/statusbar/BatteryIndicator.qml \
settings-plugins/wallpaper/selectImage.qml \
settings-plugins/notifications/notifications.json \
settings-plugins/notifications/notifications.svg \
settings-plugins/notifications/notifications.qml
qml/*/*.qml \
settings-plugins/*/*.qml \
settings-plugins/*/*.json \
settings-plugins/*/*.svg
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment