mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
folio: Refactor and implement support for proper drag & drop
This refactors the homescreen state object to isolate drag & drop from swipe states, allowing for using proper system-level drag & drop for delegate movement. This then ports the new applet list to use it.
This commit is contained in:
parent
1250180c45
commit
4c76f55b5c
16 changed files with 901 additions and 787 deletions
|
|
@ -9,6 +9,7 @@ plasma_add_applet(org.kde.plasma.mobile.homescreen.folio
|
|||
qml/AppDrawerGrid.qml
|
||||
qml/AppDrawerHeader.qml
|
||||
qml/DelegateDragItem.qml
|
||||
qml/DelegateDropArea.qml
|
||||
qml/FavouritesBar.qml
|
||||
qml/FolderView.qml
|
||||
qml/FolderViewTitle.qml
|
||||
|
|
@ -65,6 +66,7 @@ ecm_target_qml_sources(org.kde.plasma.mobile.homescreen.folio SOURCES
|
|||
)
|
||||
|
||||
ecm_target_qml_sources(org.kde.plasma.mobile.homescreen.folio SOURCES
|
||||
qml/settings/AppletListDelegate.qml
|
||||
qml/settings/AppletListViewer.qml
|
||||
qml/settings/SettingsButton.qml
|
||||
qml/settings/SettingsComponent.qml
|
||||
|
|
|
|||
|
|
@ -175,12 +175,9 @@ DragState::DragState(HomeScreenState *state, HomeScreen *parent)
|
|||
connect(m_state, &HomeScreenState::delegateDragFromFavouritesStarted, this, &DragState::onDelegateDragFromFavouritesStarted);
|
||||
connect(m_state, &HomeScreenState::delegateDragFromFolderStarted, this, &DragState::onDelegateDragFromFolderStarted);
|
||||
connect(m_state, &HomeScreenState::delegateDragFromWidgetListStarted, this, &DragState::onDelegateDragFromWidgetListStarted);
|
||||
connect(m_state, &HomeScreenState::swipeStateChanged, this, [this]() {
|
||||
if (m_state->swipeState() == HomeScreenState::DraggingDelegate) {
|
||||
onDelegateDraggingStarted();
|
||||
}
|
||||
});
|
||||
connect(m_state, &HomeScreenState::delegateDragEnded, this, &DragState::onDelegateDropped);
|
||||
connect(m_state, &HomeScreenState::delegateDragStarted, this, &DragState::onDelegateDraggingStarted);
|
||||
connect(m_state, &HomeScreenState::delegateDragDropped, this, &DragState::onDelegateDropped);
|
||||
connect(m_state, &HomeScreenState::delegateDragCancelled, this, &DragState::onDelegateDraggingCancelled);
|
||||
|
||||
connect(m_state, &HomeScreenState::pageNumChanged, this, [this]() {
|
||||
m_candidateDropPosition->setPageRow(m_state->currentPage());
|
||||
|
|
@ -546,19 +543,8 @@ void DragState::onDelegateDropped()
|
|||
// add dropped delegate
|
||||
bool success = createDropPositionDelegate();
|
||||
|
||||
// delete empty pages at the end if they exist
|
||||
// (it can be created if user drags app to new page, but doesn't place it there)
|
||||
m_homeScreen->pageListModel()->deleteEmptyPagesAtEnd();
|
||||
|
||||
// clear ghost position if there is one
|
||||
m_homeScreen->favouritesModel()->deleteGhostEntry();
|
||||
|
||||
// reset timers
|
||||
m_folderInsertBetweenTimer->stop();
|
||||
m_changeFolderPageTimer->stop();
|
||||
m_leaveFolderTimer->stop();
|
||||
m_changePageTimer->stop();
|
||||
m_favouritesInsertBetweenTimer->stop();
|
||||
// Cleanup timers and state
|
||||
dragStopCleanup();
|
||||
|
||||
// emit corresponding signal
|
||||
// -> if we couldn't drop a new delegate at a spot, emit newDelegateDropAbandoned()
|
||||
|
|
@ -570,6 +556,41 @@ void DragState::onDelegateDropped()
|
|||
}
|
||||
}
|
||||
|
||||
void DragState::onDelegateDraggingCancelled()
|
||||
{
|
||||
if (!m_dropDelegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup timers and state
|
||||
dragStopCleanup();
|
||||
|
||||
if (m_startPosition->location() == DelegateDragPosition::WidgetList || m_startPosition->location() == DelegateDragPosition::AppDrawer) {
|
||||
// If this is a new delegate, it's abandoned
|
||||
Q_EMIT newDelegateDropAbandoned();
|
||||
} else {
|
||||
// If it's an existing delegate, it simply goes back to its original position
|
||||
Q_EMIT delegateDroppedAndPlaced();
|
||||
}
|
||||
}
|
||||
|
||||
void DragState::dragStopCleanup()
|
||||
{
|
||||
// Delete empty pages at the end if they exist
|
||||
// (it can be created if user drags app to new page, but doesn't place it there)
|
||||
m_homeScreen->pageListModel()->deleteEmptyPagesAtEnd();
|
||||
|
||||
// Clear ghost position if there is one
|
||||
m_homeScreen->favouritesModel()->deleteGhostEntry();
|
||||
|
||||
// Reset timers
|
||||
m_folderInsertBetweenTimer->stop();
|
||||
m_changeFolderPageTimer->stop();
|
||||
m_leaveFolderTimer->stop();
|
||||
m_changePageTimer->stop();
|
||||
m_favouritesInsertBetweenTimer->stop();
|
||||
}
|
||||
|
||||
void DragState::onLeaveCurrentFolder()
|
||||
{
|
||||
if (!m_state) {
|
||||
|
|
@ -589,7 +610,7 @@ void DragState::onLeaveCurrentFolder()
|
|||
|
||||
void DragState::onChangePageTimerFinished()
|
||||
{
|
||||
if (!m_state || (m_state->swipeState() != HomeScreenState::DraggingDelegate)) {
|
||||
if (!m_state || !m_state->isDraggingDelegate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -624,7 +645,7 @@ void DragState::onChangePageTimerFinished()
|
|||
|
||||
void DragState::onOpenFolderTimerFinished()
|
||||
{
|
||||
if (!m_state || m_state->swipeState() != HomeScreenState::DraggingDelegate || m_state->viewState() != HomeScreenState::PageView
|
||||
if (!m_state || !m_state->isDraggingDelegate() || m_state->viewState() != HomeScreenState::PageView
|
||||
|| (m_candidateDropPosition->location() != DelegateDragPosition::Pages && m_candidateDropPosition->location() != DelegateDragPosition::Favourites)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -672,7 +693,7 @@ void DragState::onOpenFolderTimerFinished()
|
|||
|
||||
void DragState::onLeaveFolderTimerFinished()
|
||||
{
|
||||
if (!m_state || (m_state->swipeState() != HomeScreenState::DraggingDelegate) || !m_state->currentFolder()) {
|
||||
if (!m_state || !m_state->isDraggingDelegate() || !m_state->currentFolder()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -684,7 +705,7 @@ void DragState::onLeaveFolderTimerFinished()
|
|||
|
||||
void DragState::onChangeFolderPageTimerFinished()
|
||||
{
|
||||
if (!m_state || (m_state->swipeState() != HomeScreenState::DraggingDelegate) || !m_state->currentFolder()) {
|
||||
if (!m_state || !m_state->isDraggingDelegate() || !m_state->currentFolder()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -720,7 +741,7 @@ void DragState::onChangeFolderPageTimerFinished()
|
|||
|
||||
void DragState::onFolderInsertBetweenTimerFinished()
|
||||
{
|
||||
if (!m_state || (m_state->swipeState() != HomeScreenState::DraggingDelegate) || !m_state->currentFolder()) {
|
||||
if (!m_state || !m_state->isDraggingDelegate() || !m_state->currentFolder()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ private Q_SLOTS:
|
|||
void onDelegateDragFromFolderStarted(FolioApplicationFolder *folder, int position);
|
||||
void onDelegateDragFromWidgetListStarted(QString appletPluginId);
|
||||
void onDelegateDropped();
|
||||
void onDelegateDraggingCancelled();
|
||||
|
||||
void onLeaveCurrentFolder();
|
||||
|
||||
|
|
@ -129,6 +130,9 @@ private Q_SLOTS:
|
|||
void onFavouritesInsertBetweenTimerFinished();
|
||||
|
||||
private:
|
||||
// Cleanup after a drag event (stop timers etc.)
|
||||
void dragStopCleanup();
|
||||
|
||||
// deletes the delegate at m_startPosition
|
||||
void deleteStartPositionDelegate();
|
||||
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ void HomeScreenState::setSwipeState(SwipeState swipeState)
|
|||
if (swipeState != m_swipeState) {
|
||||
m_swipeState = swipeState;
|
||||
Q_EMIT swipeStateChanged();
|
||||
Q_EMIT isDraggingDelegateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -692,6 +693,11 @@ int HomeScreenState::currentFolderPage()
|
|||
return m_folderPageNum;
|
||||
}
|
||||
|
||||
bool HomeScreenState::isDraggingDelegate()
|
||||
{
|
||||
return m_dragDropActive || m_swipeState == SwipeState::DraggingDelegate;
|
||||
}
|
||||
|
||||
FolioDelegate *HomeScreenState::getPageDelegateAt(int page, int row, int column)
|
||||
{
|
||||
PageModel *pageModel = m_homeScreen->pageListModel()->getPage(page);
|
||||
|
|
@ -905,6 +911,7 @@ void HomeScreenState::startDelegateAppDrawerDrag(qreal startX, qreal startY, qre
|
|||
// we start dragging the delegate immediately from the app drawer, because we don't have a context menu to deal with!
|
||||
// NOTE: this has to happen after delegateDragFromAppDrawerStarted, because slots for that expect SwipeState::AwaitingDraggingDelegate
|
||||
setSwipeState(SwipeState::DraggingDelegate);
|
||||
Q_EMIT delegateDragStarted();
|
||||
}
|
||||
|
||||
void HomeScreenState::startDelegateFolderDrag(qreal startX,
|
||||
|
|
@ -922,10 +929,6 @@ void HomeScreenState::startDelegateWidgetListDrag(qreal startX, qreal startY, qr
|
|||
{
|
||||
startDelegateDrag(startX, startY, pointerOffsetX, pointerOffsetY);
|
||||
Q_EMIT delegateDragFromWidgetListStarted(appletPluginId);
|
||||
|
||||
// we start dragging the delegate immediately from the app drawer, because we don't have a context menu to deal with!
|
||||
// NOTE: this has to happen after delegateDragFromAppDrawerStarted, because slots for that expect SwipeState::AwaitingDraggingDelegate
|
||||
setSwipeState(SwipeState::DraggingDelegate);
|
||||
}
|
||||
|
||||
void HomeScreenState::cancelDelegateDrag()
|
||||
|
|
@ -991,7 +994,7 @@ void HomeScreenState::swipeEnded()
|
|||
break;
|
||||
}
|
||||
case SwipeState::DraggingDelegate:
|
||||
Q_EMIT delegateDragEnded();
|
||||
Q_EMIT delegateDragDropped();
|
||||
break;
|
||||
case SwipeState::AwaitingDraggingDelegate:
|
||||
case SwipeState::DeterminingSwipeType:
|
||||
|
|
@ -1003,6 +1006,45 @@ void HomeScreenState::swipeEnded()
|
|||
setSwipeState(SwipeState::None);
|
||||
}
|
||||
|
||||
void HomeScreenState::dragStart()
|
||||
{
|
||||
// Cancel AwaitingDraggingDelegate
|
||||
swipeEnded();
|
||||
|
||||
m_dragDropActive = true;
|
||||
Q_EMIT delegateDragStarted();
|
||||
Q_EMIT isDraggingDelegateChanged();
|
||||
}
|
||||
|
||||
void HomeScreenState::dragMove(qreal deltaX, qreal deltaY)
|
||||
{
|
||||
if (!m_dragDropActive) {
|
||||
return;
|
||||
}
|
||||
setDelegateDragX(m_delegateDragX + deltaX);
|
||||
setDelegateDragY(m_delegateDragY + deltaY);
|
||||
}
|
||||
|
||||
void HomeScreenState::dragDrop()
|
||||
{
|
||||
if (!m_dragDropActive) {
|
||||
return;
|
||||
}
|
||||
m_dragDropActive = false;
|
||||
Q_EMIT delegateDragDropped();
|
||||
Q_EMIT isDraggingDelegateChanged();
|
||||
}
|
||||
|
||||
void HomeScreenState::dragCancel()
|
||||
{
|
||||
if (!m_dragDropActive) {
|
||||
return;
|
||||
}
|
||||
m_dragDropActive = false;
|
||||
Q_EMIT delegateDragCancelled();
|
||||
Q_EMIT isDraggingDelegateChanged();
|
||||
}
|
||||
|
||||
void HomeScreenState::swipeCancelled()
|
||||
{
|
||||
setSwipeState(SwipeState::None);
|
||||
|
|
@ -1038,6 +1080,7 @@ void HomeScreenState::swipeMoved(qreal totalDeltaX, qreal totalDeltaY, qreal del
|
|||
break;
|
||||
case SwipeState::AwaitingDraggingDelegate:
|
||||
setSwipeState(SwipeState::DraggingDelegate);
|
||||
Q_EMIT delegateDragStarted();
|
||||
break;
|
||||
case SwipeState::DraggingDelegate:
|
||||
setDelegateDragX(m_delegateDragX + deltaX);
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ class HomeScreenState : public QObject
|
|||
Q_PROPERTY(int currentPage READ currentPage NOTIFY pageNumChanged)
|
||||
Q_PROPERTY(int currentFolderPage READ currentFolderPage NOTIFY folderPageNumChanged)
|
||||
|
||||
Q_PROPERTY(bool isDraggingDelegate READ isDraggingDelegate NOTIFY isDraggingDelegateChanged)
|
||||
|
||||
public:
|
||||
enum SwipeState {
|
||||
None,
|
||||
|
|
@ -258,6 +260,9 @@ public:
|
|||
|
||||
int currentFolderPage();
|
||||
|
||||
// Whether something is being dragged (either from SwipeArea or Drag & Drop)
|
||||
bool isDraggingDelegate();
|
||||
|
||||
// QML helpers
|
||||
Q_INVOKABLE FolioDelegate *getPageDelegateAt(int page, int row, int column);
|
||||
Q_INVOKABLE FolioDelegate *getFavouritesDelegateAt(int position);
|
||||
|
|
@ -305,7 +310,9 @@ Q_SIGNALS:
|
|||
void searchWidgetYChanged();
|
||||
void delegateDragXChanged();
|
||||
void delegateDragYChanged();
|
||||
void delegateDragEnded();
|
||||
void delegateDragStarted();
|
||||
void delegateDragDropped();
|
||||
void delegateDragCancelled();
|
||||
void delegateDragFromPageStarted(int page, int row, int column);
|
||||
void delegateDragFromFavouritesStarted(int position);
|
||||
void delegateDragFromAppDrawerStarted(QString storageId);
|
||||
|
|
@ -313,6 +320,7 @@ Q_SIGNALS:
|
|||
void delegateDragFromWidgetListStarted(QString appletPluginId);
|
||||
void pageNumChanged();
|
||||
void folderPageNumChanged();
|
||||
void isDraggingDelegateChanged();
|
||||
|
||||
void leftCurrentFolder();
|
||||
void folderAboutToOpen(qreal x, qreal y); // the position on the screen where the delegate is at, for animations
|
||||
|
|
@ -347,6 +355,12 @@ public Q_SLOTS:
|
|||
void swipeCancelled();
|
||||
void swipeMoved(qreal totalDeltaX, qreal totalDeltaY, qreal deltaX, qreal deltaY);
|
||||
|
||||
// from DropArea
|
||||
void dragStart();
|
||||
void dragMove(qreal deltaX, qreal deltaY);
|
||||
void dragDrop();
|
||||
void dragCancel();
|
||||
|
||||
private:
|
||||
void setViewState(ViewState viewState);
|
||||
void setSwipeState(SwipeState swipeState);
|
||||
|
|
@ -410,6 +424,7 @@ private:
|
|||
qreal m_delegateDragY{0};
|
||||
qreal m_delegateDragPointerOffsetX{0};
|
||||
qreal m_delegateDragPointerOffsetY{0};
|
||||
bool m_dragDropActive{false};
|
||||
|
||||
int m_pageNum{0};
|
||||
int m_folderPageNum{0};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import org.kde.plasma.private.mobileshell as MobileShell
|
|||
|
||||
import "./delegate"
|
||||
|
||||
// Placeholder item that the user sees as they drag app/folder delegates around.
|
||||
// See WidgetDragItem for the equivalent for widgets.
|
||||
Item {
|
||||
id: root
|
||||
property Folio.HomeScreen folio
|
||||
|
|
@ -75,15 +77,16 @@ Item {
|
|||
property var delegateDroppedOn: null
|
||||
|
||||
// reset and show drag item
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate && !isWidgetDrag) {
|
||||
root.scale = 1.0;
|
||||
root.visible = true;
|
||||
function onDelegateDragStarted() {
|
||||
if (isWidgetDrag) {
|
||||
return;
|
||||
}
|
||||
root.scale = 1.0;
|
||||
root.visible = true;
|
||||
}
|
||||
|
||||
// save the existing delegate at the spot (this is called before the delegate is dropped)
|
||||
function onDelegateDragEnded() {
|
||||
function onDelegateDragDropped() {
|
||||
if (root.isWidgetDrag) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -103,6 +106,7 @@ Item {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
|
@ -142,7 +146,7 @@ Item {
|
|||
|
||||
// scale animation if we are creating, or inserting into a folder
|
||||
scaleAnim.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the drop has been abandoned, just hide
|
||||
|
|
|
|||
57
containments/homescreens/folio/qml/DelegateDropArea.qml
Normal file
57
containments/homescreens/folio/qml/DelegateDropArea.qml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
||||
|
||||
DropArea {
|
||||
id: root
|
||||
property Folio.HomeScreen folio
|
||||
property Folio.HomeScreenState homeScreenState: folio.HomeScreenState
|
||||
|
||||
keys: ["text/x-plasmoidservicename"]
|
||||
|
||||
property real prevX
|
||||
property real prevY
|
||||
|
||||
onEntered: (drag) => {
|
||||
drag.accept();
|
||||
const widthOffset = folio.HomeScreenState.pageCellWidth / 2;
|
||||
const heightOffset = folio.HomeScreenState.pageCellHeight / 2;
|
||||
|
||||
homeScreenState.startDelegateWidgetListDrag(
|
||||
drag.x - widthOffset,
|
||||
drag.y - heightOffset,
|
||||
widthOffset,
|
||||
heightOffset,
|
||||
drag.getDataAsString("text/x-plasmoidservicename")
|
||||
);
|
||||
|
||||
homeScreenState.dragStart();
|
||||
prevX = drag.x;
|
||||
prevY = drag.y;
|
||||
}
|
||||
onDropped: (drop) => {
|
||||
drop.accept();
|
||||
dropWaitTimer.restart();
|
||||
}
|
||||
onExited: {
|
||||
homeScreenState.dragCancel();
|
||||
}
|
||||
onPositionChanged: (drag) => {
|
||||
drag.accept();
|
||||
homeScreenState.dragMove(drag.x - prevX, drag.y - prevY);
|
||||
prevX = drag.x;
|
||||
prevY = drag.y;
|
||||
}
|
||||
|
||||
// HACK: Seems to crash otherwise, Qt bug?
|
||||
Timer {
|
||||
id: dropWaitTimer
|
||||
interval: 10
|
||||
onTriggered: {
|
||||
homeScreenState.dragDrop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ MouseArea {
|
|||
readonly property var dragState: folio.HomeScreenState.dragState
|
||||
readonly property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Favourites &&
|
||||
dragState.candidateDropPosition.favouritesPosition === delegate.index
|
||||
readonly property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
|
||||
readonly property bool isAppHoveredOver: folio.HomeScreenState.isDraggingDelegate &&
|
||||
dragState.dropDelegate &&
|
||||
dragState.dropDelegate.type === Folio.FolioDelegate.Application &&
|
||||
isDropPositionThis
|
||||
|
|
@ -178,7 +178,7 @@ MouseArea {
|
|||
shadow: true
|
||||
|
||||
turnToFolder: delegate.isAppHoveredOver
|
||||
turnToFolderAnimEnabled: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate
|
||||
turnToFolderAnimEnabled: folio.HomeScreenState.isDraggingDelegate
|
||||
|
||||
// do not show if the drop animation is running to this delegate
|
||||
visible: !(root.homeScreen.dropAnimationRunning && delegate.isDropPositionThis)
|
||||
|
|
@ -221,10 +221,8 @@ MouseArea {
|
|||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
||||
contextMenu.close();
|
||||
}
|
||||
function onDelegateDragStarted() {
|
||||
contextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,10 +296,8 @@ MouseArea {
|
|||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
||||
contextMenu.close();
|
||||
}
|
||||
function onDelegateDragStarted() {
|
||||
contextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ Folio.DelegateTouchArea {
|
|||
anchors.fill: parent
|
||||
|
||||
sourceComponent: {
|
||||
if (delegate.delegateModel.type === Folio.FolioDelegate.Application) {
|
||||
if (delegate.delegateModel && delegate.delegateModel.type === Folio.FolioDelegate.Application) {
|
||||
return appComponent;
|
||||
} else {
|
||||
return noneComponent;
|
||||
|
|
@ -391,10 +391,8 @@ Folio.DelegateTouchArea {
|
|||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
||||
contextMenu.close();
|
||||
}
|
||||
function onDelegateDragStarted() {
|
||||
contextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,497 +117,493 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
// area that can be swiped
|
||||
MobileShell.SwipeArea {
|
||||
id: swipeArea
|
||||
// Drag and drop area
|
||||
DelegateDropArea {
|
||||
id: dropArea
|
||||
folio: root.folio
|
||||
anchors.fill: parent
|
||||
|
||||
interactive: root.interactive &&
|
||||
settingsLoader.homeScreenInteractive &&
|
||||
(appDrawer.flickable.atYBeginning || // there are cases where contentY > 0 but atYBeginning is true
|
||||
appDrawer.flickable.contentY <= 10 ||
|
||||
// disable the swipe area when we are swiping in the app drawer, and not in drag-and-drop
|
||||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate ||
|
||||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate ||
|
||||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.SwipingAppDrawerGrid ||
|
||||
folio.HomeScreenState.viewState !== Folio.HomeScreenState.AppDrawerView)
|
||||
// Area that can be swiped
|
||||
MobileShell.SwipeArea {
|
||||
id: swipeArea
|
||||
anchors.fill: parent
|
||||
|
||||
onSwipeStarted: (currentPos, startPos) => {
|
||||
const deltaX = currentPos.x - startPos.x;
|
||||
const deltaY = currentPos.y - startPos.y;
|
||||
homeScreenState.swipeStarted(deltaX, deltaY);
|
||||
}
|
||||
onSwipeEnded: {
|
||||
homeScreenState.swipeEnded();
|
||||
}
|
||||
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => {
|
||||
// cancel swipe when settings component is opening to prevent conflicts
|
||||
if (folio.HomeScreenState.settingsOpenProgress && folio.HomeScreenState.viewState !== Folio.HomeScreenState.SettingsView) {
|
||||
homeScreenState.swipeCancelled();
|
||||
return;
|
||||
interactive: root.interactive &&
|
||||
settings.homeScreenInteractive &&
|
||||
!dropArea.containsDrag &&
|
||||
(appDrawer.flickable.atYBeginning || // there are cases where contentY > 0 but atYBeginning is true
|
||||
appDrawer.flickable.contentY <= 10 ||
|
||||
// disable the swipe area when we are swiping in the app drawer, and not in drag-and-drop
|
||||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate ||
|
||||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate ||
|
||||
folio.HomeScreenState.swipeState === Folio.HomeScreenState.SwipingAppDrawerGrid ||
|
||||
folio.HomeScreenState.viewState !== Folio.HomeScreenState.AppDrawerView)
|
||||
|
||||
onSwipeStarted: (currentPos, startPos) => {
|
||||
const deltaX = currentPos.x - startPos.x;
|
||||
const deltaY = currentPos.y - startPos.y;
|
||||
homeScreenState.swipeStarted(deltaX, deltaY);
|
||||
}
|
||||
homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY);
|
||||
}
|
||||
|
||||
onTouchpadScrollStarted: homeScreenState.swipeStarted(0, 0);
|
||||
onTouchpadScrollEnded: homeScreenState.swipeEnded();
|
||||
onTouchpadScrollMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY);
|
||||
|
||||
onPressedChanged: {
|
||||
if (pressed) {
|
||||
// ensures that components like the widget settings overlay close when swiping
|
||||
noFocus.forceActiveFocus();
|
||||
onSwipeEnded: {
|
||||
homeScreenState.swipeEnded();
|
||||
}
|
||||
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => {
|
||||
// cancel swipe when settings component is opening to prevent conflicts
|
||||
if (folio.HomeScreenState.settingsOpenProgress && folio.HomeScreenState.viewState !== Folio.HomeScreenState.SettingsView) {
|
||||
homeScreenState.swipeCancelled();
|
||||
return;
|
||||
}
|
||||
homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: settingsLoader
|
||||
asynchronous: true
|
||||
active: true
|
||||
onTouchpadScrollStarted: homeScreenState.swipeStarted(0, 0);
|
||||
onTouchpadScrollEnded: homeScreenState.swipeEnded();
|
||||
onTouchpadScrollMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => homeScreenState.swipeMoved(totalDeltaX, totalDeltaY, deltaX, deltaY);
|
||||
|
||||
// Don't anchor, since we set y
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
opacity: folio.HomeScreenState.settingsOpenProgress
|
||||
onPressedChanged: {
|
||||
if (pressed) {
|
||||
// ensures that components like the widget settings overlay close when swiping
|
||||
noFocus.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// move the settings out of the way if it is not visible
|
||||
// NOTE: we do this instead of setting visible to false, because
|
||||
// it doesn't mess with widget drag and drop
|
||||
y: (opacity > 0) ? 0 : parent.height
|
||||
z: 1
|
||||
|
||||
readonly property bool homeScreenInteractive: item ? item.homeScreenInteractive : true
|
||||
|
||||
sourceComponent: SettingsComponent {
|
||||
SettingsComponent {
|
||||
id: settings
|
||||
folio: root.folio
|
||||
homeScreen: root
|
||||
settingsModeHomeScreenScale: root.settingsModeHomeScreenScale
|
||||
|
||||
// Don't anchor, since we set y
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
opacity: folio.HomeScreenState.settingsOpenProgress
|
||||
visible: opacity > 0
|
||||
|
||||
z: 1
|
||||
|
||||
bottomMargin: root.bottomMargin
|
||||
leftMargin: root.leftMargin
|
||||
rightMargin: root.rightMargin
|
||||
|
||||
settingsModeHomeScreenScale: root.settingsModeHomeScreenScale
|
||||
homeScreen: root
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mainHomeScreen
|
||||
anchors.fill: parent
|
||||
Item {
|
||||
id: mainHomeScreen
|
||||
anchors.fill: parent
|
||||
|
||||
// we stop showing halfway through the animation
|
||||
opacity: 1 - Math.max(homeScreenState.appDrawerOpenProgress, homeScreenState.searchWidgetOpenProgress, homeScreenState.folderOpenProgress) * 2
|
||||
visible: opacity > 0 // prevent handlers from picking up events
|
||||
|
||||
transform: [
|
||||
Scale {
|
||||
property real scaleFactor: Math.max(homeScreenState.appDrawerOpenProgress, homeScreenState.searchWidgetOpenProgress)
|
||||
origin.x: mainHomeScreen.width / 2
|
||||
origin.y: mainHomeScreen.height / 2
|
||||
yScale: 1 - (scaleFactor * 2) * 0.1
|
||||
xScale: 1 - (scaleFactor * 2) * 0.1
|
||||
}
|
||||
]
|
||||
|
||||
HomeScreenPages {
|
||||
id: homeScreenPages
|
||||
folio: root.folio
|
||||
maskManager: root.maskManager
|
||||
homeScreen: root
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.leftMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left ? 0 : root.leftMargin
|
||||
anchors.rightMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right ? 0 : root.rightMargin
|
||||
anchors.bottomMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? 0 : root.bottomMargin
|
||||
|
||||
// update the model with page dimensions
|
||||
onWidthChanged: {
|
||||
homeScreenState.pageWidth = homeScreenPages.width;
|
||||
}
|
||||
onHeightChanged: {
|
||||
homeScreenState.pageHeight = homeScreenPages.height;
|
||||
}
|
||||
|
||||
// Keyboard navigation from pages
|
||||
Keys.onPressed: (event) => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
// Open search widget when going up
|
||||
folio.HomeScreenState.openSearchWidget();
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
// Focus on favorites bar or app drawer, depending on its physical location
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom) {
|
||||
favouritesBar.forceActiveFocus();
|
||||
} else {
|
||||
folio.HomeScreenState.openAppDrawer();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Left:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left) {
|
||||
// If favorites bar is on the left, navigate to it
|
||||
favouritesBar.forceActiveFocus();
|
||||
} else {
|
||||
// Otherwise go to page on the left
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage - 1, false);
|
||||
homeScreenPages.focusCurrentPageForKeyboardNav();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Right:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right) {
|
||||
// If favorites bar is on the right, navigate to it
|
||||
favouritesBar.forceActiveFocus();
|
||||
} else {
|
||||
// Otherwise go to page on the right
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage + 1, false);
|
||||
homeScreenPages.focusCurrentPageForKeyboardNav();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// we stop showing halfway through the animation
|
||||
opacity: 1 - Math.max(homeScreenState.appDrawerOpenProgress, homeScreenState.searchWidgetOpenProgress, homeScreenState.folderOpenProgress) * 2
|
||||
visible: opacity > 0 // prevent handlers from picking up events
|
||||
|
||||
transform: [
|
||||
Scale {
|
||||
// animation when settings opens
|
||||
property real scaleFactor: 1 - folio.HomeScreenState.settingsOpenProgress * (1 - settingsModeHomeScreenScale)
|
||||
origin.x: root.leftMargin + (root.width - root.rightMargin - root.leftMargin) / 2
|
||||
origin.y: root.height * settingsModeHomeScreenScale / 2
|
||||
xScale: scaleFactor
|
||||
yScale: scaleFactor
|
||||
property real scaleFactor: Math.max(homeScreenState.appDrawerOpenProgress, homeScreenState.searchWidgetOpenProgress)
|
||||
origin.x: mainHomeScreen.width / 2
|
||||
origin.y: mainHomeScreen.height / 2
|
||||
yScale: 1 - (scaleFactor * 2) * 0.1
|
||||
xScale: 1 - (scaleFactor * 2) * 0.1
|
||||
}
|
||||
]
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "bottom"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
|
||||
AnchorChanges {
|
||||
target: homeScreenPages
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: favouritesBar.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}, State {
|
||||
name: "left"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left
|
||||
AnchorChanges {
|
||||
target: homeScreenPages
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: favouritesBar.right
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}, State {
|
||||
name: "right"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right
|
||||
AnchorChanges {
|
||||
target: homeScreenPages
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: favouritesBar.left
|
||||
HomeScreenPages {
|
||||
id: homeScreenPages
|
||||
folio: root.folio
|
||||
maskManager: root.maskManager
|
||||
homeScreen: root
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.leftMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left ? 0 : root.leftMargin
|
||||
anchors.rightMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right ? 0 : root.rightMargin
|
||||
anchors.bottomMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? 0 : root.bottomMargin
|
||||
|
||||
// update the model with page dimensions
|
||||
onWidthChanged: {
|
||||
homeScreenState.pageWidth = homeScreenPages.width;
|
||||
}
|
||||
onHeightChanged: {
|
||||
homeScreenState.pageHeight = homeScreenPages.height;
|
||||
}
|
||||
|
||||
// Keyboard navigation from pages
|
||||
Keys.onPressed: (event) => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
// Open search widget when going up
|
||||
folio.HomeScreenState.openSearchWidget();
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
// Focus on favorites bar or app drawer, depending on its physical location
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom) {
|
||||
favouritesBar.forceActiveFocus();
|
||||
} else {
|
||||
folio.HomeScreenState.openAppDrawer();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Left:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left) {
|
||||
// If favorites bar is on the left, navigate to it
|
||||
favouritesBar.forceActiveFocus();
|
||||
} else {
|
||||
// Otherwise go to page on the left
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage - 1, false);
|
||||
homeScreenPages.focusCurrentPageForKeyboardNav();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Right:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right) {
|
||||
// If favorites bar is on the right, navigate to it
|
||||
favouritesBar.forceActiveFocus();
|
||||
} else {
|
||||
// Otherwise go to page on the right
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage + 1, false);
|
||||
homeScreenPages.focusCurrentPageForKeyboardNav();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transform: [
|
||||
Scale {
|
||||
// animation when settings opens
|
||||
property real scaleFactor: 1 - folio.HomeScreenState.settingsOpenProgress * (1 - settingsModeHomeScreenScale)
|
||||
origin.x: root.leftMargin + (root.width - root.rightMargin - root.leftMargin) / 2
|
||||
origin.y: root.height * settingsModeHomeScreenScale / 2
|
||||
xScale: scaleFactor
|
||||
yScale: scaleFactor
|
||||
}
|
||||
]
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "bottom"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
|
||||
AnchorChanges {
|
||||
target: homeScreenPages
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: favouritesBar.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}, State {
|
||||
name: "left"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left
|
||||
AnchorChanges {
|
||||
target: homeScreenPages
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: favouritesBar.right
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}, State {
|
||||
name: "right"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right
|
||||
AnchorChanges {
|
||||
target: homeScreenPages
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: favouritesBar.left
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: favouritesBarScrim
|
||||
color: Qt.rgba(255, 255, 255, 0.2)
|
||||
|
||||
Component.onCompleted: maskManager.assignToMask(this)
|
||||
|
||||
// don't show in settings mode
|
||||
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
|
||||
visible: folio.FolioSettings.showFavouritesBarBackground
|
||||
|
||||
anchors.top: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? favouritesBar.top : parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right ? favouritesBar.left : parent.left
|
||||
anchors.right: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left ? favouritesBar.right : parent.right
|
||||
|
||||
// because of the scale animation, we need to extend the panel out a bit
|
||||
anchors.topMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? 0 : -Kirigami.Units.gridUnit * 5
|
||||
anchors.bottomMargin: -Kirigami.Units.gridUnit * 5
|
||||
anchors.leftMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right ? 0 : -Kirigami.Units.gridUnit * 5
|
||||
anchors.rightMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left ? 0 : -Kirigami.Units.gridUnit * 5
|
||||
}
|
||||
|
||||
FavouritesBar {
|
||||
id: favouritesBar
|
||||
folio: root.folio
|
||||
maskManager: root.maskManager
|
||||
homeScreen: root
|
||||
|
||||
// don't show in settings mode
|
||||
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
|
||||
visible: opacity > 0
|
||||
|
||||
// one is ignored as anchors are set
|
||||
height: Kirigami.Units.gridUnit * 6
|
||||
width: Kirigami.Units.gridUnit * 6
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
|
||||
// Keyboard navigation on favorites bar
|
||||
Keys.onPressed: (event) => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
// Focus on homescreen pages or search widget depending on physical position
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom) {
|
||||
homeScreenPages.forceActiveFocus();
|
||||
} else {
|
||||
folio.HomeScreenState.openSearchWidget();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
// Open app drawer
|
||||
folio.HomeScreenState.openAppDrawer();
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Left:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left) {
|
||||
// Go to left page if mounted on the left
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage - 1, false);
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Right:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right) {
|
||||
// Go to right page if mounted on the right
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage + 1, false);
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "bottom"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
|
||||
AnchorChanges {
|
||||
target: favouritesBar
|
||||
anchors.top: undefined
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
PropertyChanges {
|
||||
target: favouritesBar
|
||||
height: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
}, State {
|
||||
name: "left"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left
|
||||
AnchorChanges {
|
||||
target: favouritesBar
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: undefined
|
||||
}
|
||||
PropertyChanges {
|
||||
target: favouritesBar
|
||||
width: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
}, State {
|
||||
name: "right"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right
|
||||
AnchorChanges {
|
||||
target: favouritesBar
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: undefined
|
||||
anchors.right: parent.right
|
||||
}
|
||||
PropertyChanges {
|
||||
target: favouritesBar
|
||||
width: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Item {
|
||||
id: pageIndicatorWrapper
|
||||
property bool favouritesBarAtBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
|
||||
|
||||
// don't show in settings mode
|
||||
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: favouritesBarAtBottom ? favouritesBar.top : parent.bottom
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
anchors.bottomMargin: favouritesBarAtBottom ? 0 : (root.bottomMargin + Kirigami.Units.largeSpacing)
|
||||
|
||||
// show page indicator if there are multiple pages
|
||||
QQC2.PageIndicator {
|
||||
visible: count > 1
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
currentIndex: folio.HomeScreenState.currentPage
|
||||
count: folio.PageListModel.length
|
||||
|
||||
TapHandler {
|
||||
onTapped: folio.HomeScreenState.openAppDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
// show arrow to open app drawer when there is 1 page
|
||||
Kirigami.Icon {
|
||||
source: 'arrow-up'
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
implicitHeight: Kirigami.Units.iconSizes.small
|
||||
implicitWidth: Kirigami.Units.iconSizes.small
|
||||
|
||||
visible: folio.PageListModel.length <= 1
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
TapHandler {
|
||||
onTapped: folio.HomeScreenState.openAppDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: favouritesBarScrim
|
||||
color: Qt.rgba(255, 255, 255, 0.2)
|
||||
|
||||
Component.onCompleted: maskManager.assignToMask(this)
|
||||
|
||||
// don't show in settings mode
|
||||
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
|
||||
visible: folio.FolioSettings.showFavouritesBarBackground
|
||||
|
||||
anchors.top: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? favouritesBar.top : parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right ? favouritesBar.left : parent.left
|
||||
anchors.right: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left ? favouritesBar.right : parent.right
|
||||
|
||||
// because of the scale animation, we need to extend the panel out a bit
|
||||
anchors.topMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom ? 0 : -Kirigami.Units.gridUnit * 5
|
||||
anchors.bottomMargin: -Kirigami.Units.gridUnit * 5
|
||||
anchors.leftMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right ? 0 : -Kirigami.Units.gridUnit * 5
|
||||
anchors.rightMargin: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left ? 0 : -Kirigami.Units.gridUnit * 5
|
||||
}
|
||||
|
||||
FavouritesBar {
|
||||
id: favouritesBar
|
||||
// folder view
|
||||
FolderView {
|
||||
id: folderView
|
||||
folio: root.folio
|
||||
maskManager: root.maskManager
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
|
||||
homeScreen: root
|
||||
opacity: homeScreenState.folderOpenProgress
|
||||
transform: Translate { y: folderView.opacity > 0 ? 0 : folderView.height }
|
||||
}
|
||||
|
||||
// bottom app drawer
|
||||
AppDrawer {
|
||||
id: appDrawer
|
||||
folio: root.folio
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
homeScreen: root
|
||||
|
||||
// don't show in settings mode
|
||||
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
|
||||
visible: opacity > 0
|
||||
// we only start showing it halfway through
|
||||
opacity: homeScreenState.appDrawerOpenProgress < 0.5 ? 0 : (homeScreenState.appDrawerOpenProgress - 0.5) * 2
|
||||
|
||||
// one is ignored as anchors are set
|
||||
height: Kirigami.Units.gridUnit * 6
|
||||
width: Kirigami.Units.gridUnit * 6
|
||||
// position for animation
|
||||
property real animationY: (1 - homeScreenState.appDrawerOpenProgress) * (Kirigami.Units.gridUnit * 2)
|
||||
|
||||
// move the app drawer out of the way if it is not visible
|
||||
// NOTE: we do this instead of setting visible to false, because
|
||||
// it doesn't mess with app drag and drop from the app drawer
|
||||
y: (opacity > 0) ? animationY : parent.height
|
||||
|
||||
headerHeight: Math.round(Kirigami.Units.gridUnit * 4)
|
||||
headerItem: AppDrawerHeader {
|
||||
id: appDrawerHeader
|
||||
folio: root.folio
|
||||
|
||||
onReleaseFocusRequested: appDrawer.forceActiveFocus()
|
||||
}
|
||||
|
||||
// Account for panels
|
||||
topPadding: root.topMargin
|
||||
bottomPadding: root.bottomMargin
|
||||
leftPadding: root.leftMargin
|
||||
rightPadding: root.rightMargin
|
||||
|
||||
// Forward keyboard text to the search bar
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.text.trim().length > 0) {
|
||||
appDrawerHeader.addSearchText(event.text);
|
||||
appDrawerHeader.forceActiveFocus();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Up || event.key === Qt.Key_Down) {
|
||||
appDrawerHeader.forceActiveFocus();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onAppDrawerOpened() {
|
||||
appDrawer.forceActiveFocus();
|
||||
}
|
||||
|
||||
function onAppDrawerClosed() {
|
||||
// reset app drawer position when closed
|
||||
appDrawer.flickable.contentY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search component
|
||||
MobileShell.KRunnerScreen {
|
||||
id: searchWidget
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: homeScreenState.searchWidgetOpenProgress
|
||||
visible: opacity > 0
|
||||
transform: Translate { y: (1 - homeScreenState.searchWidgetOpenProgress) * (-Kirigami.Units.gridUnit * 2) }
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
// clear search bar when closed
|
||||
searchWidget.clearField();
|
||||
}
|
||||
}
|
||||
|
||||
// focus the search bar if it opens
|
||||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSearchWidgetOpenProgressChanged() {
|
||||
if (homeScreenState.searchWidgetOpenProgress === 1.0) {
|
||||
searchWidget.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRequestedClose: (triggeredByKeyEvent) => {
|
||||
homeScreenState.closeSearchWidget();
|
||||
}
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
|
||||
// Keyboard navigation on favorites bar
|
||||
Keys.onPressed: (event) => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
// Focus on homescreen pages or search widget depending on physical position
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom) {
|
||||
homeScreenPages.forceActiveFocus();
|
||||
} else {
|
||||
folio.HomeScreenState.openSearchWidget();
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
// Open app drawer
|
||||
folio.HomeScreenState.openAppDrawer();
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Left:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left) {
|
||||
// Go to left page if mounted on the left
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage - 1, false);
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Right:
|
||||
if (folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right) {
|
||||
// Go to right page if mounted on the right
|
||||
folio.HomeScreenState.goToPage(folio.HomeScreenState.currentPage + 1, false);
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "bottom"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
|
||||
AnchorChanges {
|
||||
target: favouritesBar
|
||||
anchors.top: undefined
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
PropertyChanges {
|
||||
target: favouritesBar
|
||||
height: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
}, State {
|
||||
name: "left"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Left
|
||||
AnchorChanges {
|
||||
target: favouritesBar
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: undefined
|
||||
}
|
||||
PropertyChanges {
|
||||
target: favouritesBar
|
||||
width: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
}, State {
|
||||
name: "right"
|
||||
when: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Right
|
||||
AnchorChanges {
|
||||
target: favouritesBar
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: undefined
|
||||
anchors.right: parent.right
|
||||
}
|
||||
PropertyChanges {
|
||||
target: favouritesBar
|
||||
width: Kirigami.Units.gridUnit * 6
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Item {
|
||||
id: pageIndicatorWrapper
|
||||
property bool favouritesBarAtBottom: folio.HomeScreenState.favouritesBarLocation === Folio.HomeScreenState.Bottom
|
||||
|
||||
// don't show in settings mode
|
||||
opacity: 1 - folio.HomeScreenState.settingsOpenProgress
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: favouritesBarAtBottom ? favouritesBar.top : parent.bottom
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
anchors.bottomMargin: favouritesBarAtBottom ? 0 : (root.bottomMargin + Kirigami.Units.largeSpacing)
|
||||
|
||||
// show page indicator if there are multiple pages
|
||||
QQC2.PageIndicator {
|
||||
visible: count > 1
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
currentIndex: folio.HomeScreenState.currentPage
|
||||
count: folio.PageListModel.length
|
||||
|
||||
TapHandler {
|
||||
onTapped: folio.HomeScreenState.openAppDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
// show arrow to open app drawer when there is 1 page
|
||||
Kirigami.Icon {
|
||||
source: 'arrow-up'
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
implicitHeight: Kirigami.Units.iconSizes.small
|
||||
implicitWidth: Kirigami.Units.iconSizes.small
|
||||
|
||||
visible: folio.PageListModel.length <= 1
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
TapHandler {
|
||||
onTapped: folio.HomeScreenState.openAppDrawer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// folder view
|
||||
FolderView {
|
||||
id: folderView
|
||||
folio: root.folio
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
|
||||
homeScreen: root
|
||||
opacity: homeScreenState.folderOpenProgress
|
||||
transform: Translate { y: folderView.opacity > 0 ? 0 : folderView.height }
|
||||
}
|
||||
|
||||
// bottom app drawer
|
||||
AppDrawer {
|
||||
id: appDrawer
|
||||
folio: root.folio
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
homeScreen: root
|
||||
|
||||
// we only start showing it halfway through
|
||||
opacity: homeScreenState.appDrawerOpenProgress < 0.5 ? 0 : (homeScreenState.appDrawerOpenProgress - 0.5) * 2
|
||||
|
||||
// position for animation
|
||||
property real animationY: (1 - homeScreenState.appDrawerOpenProgress) * (Kirigami.Units.gridUnit * 2)
|
||||
|
||||
// move the app drawer out of the way if it is not visible
|
||||
// NOTE: we do this instead of setting visible to false, because
|
||||
// it doesn't mess with app drag and drop from the app drawer
|
||||
y: (opacity > 0) ? animationY : parent.height
|
||||
|
||||
headerHeight: Math.round(Kirigami.Units.gridUnit * 4)
|
||||
headerItem: AppDrawerHeader {
|
||||
id: appDrawerHeader
|
||||
folio: root.folio
|
||||
|
||||
onReleaseFocusRequested: appDrawer.forceActiveFocus()
|
||||
}
|
||||
|
||||
// Account for panels
|
||||
topPadding: root.topMargin
|
||||
bottomPadding: root.bottomMargin
|
||||
leftPadding: root.leftMargin
|
||||
rightPadding: root.rightMargin
|
||||
|
||||
// Forward keyboard text to the search bar
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.text.trim().length > 0) {
|
||||
appDrawerHeader.addSearchText(event.text);
|
||||
appDrawerHeader.forceActiveFocus();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Up || event.key === Qt.Key_Down) {
|
||||
appDrawerHeader.forceActiveFocus();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onAppDrawerOpened() {
|
||||
appDrawer.forceActiveFocus();
|
||||
}
|
||||
|
||||
function onAppDrawerClosed() {
|
||||
// reset app drawer position when closed
|
||||
appDrawer.flickable.contentY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search component
|
||||
MobileShell.KRunnerScreen {
|
||||
id: searchWidget
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: homeScreenState.searchWidgetOpenProgress
|
||||
visible: opacity > 0
|
||||
transform: Translate { y: (1 - homeScreenState.searchWidgetOpenProgress) * (-Kirigami.Units.gridUnit * 2) }
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
// clear search bar when closed
|
||||
searchWidget.clearField();
|
||||
}
|
||||
}
|
||||
|
||||
// focus the search bar if it opens
|
||||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSearchWidgetOpenProgressChanged() {
|
||||
if (homeScreenState.searchWidgetOpenProgress === 1.0) {
|
||||
searchWidget.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onRequestedClose: (triggeredByKeyEvent) => {
|
||||
homeScreenState.closeSearchWidget();
|
||||
}
|
||||
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,11 +124,11 @@ Item {
|
|||
property bool dropDelegateIsWidget: dropDelegate && dropDelegate.type === Folio.FolioDelegate.Widget
|
||||
|
||||
// only show if it is an empty spot on this page
|
||||
visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
|
||||
dropPosition.location === Folio.DelegateDragPosition.Pages &&
|
||||
dropPosition.page === root.pageNum &&
|
||||
!dropDelegateIsWidget &&
|
||||
folio.HomeScreenState.getPageDelegateAt(root.pageNum, dropPosition.pageRow, dropPosition.pageColumn) === null
|
||||
visible: folio.HomeScreenState.isDraggingDelegate &&
|
||||
dropPosition.location === Folio.DelegateDragPosition.Pages &&
|
||||
dropPosition.page === root.pageNum &&
|
||||
!dropDelegateIsWidget &&
|
||||
folio.HomeScreenState.getPageDelegateAt(root.pageNum, dropPosition.pageRow, dropPosition.pageColumn) === null
|
||||
|
||||
x: dropPosition.pageColumn * folio.HomeScreenState.pageCellWidth
|
||||
y: dropPosition.pageRow * folio.HomeScreenState.pageCellHeight
|
||||
|
|
@ -145,7 +145,7 @@ Item {
|
|||
property bool dropDelegateIsWidget: dropDelegate && dropDelegate.type === Folio.FolioDelegate.Widget
|
||||
|
||||
// only show if the widget can be placed here
|
||||
visible: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
|
||||
visible: folio.HomeScreenState.isDraggingDelegate &&
|
||||
dropPosition.location === Folio.DelegateDragPosition.Pages &&
|
||||
dropPosition.page === root.pageNum &&
|
||||
dropDelegateIsWidget &&
|
||||
|
|
@ -180,7 +180,7 @@ Item {
|
|||
dragState.candidateDropPosition.pageRow === delegate.pageDelegate.row &&
|
||||
dragState.candidateDropPosition.pageColumn === delegate.pageDelegate.column
|
||||
|
||||
property bool isAppHoveredOver: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
|
||||
property bool isAppHoveredOver: folio.HomeScreenState.isDraggingDelegate &&
|
||||
dragState.dropDelegate &&
|
||||
dragState.dropDelegate.type === Folio.FolioDelegate.Application &&
|
||||
isDropPositionThis
|
||||
|
|
@ -274,7 +274,7 @@ Item {
|
|||
name: folio.FolioSettings.showPagesAppLabels ? delegate.pageDelegate.application.name : ""
|
||||
application: delegate.pageDelegate.application
|
||||
turnToFolder: delegate.isAppHoveredOver
|
||||
turnToFolderAnimEnabled: folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate
|
||||
turnToFolderAnimEnabled: folio.HomeScreenState.isDraggingDelegate
|
||||
|
||||
implicitWidth: folio.HomeScreenState.pageCellWidth
|
||||
implicitHeight: folio.HomeScreenState.pageCellHeight
|
||||
|
|
@ -323,10 +323,8 @@ Item {
|
|||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
||||
contextMenu.close();
|
||||
}
|
||||
function onDelegateDragStarted() {
|
||||
contextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -409,10 +407,8 @@ Item {
|
|||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
||||
contextMenu.close();
|
||||
}
|
||||
function onDelegateDragStarted() {
|
||||
contextMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
|||
import './delegate'
|
||||
import './private'
|
||||
|
||||
// Placeholder item that the user sees as they drag widgets around.
|
||||
// See DelegateDragItem for the equivalent for app delegates.
|
||||
Item {
|
||||
id: root
|
||||
property Folio.HomeScreen folio
|
||||
|
|
@ -25,7 +27,9 @@ Item {
|
|||
|
||||
property Folio.FolioWidget widget
|
||||
|
||||
readonly property bool isWidgetDelegate: folio.HomeScreenState.dragState.dropDelegate && folio.HomeScreenState.dragState.dropDelegate.type === Folio.FolioDelegate.Widget
|
||||
readonly property bool isWidgetDelegate: folio.HomeScreenState.dragState.dropDelegate
|
||||
&& folio.HomeScreenState.dragState.dropDelegate.type === Folio.FolioDelegate.Widget
|
||||
&& folio.HomeScreenState.dragState.dropDelegate.widget.visualApplet
|
||||
readonly property bool dropAnimationRunning: dragXAnim.running || dragYAnim.running
|
||||
|
||||
visible: false
|
||||
|
|
@ -74,13 +78,12 @@ Item {
|
|||
id: stateWatcher
|
||||
target: folio.HomeScreenState
|
||||
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate &&
|
||||
folio.HomeScreenState.dragState.dropDelegate &&
|
||||
folio.HomeScreenState.dragState.dropDelegate.type === Folio.FolioDelegate.Widget) {
|
||||
|
||||
root.startDrag(folio.HomeScreenState.dragState.dropDelegate.widget);
|
||||
function onDelegateDragStarted() {
|
||||
if (!root.isWidgetDelegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
root.startDrag(folio.HomeScreenState.dragState.dropDelegate.widget);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,11 +91,9 @@ Item {
|
|||
target: folio.HomeScreenState
|
||||
|
||||
// if we are starting drag-and-drop, close the menu immediately
|
||||
function onSwipeStateChanged() {
|
||||
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
||||
configOverlay.animClose();
|
||||
root.closed();
|
||||
}
|
||||
function onDelegateDragStarted() {
|
||||
configOverlay.animClose();
|
||||
root.closed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
// SPDX-FileCopyrightText: 2023-2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.plasma.private.shell 2.0
|
||||
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
||||
import org.kde.plasma.components 3.0 as PC3
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
|
||||
Item {
|
||||
id: delegate
|
||||
property Folio.HomeScreen folio
|
||||
|
||||
readonly property string pluginName: model.pluginName
|
||||
|
||||
property real zoomScale: (model.isSupported && mouseArea.pressed) ? 0.8 : 1
|
||||
transform: Scale {
|
||||
origin.x: delegate.width / 2;
|
||||
origin.y: delegate.height / 2;
|
||||
xScale: delegate.zoomScale
|
||||
yScale: delegate.zoomScale
|
||||
}
|
||||
|
||||
Behavior on zoomScale { NumberAnimation { duration: 80 } }
|
||||
|
||||
// Placeholder item used for implement drag & drop
|
||||
Item {
|
||||
id: draggable
|
||||
anchors.fill: parent
|
||||
|
||||
Drag.hotSpot.x: iconWidget.width / 2
|
||||
Drag.hotSpot.y: iconWidget.height / 2
|
||||
Drag.mimeData: { "text/x-plasmoidservicename": pluginName }
|
||||
Drag.dragType: Drag.Automatic
|
||||
Drag.onDragFinished: {
|
||||
root.requestClose();
|
||||
}
|
||||
}
|
||||
|
||||
MobileShell.HapticsEffect {
|
||||
id: haptics
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
cursorShape: model.isSupported ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
hoverEnabled: true
|
||||
|
||||
onPressAndHold: {
|
||||
if (!model.isSupported) {
|
||||
return
|
||||
}
|
||||
|
||||
haptics.buttonVibrate();
|
||||
iconWidget.grabToImage(function(result) {
|
||||
// Start drag & drop
|
||||
folio.HomeScreenState.closeSettingsView();
|
||||
draggable.Drag.imageSource = result.url;
|
||||
draggable.Drag.active = true;
|
||||
root.requestClose();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
color: Qt.rgba(255, 255, 255, 0.3)
|
||||
visible: model.isSupported && mouseArea.containsMouse
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Kirigami.Units.largeSpacing
|
||||
|
||||
Item {
|
||||
id: iconWidget
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: delegate.width
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.large
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.large
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.centerIn: parent
|
||||
source: model.decoration
|
||||
visible: model.screenshot == ""
|
||||
implicitWidth: Kirigami.Units.iconSizes.large
|
||||
implicitHeight: Kirigami.Units.iconSizes.large
|
||||
}
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.screenshot
|
||||
width: Kirigami.Units.iconSizes.large
|
||||
height: Kirigami.Units.iconSizes.large
|
||||
}
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
id: heading
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: delegate.width
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: model.name
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: delegate.width
|
||||
Layout.alignment: Qt.AlignTop
|
||||
// otherwise causes binding loop due to the way the Plasma sets the height
|
||||
height: implicitHeight
|
||||
text: model.isSupported ? model.description : model.unsupportedMessage
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: heading.lineCount === 1 ? 3 : 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,231 +19,123 @@ import org.kde.plasma.private.mobileshell as MobileShell
|
|||
import '../delegate'
|
||||
import '../private'
|
||||
|
||||
MouseArea {
|
||||
Loader {
|
||||
id: root
|
||||
property Folio.HomeScreen folio
|
||||
|
||||
property var homeScreen
|
||||
|
||||
signal requestClose()
|
||||
onClicked: root.requestClose()
|
||||
active: false
|
||||
|
||||
function requestClose() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
MobileShell.HapticsEffect {
|
||||
id: haptics
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(0, 0, 0, 0.7)
|
||||
}
|
||||
|
||||
|
||||
PlasmaExtras.ModelContextMenu {
|
||||
id: getWidgetsDialog
|
||||
visualParent: getWidgetsButton
|
||||
placement: PlasmaExtras.Menu.TopPosedLeftAlignedPopup
|
||||
// model set on first invocation
|
||||
onClicked: model.trigger()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: header
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Kirigami.Units.gridUnit
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Kirigami.Units.gridUnit * 3 + root.homeScreen.topMargin
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Kirigami.Units.gridUnit
|
||||
|
||||
PC3.ToolButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon.name: 'go-previous'
|
||||
implicitWidth: Kirigami.Units.gridUnit * 2
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
onClicked: root.requestClose()
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
text: i18n("Widgets")
|
||||
wrapMode: Text.Wrap
|
||||
font.weight: Font.Bold
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.5
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
PC3.ToolButton {
|
||||
id: getWidgetsButton
|
||||
icon.name: "get-hot-new-stuff"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.mobile", "@action:button The word 'new' refers to widgets", "Get New Widgets…")
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.mobile", "@action:button", "Get New Widgets…")
|
||||
|
||||
onClicked: {
|
||||
getWidgetsDialog.model = widgetExplorer.widgetsMenuActions
|
||||
getWidgetsDialog.openRelative()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: gridView
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
opacity: 0 // we display with the opacity gradient below
|
||||
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: Kirigami.Units.gridUnit
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.homeScreen.leftMargin
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.homeScreen.rightMargin
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.homeScreen.bottomMargin
|
||||
|
||||
model: widgetExplorer.widgetsModel
|
||||
|
||||
readonly property real maxCellWidth: Kirigami.Units.gridUnit * 20
|
||||
readonly property real intendedCellWidth: Kirigami.Units.gridUnit * 8
|
||||
readonly property int columns: Math.min(5, (width - leftMargin - rightMargin) / intendedCellWidth)
|
||||
|
||||
cellWidth: (width - leftMargin - rightMargin) / columns
|
||||
cellHeight: cellWidth + Kirigami.Units.gridUnit * 3
|
||||
|
||||
readonly property real horizontalMargin: Math.round(width * 0.05)
|
||||
leftMargin: horizontalMargin
|
||||
rightMargin: horizontalMargin
|
||||
|
||||
MouseArea {
|
||||
z: -1
|
||||
sourceComponent: Item {
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
onClicked: root.requestClose()
|
||||
color: Qt.rgba(0, 0, 0, 0.7)
|
||||
}
|
||||
|
||||
delegate: MouseArea {
|
||||
id: delegate
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
PlasmaExtras.ModelContextMenu {
|
||||
id: getWidgetsDialog
|
||||
visualParent: getWidgetsButton
|
||||
placement: PlasmaExtras.Menu.TopPosedLeftAlignedPopup
|
||||
// model set on first invocation
|
||||
onClicked: model.trigger()
|
||||
}
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
RowLayout {
|
||||
id: header
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Kirigami.Units.gridUnit
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Kirigami.Units.gridUnit * 3 + root.homeScreen.topMargin
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Kirigami.Units.gridUnit
|
||||
|
||||
property real zoomScale: pressed ? 0.8 : 1
|
||||
transform: Scale {
|
||||
origin.x: delegate.width / 2;
|
||||
origin.y: delegate.height / 2;
|
||||
xScale: delegate.zoomScale
|
||||
yScale: delegate.zoomScale
|
||||
PC3.ToolButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon.name: 'go-previous'
|
||||
implicitWidth: Kirigami.Units.gridUnit * 2
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
onClicked: root.requestClose()
|
||||
}
|
||||
|
||||
Behavior on zoomScale { NumberAnimation { duration: 80 } }
|
||||
|
||||
readonly property string pluginName: model.pluginName
|
||||
|
||||
onPressAndHold: {
|
||||
if (!model.isSupported) {
|
||||
return
|
||||
}
|
||||
|
||||
root.requestClose();
|
||||
folio.HomeScreenState.closeSettingsView();
|
||||
haptics.buttonVibrate();
|
||||
|
||||
let mappedCoords = root.homeScreen.prepareStartDelegateDrag(null, delegate, true);
|
||||
const widthOffset = folio.HomeScreenState.pageCellWidth / 2;
|
||||
const heightOffset = folio.HomeScreenState.pageCellHeight / 2;
|
||||
|
||||
folio.HomeScreenState.startDelegateWidgetListDrag(
|
||||
mappedCoords.x + mouseX - widthOffset,
|
||||
mappedCoords.y + mouseY - heightOffset,
|
||||
widthOffset,
|
||||
heightOffset,
|
||||
pluginName
|
||||
);
|
||||
PC3.Label {
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
text: i18n("Widgets")
|
||||
wrapMode: Text.Wrap
|
||||
font.weight: Font.Bold
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.5
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
color: Qt.rgba(255, 255, 255, 0.3)
|
||||
visible: delegate.containsMouse
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
anchors.fill: parent
|
||||
}
|
||||
PC3.ToolButton {
|
||||
id: getWidgetsButton
|
||||
icon.name: "get-hot-new-stuff"
|
||||
text: i18ndc("plasma_shell_org.kde.plasma.mobile", "@action:button The word 'new' refers to widgets", "Get New Widgets…")
|
||||
Accessible.name: i18ndc("plasma_shell_org.kde.plasma.mobile", "@action:button", "Get New Widgets…")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Kirigami.Units.largeSpacing
|
||||
|
||||
Item {
|
||||
id: iconWidget
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: delegate.width
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.large
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.large
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.centerIn: parent
|
||||
source: model.decoration
|
||||
visible: model.screenshot == ""
|
||||
implicitWidth: Kirigami.Units.iconSizes.large
|
||||
implicitHeight: Kirigami.Units.iconSizes.large
|
||||
}
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.screenshot
|
||||
width: Kirigami.Units.iconSizes.large
|
||||
height: Kirigami.Units.iconSizes.large
|
||||
}
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
id: heading
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: delegate.width
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: model.name
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: delegate.width
|
||||
Layout.alignment: Qt.AlignTop
|
||||
// otherwise causes binding loop due to the way the Plasma sets the height
|
||||
height: implicitHeight
|
||||
text: model.isSupported ? model.description : model.unsupportedMessage
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: heading.lineCount === 1 ? 3 : 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
onClicked: {
|
||||
getWidgetsDialog.model = widgetExplorer.widgetsMenuActions
|
||||
getWidgetsDialog.openRelative()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// opacity gradient at grid edges
|
||||
MobileShell.FlickableOpacityGradient {
|
||||
anchors.fill: gridView
|
||||
flickable: gridView
|
||||
}
|
||||
GridView {
|
||||
id: gridView
|
||||
clip: true
|
||||
reuseItems: true
|
||||
|
||||
WidgetExplorer {
|
||||
id: widgetExplorer
|
||||
containment: Plasmoid
|
||||
opacity: 0 // we display with the opacity gradient below
|
||||
|
||||
onShouldClose: root.requestClose()
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: Kirigami.Units.gridUnit
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.homeScreen.leftMargin
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.homeScreen.rightMargin
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.homeScreen.bottomMargin
|
||||
|
||||
model: widgetExplorer.widgetsModel
|
||||
|
||||
readonly property real intendedCellWidth: Kirigami.Units.gridUnit * 8
|
||||
readonly property int columns: Math.min(5, (width - leftMargin - rightMargin) / intendedCellWidth)
|
||||
|
||||
cellWidth: (width - leftMargin - rightMargin) / columns
|
||||
cellHeight: cellWidth + Kirigami.Units.gridUnit * 3
|
||||
|
||||
readonly property real horizontalMargin: Math.round(width * 0.05)
|
||||
leftMargin: horizontalMargin
|
||||
rightMargin: horizontalMargin
|
||||
|
||||
delegate: AppletListDelegate {
|
||||
folio: root.folio
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
}
|
||||
}
|
||||
|
||||
// opacity gradient at grid edges
|
||||
MobileShell.FlickableOpacityGradient {
|
||||
anchors.fill: gridView
|
||||
flickable: gridView
|
||||
}
|
||||
|
||||
WidgetExplorer {
|
||||
id: widgetExplorer
|
||||
containment: Plasmoid
|
||||
|
||||
onShouldClose: root.requestClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,23 +21,12 @@ Item {
|
|||
property var homeScreen
|
||||
property real settingsModeHomeScreenScale
|
||||
|
||||
readonly property bool homeScreenInteractive: !appletListViewerLoader.active
|
||||
readonly property bool homeScreenInteractive: !appletListViewer.active
|
||||
|
||||
property real bottomMargin: 0
|
||||
property real leftMargin: 0
|
||||
property real rightMargin: 0
|
||||
|
||||
Connections {
|
||||
target: folio.HomeScreenState
|
||||
|
||||
// Close applet viewer when settings view closes
|
||||
function onViewStateChanged() {
|
||||
if (folio.HomeScreenState.viewState !== Folio.HomeScreenState.SettingsView) {
|
||||
appletListViewerLoader.requestClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeSettings
|
||||
|
||||
|
|
@ -84,7 +73,7 @@ Item {
|
|||
iconName: 'widget-alternatives'
|
||||
textLabel: i18n("Widgets")
|
||||
onClicked: {
|
||||
appletListViewerLoader.active = true;
|
||||
appletListViewer.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,49 +146,13 @@ Item {
|
|||
}
|
||||
]
|
||||
|
||||
Loader {
|
||||
id: appletListViewerLoader
|
||||
asynchronous: true
|
||||
active: false
|
||||
|
||||
signal requestClose()
|
||||
onRequestClose: item?.requestClose()
|
||||
AppletListViewer {
|
||||
id: appletListViewer
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
opacity: status == Loader.Ready ? 1 : 0
|
||||
// move the settings out of the way if it is not visible
|
||||
// NOTE: we do this instead of setting visible to false, because
|
||||
// it doesn't mess with widget drag and drop
|
||||
y: (opacity > 0) ? 0 : parent.height
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Kirigami.Units.shortDuration }
|
||||
}
|
||||
|
||||
sourceComponent: AppletListViewer {
|
||||
id: appletListViewer
|
||||
folio: root.folio
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
onRequestClose: parent.active = false
|
||||
|
||||
homeScreen: root.homeScreen
|
||||
}
|
||||
}
|
||||
|
||||
PC3.BusyIndicator {
|
||||
id: appletListLoadingIndicator
|
||||
anchors.centerIn: parent
|
||||
visible: appletListViewerLoader.status === Loader.Loading
|
||||
|
||||
implicitHeight: Kirigami.Units.iconSizes.huge
|
||||
implicitWidth: Kirigami.Units.iconSizes.huge
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
folio: root.folio
|
||||
homeScreen: root.homeScreen
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue