homescreens/folio: Animate settings open, and add close button to widget selector

This commit is contained in:
Devin Lin 2023-11-05 09:46:17 -08:00
parent a049f07095
commit 34271281ec
3 changed files with 320 additions and 264 deletions

View file

@ -14,20 +14,15 @@ Most of the homescreen is in C++ in order to keep logic together, with QML only
As such, all of the positioning and placement of delegates on the screen are top down from the model, as well as drag and drop behaviour. As such, all of the positioning and placement of delegates on the screen are top down from the model, as well as drag and drop behaviour.
#### TODO #### TODO
- Add folio/halcyon switcher in initial-start - BUG: If an app gets uninstalled, the homescreen UI needs to ensure that delegates are updated
- If an app gets uninstalled, the homescreen UI needs to ensure that delegates are updated
- BUG: landscape favourites bar duplication when dragging icon from it sometimes - BUG: landscape favourites bar duplication when dragging icon from it sometimes
- BUG: can't insert delegates in-between very well in landscape favourites bar - BUG: can't insert delegates in-between very well in landscape favourites bar
- BUG: drag and drop animation when rejected on a different page - BUG: drag and drop animation when rejected on a different page
- IMPROVEMENT: can make the touch area only the icon? - IMPROVEMENT: can make the touch area only the icon?
- FEATURE: Add folio/halcyon switcher in initial-start
- FEATURE: add widget import/export - FEATURE: add widget import/export
- FEATURE: keyboard navigation - FEATURE: keyboard navigation
- FEATURE: touchpad navigation - FEATURE: touchpad navigation
- FEATURE: option to darken wallpaper - FEATURE: option to darken wallpaper
- FEATURE: option to turn off row/column swap - FEATURE: option to turn off row/column swap
- FEATURE: animate homescreen config opening
- RESTORE: app drawer overshoot
- PERFORMANCE: ensure that the widget config overlays are in loaders - PERFORMANCE: ensure that the widget config overlays are in loaders

View file

@ -33,15 +33,30 @@ MouseArea {
color: Qt.rgba(0, 0, 0, 0.7) color: Qt.rgba(0, 0, 0, 0.7)
} }
PC3.Label { RowLayout {
id: heading id: header
color: 'white' spacing: Kirigami.Units.largeSpacing
text: i18n("Widgets") anchors.left: parent.left
font.weight: Font.Bold anchors.leftMargin: Kirigami.Units.gridUnit
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.5
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: Kirigami.Units.gridUnit * 3 + root.homeScreen.topMargin anchors.topMargin: Kirigami.Units.gridUnit * 3 + root.homeScreen.topMargin
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 {
id: heading
color: 'white'
text: i18n("Widgets")
font.weight: Font.Bold
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.5
}
} }
GridView { GridView {
@ -51,7 +66,7 @@ MouseArea {
opacity: 0 // we display with the opacity gradient below opacity: 0 // we display with the opacity gradient below
anchors.top: heading.bottom anchors.top: header.bottom
anchors.topMargin: Kirigami.Units.gridUnit anchors.topMargin: Kirigami.Units.gridUnit
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: root.homeScreen.leftMargin anchors.leftMargin: root.homeScreen.leftMargin
@ -87,6 +102,16 @@ MouseArea {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
hoverEnabled: true hoverEnabled: true
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
}
Behavior on zoomScale { NumberAnimation { duration: 80 } }
readonly property string pluginName: model.pluginName readonly property string pluginName: model.pluginName
onPressAndHold: { onPressAndHold: {

View file

@ -14,275 +14,311 @@ import org.kde.kirigamiaddons.formcard 1.0 as FormCard
import '../delegate' import '../delegate'
Kirigami.ApplicationWindow { Window {
id: root id: root
flags: Qt.FramelessWindowHint flags: Qt.FramelessWindowHint
color: 'transparent'
pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar onVisibleChanged: {
pageStack.globalToolBar.showNavigationButtons: Kirigami.ApplicationHeaderStyle.NoNavigationButtons; if (visible) {
opacityAnim.to = 1;
opacityAnim.restart();
}
}
onClosing: (close) => {
if (applicationItem.opacity !== 0) {
close.accepted = false;
opacityAnim.to = 0;
opacityAnim.restart();
}
}
signal requestConfigureMenu() signal requestConfigureMenu()
pageStack.initialPage: Kirigami.ScrollablePage { Kirigami.ApplicationItem {
id: page id: applicationItem
opacity: root.opacity anchors.fill: parent
titleDelegate: RowLayout { opacity: 0
QQC2.ToolButton {
Layout.leftMargin: -Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
icon.name: "arrow-left"
onClicked: root.close()
}
Kirigami.Heading { NumberAnimation on opacity {
level: 1 id: opacityAnim
text: page.title duration: 200
} easing.type: Easing.OutCubic
} onFinished: {
if (applicationItem.opacity === 0) {
title: i18n("Homescreen Settings") root.close();
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
ColumnLayout {
FormCard.FormHeader {
title: i18n("Icons")
}
FormCard.FormCard {
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Item {
Layout.preferredHeight: Folio.HomeScreenState.pageCellHeight
Layout.fillWidth: true
AbstractDelegate {
anchors.centerIn: parent
implicitHeight: Folio.HomeScreenState.pageCellHeight
implicitWidth: Folio.HomeScreenState.pageCellWidth
name: i18n('Application')
contentItem: DelegateAppIcon {
height: Folio.FolioSettings.delegateIconSize
width: Folio.FolioSettings.delegateIconSize
source: 'applications-system'
}
}
}
}
FormCard.FormCard {
id: iconsCard
readonly property bool isVerticalOrientation: Folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RegularPosition ||
Folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RotateUpsideDown
readonly property string numOfRowsText: i18n("Number of rows")
readonly property string numOfColumnsText: i18n("Number of columns")
FormCard.FormSpinBoxDelegate {
id: iconSizeSpinBox
label: i18n("Size of icons on homescreen")
from: 16
to: 128
value: Folio.FolioSettings.delegateIconSize
onValueChanged: {
if (value !== Folio.FolioSettings.delegateIconSize) {
Folio.FolioSettings.delegateIconSize = value;
}
}
}
FormCard.FormSpinBoxDelegate {
id: rowsSpinBox
label: iconsCard.isVerticalOrientation ? iconsCard.numOfRowsText : iconsCard.numOfColumnsText
from: 3
to: 10
value: Folio.FolioSettings.homeScreenRows
onValueChanged: {
if (value !== Folio.FolioSettings.homeScreenRows) {
Folio.FolioSettings.homeScreenRows = value;
}
}
}
FormCard.FormSpinBoxDelegate {
id: columnsSpinBox
label: iconsCard.isVerticalOrientation ? iconsCard.numOfColumnsText : iconsCard.numOfRowsText
from: 3
to: 10
value: Folio.FolioSettings.homeScreenColumns
onValueChanged: {
if (value !== Folio.FolioSettings.homeScreenColumns) {
Folio.FolioSettings.homeScreenColumns = value;
}
}
}
}
FormCard.FormSectionText {
text: i18n("The rows and columns will swap depending on the screen rotation.")
}
FormCard.FormHeader {
title: i18n("Homescreen")
}
FormCard.FormCard {
FormCard.FormSwitchDelegate {
id: showLabelsOnHomeScreen
text: i18n("Show labels on homescreen")
checked: Folio.FolioSettings.showPagesAppLabels
onCheckedChanged: {
if (checked != Folio.FolioSettings.showPagesAppLabels) {
Folio.FolioSettings.showPagesAppLabels = checked;
}
}
}
FormCard.FormDelegateSeparator { above: showLabelsOnHomeScreen; below: showLabelsInFavourites }
FormCard.FormSwitchDelegate {
id: showLabelsInFavourites
text: i18n("Show labels in favorites bar")
checked: Folio.FolioSettings.showFavouritesAppLabels
onCheckedChanged: {
if (checked != Folio.FolioSettings.showFavouritesAppLabels) {
Folio.FolioSettings.showFavouritesAppLabels = checked;
}
}
}
FormCard.FormDelegateSeparator { above: showLabelsInFavourites; below: pageTransitionCombobox }
FormCard.FormComboBoxDelegate {
id: pageTransitionCombobox
text: i18n("Page transition effect")
currentIndex: indexOfValue(Folio.FolioSettings.pageTransitionEffect)
model: ListModel {
// we can't use i18n with ListElement
Component.onCompleted: {
append({"name": i18n("Slide"), "value": Folio.FolioSettings.SlideTransition});
append({"name": i18n("Cube"), "value": Folio.FolioSettings.CubeTransition});
append({"name": i18n("Fade"), "value": Folio.FolioSettings.FadeTransition});
append({"name": i18n("Stack"), "value": Folio.FolioSettings.StackTransition});
append({"name": i18n("Rotation"), "value": Folio.FolioSettings.RotationTransition});
// indexOfValue doesn't bind to model changes unfortunately, set currentIndex manually here
pageTransitionCombobox.currentIndex = pageTransitionCombobox.indexOfValue(Folio.FolioSettings.pageTransitionEffect)
}
}
textRole: "name"
valueRole: "value"
onCurrentValueChanged: Folio.FolioSettings.pageTransitionEffect = currentValue
}
}
FormCard.FormHeader {
title: i18n("Favorites Bar")
}
FormCard.FormCard {
FormCard.FormSwitchDelegate {
text: i18n('Show background')
icon.name: 'draw-rectangle'
checked: Folio.FolioSettings.showFavouritesBarBackground
onCheckedChanged: {
if (checked !== Folio.FolioSettings.showFavouritesBarBackground) {
Folio.FolioSettings.showFavouritesBarBackground = checked;
}
}
}
}
FormCard.FormHeader {
title: i18n("General")
}
FormCard.FormCard {
Layout.bottomMargin: Kirigami.Units.gridUnit
FormCard.FormButtonDelegate {
id: containmentSettings
text: i18n('Switch Homescreen')
icon.name: 'settings-configure'
onClicked: root.requestConfigureMenu()
}
FormCard.FormDelegateSeparator { above: containmentSettings; below: exportSettings }
FormCard.FormButtonDelegate {
id: exportSettings
text: i18n('Export layout')
icon.name: 'document-export'
onClicked: exportFileDialog.open()
}
FormCard.FormDelegateSeparator { above: exportSettings; below: importSettings }
FormCard.FormButtonDelegate {
id: importSettings
text: i18n('Import layout')
icon.name: 'document-import'
onClicked: importFileDialog.open()
} }
} }
} }
FileDialog { scale: 0.7 + 0.3 * applicationItem.opacity
id: exportFileDialog
title: i18n("Export layout to") pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar
fileMode: FileDialog.SaveFile pageStack.globalToolBar.showNavigationButtons: Kirigami.ApplicationHeaderStyle.NoNavigationButtons;
defaultSuffix: 'json'
nameFilters: ["JSON files (*.json)"] pageStack.initialPage: Kirigami.ScrollablePage {
onAccepted: { id: page
console.log('saving layout to ' + selectedFile); opacity: applicationItem.opacity
if (selectedFile) {
let status = Folio.FolioSettings.saveLayoutToFile(selectedFile); titleDelegate: RowLayout {
if (status) { QQC2.ToolButton {
exportedSuccessfullyPrompt.open(); Layout.leftMargin: -Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
} else { icon.name: "arrow-left"
exportFailedPrompt.open(); onClicked: root.close()
}
Kirigami.Heading {
level: 1
text: page.title
}
}
title: i18n("Homescreen Settings")
topPadding: 0
bottomPadding: 0
leftPadding: 0
rightPadding: 0
ColumnLayout {
FormCard.FormHeader {
title: i18n("Icons")
}
FormCard.FormCard {
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Item {
Layout.preferredHeight: Folio.HomeScreenState.pageCellHeight
Layout.fillWidth: true
AbstractDelegate {
anchors.centerIn: parent
implicitHeight: Folio.HomeScreenState.pageCellHeight
implicitWidth: Folio.HomeScreenState.pageCellWidth
name: i18n('Application')
contentItem: DelegateAppIcon {
height: Folio.FolioSettings.delegateIconSize
width: Folio.FolioSettings.delegateIconSize
source: 'applications-system'
}
}
}
}
FormCard.FormCard {
id: iconsCard
readonly property bool isVerticalOrientation: Folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RegularPosition ||
Folio.HomeScreenState.pageOrientation === Folio.HomeScreenState.RotateUpsideDown
readonly property string numOfRowsText: i18n("Number of rows")
readonly property string numOfColumnsText: i18n("Number of columns")
FormCard.FormSpinBoxDelegate {
id: iconSizeSpinBox
label: i18n("Size of icons on homescreen")
from: 16
to: 128
value: Folio.FolioSettings.delegateIconSize
onValueChanged: {
if (value !== Folio.FolioSettings.delegateIconSize) {
Folio.FolioSettings.delegateIconSize = value;
}
}
}
FormCard.FormSpinBoxDelegate {
id: rowsSpinBox
label: iconsCard.isVerticalOrientation ? iconsCard.numOfRowsText : iconsCard.numOfColumnsText
from: 3
to: 10
value: Folio.FolioSettings.homeScreenRows
onValueChanged: {
if (value !== Folio.FolioSettings.homeScreenRows) {
Folio.FolioSettings.homeScreenRows = value;
}
}
}
FormCard.FormSpinBoxDelegate {
id: columnsSpinBox
label: iconsCard.isVerticalOrientation ? iconsCard.numOfColumnsText : iconsCard.numOfRowsText
from: 3
to: 10
value: Folio.FolioSettings.homeScreenColumns
onValueChanged: {
if (value !== Folio.FolioSettings.homeScreenColumns) {
Folio.FolioSettings.homeScreenColumns = value;
}
}
}
}
FormCard.FormSectionText {
text: i18n("The rows and columns will swap depending on the screen rotation.")
}
FormCard.FormHeader {
title: i18n("Homescreen")
}
FormCard.FormCard {
FormCard.FormSwitchDelegate {
id: showLabelsOnHomeScreen
text: i18n("Show labels on homescreen")
checked: Folio.FolioSettings.showPagesAppLabels
onCheckedChanged: {
if (checked != Folio.FolioSettings.showPagesAppLabels) {
Folio.FolioSettings.showPagesAppLabels = checked;
}
}
}
FormCard.FormDelegateSeparator { above: showLabelsOnHomeScreen; below: showLabelsInFavourites }
FormCard.FormSwitchDelegate {
id: showLabelsInFavourites
text: i18n("Show labels in favorites bar")
checked: Folio.FolioSettings.showFavouritesAppLabels
onCheckedChanged: {
if (checked != Folio.FolioSettings.showFavouritesAppLabels) {
Folio.FolioSettings.showFavouritesAppLabels = checked;
}
}
}
FormCard.FormDelegateSeparator { above: showLabelsInFavourites; below: pageTransitionCombobox }
FormCard.FormComboBoxDelegate {
id: pageTransitionCombobox
text: i18n("Page transition effect")
currentIndex: indexOfValue(Folio.FolioSettings.pageTransitionEffect)
model: ListModel {
// we can't use i18n with ListElement
Component.onCompleted: {
append({"name": i18n("Slide"), "value": Folio.FolioSettings.SlideTransition});
append({"name": i18n("Cube"), "value": Folio.FolioSettings.CubeTransition});
append({"name": i18n("Fade"), "value": Folio.FolioSettings.FadeTransition});
append({"name": i18n("Stack"), "value": Folio.FolioSettings.StackTransition});
append({"name": i18n("Rotation"), "value": Folio.FolioSettings.RotationTransition});
// indexOfValue doesn't bind to model changes unfortunately, set currentIndex manually here
pageTransitionCombobox.currentIndex = pageTransitionCombobox.indexOfValue(Folio.FolioSettings.pageTransitionEffect)
}
}
textRole: "name"
valueRole: "value"
onCurrentValueChanged: Folio.FolioSettings.pageTransitionEffect = currentValue
}
}
FormCard.FormHeader {
title: i18n("Favorites Bar")
}
FormCard.FormCard {
FormCard.FormSwitchDelegate {
text: i18n('Show background')
icon.name: 'draw-rectangle'
checked: Folio.FolioSettings.showFavouritesBarBackground
onCheckedChanged: {
if (checked !== Folio.FolioSettings.showFavouritesBarBackground) {
Folio.FolioSettings.showFavouritesBarBackground = checked;
}
}
}
}
FormCard.FormHeader {
title: i18n("General")
}
FormCard.FormCard {
Layout.bottomMargin: Kirigami.Units.gridUnit
FormCard.FormButtonDelegate {
id: containmentSettings
text: i18n('Switch Homescreen')
icon.name: 'settings-configure'
onClicked: root.requestConfigureMenu()
}
FormCard.FormDelegateSeparator { above: containmentSettings; below: exportSettings }
FormCard.FormButtonDelegate {
id: exportSettings
text: i18n('Export layout')
icon.name: 'document-export'
onClicked: exportFileDialog.open()
}
FormCard.FormDelegateSeparator { above: exportSettings; below: importSettings }
FormCard.FormButtonDelegate {
id: importSettings
text: i18n('Import layout')
icon.name: 'document-import'
onClicked: importFileDialog.open()
} }
} }
} }
}
FileDialog { FileDialog {
id: importFileDialog id: exportFileDialog
fileMode: FileDialog.OpenFile title: i18n("Export layout to")
nameFilters: ["JSON files (*.json)"] fileMode: FileDialog.SaveFile
onAccepted: { defaultSuffix: 'json'
console.log('about to load layout from ' + selectedFile); nameFilters: ["JSON files (*.json)"]
confirmImportPrompt.open(); onAccepted: {
console.log('saving layout to ' + selectedFile);
if (selectedFile) {
let status = Folio.FolioSettings.saveLayoutToFile(selectedFile);
if (status) {
exportedSuccessfullyPrompt.open();
} else {
exportFailedPrompt.open();
}
}
}
} }
}
Kirigami.PromptDialog { FileDialog {
id: exportFailedPrompt id: importFileDialog
title: i18n("Export Status") fileMode: FileDialog.OpenFile
subtitle: i18n("Failed to export to %1", String(exportFileDialog.selectedFile).substring('file://'.length)) nameFilters: ["JSON files (*.json)"]
standardButtons: Kirigami.Dialog.Close onAccepted: {
} console.log('about to load layout from ' + selectedFile);
confirmImportPrompt.open();
}
}
Kirigami.PromptDialog { Kirigami.PromptDialog {
id: exportedSuccessfullyPrompt id: exportFailedPrompt
title: i18n("Export Status") title: i18n("Export Status")
subtitle: i18n("Homescreen layout exported successfully to %1", String(exportFileDialog.selectedFile).substring('file://'.length)) subtitle: i18n("Failed to export to %1", String(exportFileDialog.selectedFile).substring('file://'.length))
standardButtons: Kirigami.Dialog.Close standardButtons: Kirigami.Dialog.Close
} }
Kirigami.PromptDialog { Kirigami.PromptDialog {
id: confirmImportPrompt id: exportedSuccessfullyPrompt
title: i18n("Confirm Import") title: i18n("Export Status")
subtitle: i18n("This will overwrite your existing homescreen layout!") subtitle: i18n("Homescreen layout exported successfully to %1", String(exportFileDialog.selectedFile).substring('file://'.length))
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel standardButtons: Kirigami.Dialog.Close
onAccepted: Folio.FolioSettings.loadLayoutFromFile(importFileDialog.selectedFile); }
Kirigami.PromptDialog {
id: confirmImportPrompt
title: i18n("Confirm Import")
subtitle: i18n("This will overwrite your existing homescreen layout!")
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
onAccepted: Folio.FolioSettings.loadLayoutFromFile(importFileDialog.selectedFile);
}
} }
} }
} }