mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +00:00
homescreen: Add search widget
This commit is contained in:
parent
f849a5fea5
commit
f22a1e0b8c
6 changed files with 322 additions and 18 deletions
|
|
@ -12,6 +12,8 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
|||
|
||||
/**
|
||||
* State object for the homescreen.
|
||||
*
|
||||
* We expose the data necessary to make custom "swipe-down" gestures from the page view.
|
||||
*/
|
||||
QtObject {
|
||||
id: root
|
||||
|
|
@ -87,7 +89,7 @@ QtObject {
|
|||
SwipingPages, // horizontal movement between pages
|
||||
SwipingAppDrawerVisibility, // opening/closing app drawer
|
||||
SwipingAppDrawerList, // scrolling app drawer
|
||||
SwipingActionPanel, // pulling down action panel
|
||||
SwipingPagesDown, // custom gesture can be implemented for swiping down on the page view
|
||||
DeterminingType
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +134,11 @@ QtObject {
|
|||
xAnim.stop();
|
||||
}
|
||||
|
||||
// expose signals necessary to implement any behaviour for the "swipe-down" action on the page view
|
||||
signal swipeDownGestureBegin
|
||||
signal swipeDownGestureEnd
|
||||
signal swipeDownGestureOffset(real value)
|
||||
|
||||
// be very careful when resetting the swipe state
|
||||
// ensure that we aren't in the middle of a gesture
|
||||
function resetSwipeState() {
|
||||
|
|
@ -191,8 +198,8 @@ QtObject {
|
|||
xDetermineSwipePosition = 0;
|
||||
yDetermineSwipePosition = 0;
|
||||
} else if (yDetermineSwipePosition >= verticalSwipeStateDetermineThreshold) {
|
||||
currentSwipeState = HomeScreenState.SwipingActionPanel;
|
||||
MobileShell.TopPanelControls.startSwipe();
|
||||
currentSwipeState = HomeScreenState.SwipingPagesDown;
|
||||
root.swipeDownGestureBegin();
|
||||
xDetermineSwipePosition = 0;
|
||||
yDetermineSwipePosition = 0;
|
||||
} else if (-yDetermineSwipePosition >= verticalSwipeStateDetermineThreshold) {
|
||||
|
|
@ -206,10 +213,10 @@ QtObject {
|
|||
xPosition += x;
|
||||
break;
|
||||
|
||||
case HomeScreenState.SwipingActionPanel:
|
||||
case HomeScreenState.SwipingPagesDown:
|
||||
yPosition = pagesYPosition;
|
||||
if (y !== 0) {
|
||||
MobileShell.TopPanelControls.requestRelativeScroll(y);
|
||||
root.swipeDownGestureOffset(y);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -277,8 +284,8 @@ QtObject {
|
|||
break;
|
||||
}
|
||||
|
||||
case HomeScreenState.SwipingActionPanel: {
|
||||
MobileShell.TopPanelControls.endSwipe();
|
||||
case HomeScreenState.SwipingPagesDown: {
|
||||
root.swipeDownGestureEnd();
|
||||
root.resetSwipeState();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,9 @@ StatusBar 1.0 statusbar/StatusBar.qml
|
|||
TaskSwitcher 1.0 taskswitcher/TaskSwitcher.qml
|
||||
|
||||
# /widgets
|
||||
MediaControlsWidget 1.0 widgets/MediaControlsWidget.qml
|
||||
NotificationsWidget 1.0 widgets/NotificationsWidget.qml
|
||||
KRunnerWidget 1.0 widgets/krunner/KRunnerWidget.qml
|
||||
MediaControlsWidget 1.0 widgets/mediacontrols/MediaControlsWidget.qml
|
||||
NotificationsWidget 1.0 widgets/notifications/NotificationsWidget.qml
|
||||
|
||||
# /
|
||||
singleton HomeScreenControls 1.0 HomeScreenControls.qml
|
||||
|
|
|
|||
271
components/mobileshell/qml/widgets/krunner/KRunnerWidget.qml
Normal file
271
components/mobileshell/qml/widgets/krunner/KRunnerWidget.qml
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Aaron Seigo <aseigo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
|
||||
import org.kde.plasma.plasmoid 2.0
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||
|
||||
import org.kde.milou 0.1 as Milou
|
||||
import org.kde.kirigami 2.19 as Kirigami
|
||||
|
||||
import "../../components" as Components
|
||||
|
||||
/**
|
||||
* Search widget that is embedded into the homescreen. The dimensions of
|
||||
* the root item is assumed to be the available screen area for applications.
|
||||
*/
|
||||
Item {
|
||||
id: root
|
||||
|
||||
function startGesture() {
|
||||
queryField.text = "";
|
||||
flickable.contentY = closedContentY;
|
||||
}
|
||||
|
||||
function updateGestureOffset(yOffset) {
|
||||
flickable.contentY = Math.max(0, Math.min(closedContentY, flickable.contentY + yOffset));
|
||||
}
|
||||
|
||||
// call when the touch gesture has let go
|
||||
function endGesture() {
|
||||
flickable.opening ? open() : close();
|
||||
}
|
||||
|
||||
// open the search widget (animated)
|
||||
function open() {
|
||||
anim.to = openedContentY;
|
||||
anim.restart();
|
||||
}
|
||||
|
||||
// close the search widget (animated)
|
||||
function close() {
|
||||
anim.to = closedContentY;
|
||||
anim.restart();
|
||||
}
|
||||
|
||||
readonly property real closedContentY: PlasmaCore.Units.gridUnit * 5
|
||||
readonly property real openedContentY: 0
|
||||
readonly property real openFactor: Math.max(0, Math.min(1, 1 - flickable.contentY / closedContentY))
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.3)
|
||||
opacity: root.openFactor
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0
|
||||
anchors.rightMargin: MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth
|
||||
anchors.bottomMargin: MobileShell.TaskPanelControls.panelHeight
|
||||
|
||||
contentHeight: flickable.height + root.closedContentY + 999999
|
||||
contentY: root.closedContentY
|
||||
property real oldContentY: contentY
|
||||
property bool opening: false
|
||||
|
||||
onContentYChanged: {
|
||||
opening = contentY < oldContentY;
|
||||
oldContentY = contentY;
|
||||
}
|
||||
|
||||
onMovementEnded: root.endGesture()
|
||||
|
||||
onDraggingChanged: {
|
||||
if (!dragging) {
|
||||
root.endGesture();
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation on contentY {
|
||||
id: anim
|
||||
duration: PlasmaCore.Units.longDuration * 2
|
||||
easing.type: Easing.OutQuad
|
||||
onFinished: {
|
||||
if (root.openedContentY) {
|
||||
queryField.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
height: flickable.height
|
||||
width: flickable.width
|
||||
|
||||
Controls.Control {
|
||||
opacity: root.openFactor
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: PlasmaCore.Units.gridUnit
|
||||
Layout.rightMargin: PlasmaCore.Units.gridUnit
|
||||
|
||||
leftPadding: PlasmaCore.Units.smallSpacing / 2
|
||||
rightPadding: PlasmaCore.Units.smallSpacing / 2
|
||||
topPadding: PlasmaCore.Units.smallSpacing / 2
|
||||
bottomPadding: PlasmaCore.Units.smallSpacing / 2
|
||||
|
||||
background: Item {
|
||||
|
||||
// shadow for search window
|
||||
RectangularGlow {
|
||||
anchors.topMargin: 1
|
||||
anchors.fill: parent
|
||||
cached: true
|
||||
glowRadius: 4
|
||||
spread: 0.2
|
||||
color: Qt.rgba(0, 0, 0, 0.15)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: PlasmaCore.Theme.backgroundColor
|
||||
radius: PlasmaCore.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
Item {
|
||||
implicitHeight: queryField.height
|
||||
implicitWidth: height
|
||||
Kirigami.Icon {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Math.round(Kirigami.Units.smallSpacing * 1.5)
|
||||
source: "start-here-symbolic"
|
||||
}
|
||||
}
|
||||
Kirigami.SearchField {
|
||||
id: queryField
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controls.ScrollView {
|
||||
opacity: root.openFactor === 1 ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: PlasmaCore.Units.shortDuration }
|
||||
}
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
Milou.ResultsListView {
|
||||
id: listView
|
||||
queryString: queryField.text
|
||||
highlight: null
|
||||
PlasmaCore.ColorScope.colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||
|
||||
onActivated: queryField.text = "";
|
||||
onUpdateQueryString: {
|
||||
queryField.text = text
|
||||
queryField.cursorPosition = cursorPosition
|
||||
}
|
||||
|
||||
delegate: MouseArea {
|
||||
id: delegate
|
||||
height: rowLayout.height
|
||||
width: listView.width
|
||||
|
||||
onClicked: {
|
||||
listView.currentIndex = model.index;
|
||||
listView.runCurrentIndex();
|
||||
}
|
||||
hoverEnabled: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: delegate.pressed ? Qt.rgba(255, 255, 255, 0.2) : (delegate.containsMouse ? Qt.rgba(255, 255, 255, 0.05) : "transparent")
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: PlasmaCore.Units.shortDuration }
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
height: PlasmaCore.Units.gridUnit * 3
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: PlasmaCore.Units.largeSpacing
|
||||
anchors.rightMargin: PlasmaCore.Units.largeSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
source: model.decoration
|
||||
implicitWidth: PlasmaCore.Units.iconSizes.medium
|
||||
implicitHeight: PlasmaCore.Units.iconSizes.medium
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: PlasmaCore.Units.smallSpacing
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: title
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2
|
||||
Layout.rightMargin: PlasmaCore.Units.largeSpacing
|
||||
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
text: typeof modelData !== "undefined" ? modelData : model.display
|
||||
color: "white"
|
||||
|
||||
font.pointSize: PlasmaCore.Theme.defaultFont.pointSize
|
||||
}
|
||||
PlasmaComponents.Label {
|
||||
id: subtitle
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2
|
||||
Layout.rightMargin: PlasmaCore.Units.largeSpacing
|
||||
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
text: model.subtext || ""
|
||||
color: "white"
|
||||
opacity: 0.8
|
||||
|
||||
font.pointSize: Math.round(PlasmaCore.Theme.defaultFont.pointSize * 0.8)
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: actionsRepeater
|
||||
model: typeof actions !== "undefined" ? actions : []
|
||||
|
||||
Controls.ToolButton {
|
||||
icon.name: modelData.icon || ""
|
||||
visible: modelData.visible || true
|
||||
enabled: modelData.enabled || true
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: modelData.text
|
||||
checkable: checked
|
||||
checked: delegate.activeAction === index
|
||||
focus: delegate.activeAction === index
|
||||
onClicked: delegate.ListView.view.runAction(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@ import org.kde.plasma.components 3.0 as PlasmaComponents3
|
|||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
|
||||
import "../components" as Components
|
||||
import "mediacontrols"
|
||||
|
||||
Components.BaseItem {
|
||||
id: root
|
||||
|
|
@ -19,8 +19,6 @@ import org.kde.plasma.components 3.0 as PlasmaComponents3
|
|||
|
||||
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||
|
||||
import "notifications"
|
||||
|
||||
/**
|
||||
* Embeddable notification list widget optimized for mobile and touch.
|
||||
* Used on the lockscreen and action drawer.
|
||||
|
|
@ -32,9 +32,10 @@ FocusScope {
|
|||
}
|
||||
|
||||
function triggerHomeScreen() {
|
||||
taskSwitcher.minimizeAll();
|
||||
MobileShell.HomeScreenControls.resetHomeScreenPosition();
|
||||
taskSwitcher.visible = false; // will trigger homescreen open
|
||||
searchWidget.close();
|
||||
taskSwitcher.minimizeAll();
|
||||
}
|
||||
|
||||
//END functions
|
||||
|
|
@ -126,18 +127,45 @@ FocusScope {
|
|||
}
|
||||
}
|
||||
|
||||
// control the opacity of both the search and homescreen components
|
||||
property real homeScreenOpacity: 1
|
||||
NumberAnimation on homeScreenOpacity {
|
||||
id: opacityAnimation
|
||||
duration: PlasmaCore.Units.longDuration
|
||||
}
|
||||
|
||||
// homescreen component
|
||||
HomeScreenComponents.HomeScreen {
|
||||
id: homescreen
|
||||
anchors.fill: parent
|
||||
opacity: root.homeScreenOpacity * (1 - searchWidget.openFactor)
|
||||
|
||||
// make the homescreen not interactable when task switcher or startup feedback is on
|
||||
interactive: !taskSwitcher.visible && !startupFeedback.visible
|
||||
|
||||
opacity: 1
|
||||
NumberAnimation on opacity {
|
||||
id: opacityAnimation
|
||||
duration: PlasmaCore.Units.longDuration
|
||||
}
|
||||
|
||||
// search component
|
||||
MobileShell.KRunnerWidget {
|
||||
id: searchWidget
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: root.homeScreenOpacity
|
||||
visible: openFactor > 0
|
||||
onOpenFactorChanged: homescreen.opacity = 1 - openFactor;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: homescreen.homeScreenState
|
||||
|
||||
function onSwipeDownGestureBegin() {
|
||||
searchWidget.startGesture();
|
||||
}
|
||||
function onSwipeDownGestureEnd() {
|
||||
searchWidget.endGesture();
|
||||
}
|
||||
function onSwipeDownGestureOffset(offset) {
|
||||
searchWidget.updateGestureOffset(-offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +205,7 @@ FocusScope {
|
|||
opacityAnimation.to = 0;
|
||||
opacityAnimation.restart();
|
||||
} else {
|
||||
homescreen.opacity = 0;
|
||||
root.homeScreenOpacity = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue